//
// This File contains the definition of the MGImage-class
//
//   Author: Thomas Bretz
//   Version: V1.0 (1-8-2000)
//
// x11/src/GX11Gui.cxx
//

//////////////////////////////////////////////////////////////////////////////
//
// MGImage
//
// If sync-mode is enabled the Redraw function is secured by a mutex (ignore
// error messages about it comming from root) This has the advantage that
// if you use a timer for screen update reading and writing the image is
// synchronized. In this way you don't get flickering half images.
//
//////////////////////////////////////////////////////////////////////////////
#include "MGImage.h"

#include <iostream>

#include <TGX11.h>
#include <TMutex.h>

ClassImp(MGImage);

using namespace std;

MGImage::MGImage(const TGWindow* p, UInt_t w, UInt_t h, UInt_t options, ULong_t back)
    : TGFrame(p, w, h, options, back), fWidth(w), fHeight(h)
{
    // p = pointer to MainFrame (not owner)
    // w = width of frame
    // h = width of frame

    //
    // Creat drawing semaphore
    //
    fMuxPixmap = new TMutex;

    Resize(GetWidth(), GetHeight());

    //
    // create empty pixmap
    //
    fDefGC  = gVirtualX->CreateGC(fId, 0);
    fImage  = (XImage*)gVirtualX->CreateImage(fWidth, fHeight);

    cout << "Detected Color Depth: " << gVirtualX->GetDepth() << endl;
}

MGImage::~MGImage()
{
    if (fMuxPixmap->Lock()==13)
        cout << "MGImage::~MGImage - mutex is already locked by this thread" << endl;

    cout << "Deleting MGImage..." << endl;

    gVirtualX->DeleteGC(fDefGC);
    gVirtualX->DeleteImage((Drawable_t)fImage);

    delete fMuxPixmap;

    cout << "MGImage destroyed." << endl;
}

void MGImage::DoRedraw()
{
    if (TestBit(kSyncMode))
        while (fMuxPixmap->Lock()==13)
            usleep(1);

    //    gVirtualX->DrawLine(fId, fDefGC, 0, 0, fWidth+2, 0);
    //    gVirtualX->DrawLine(fId, fDefGC, 0, 0, 0, fHeight+2);
    //    gVirtualX->DrawLine(fId, fDefGC, fWidth+2, 0,  fWidth+2, fHeight+2);
    //    gVirtualX->DrawLine(fId, fDefGC, 0, fHeight+2, fWidth+2, fHeight+2);

    //    if (TestBit(kNeedRedraw))
    {
        gVirtualX->PutImage(fId, fDefGC, (Drawable_t)fImage, 0, 0, 0, 0,
                            fWidth, fHeight);
        ResetBit(kNeedRedraw);
    }

    if (TestBit(kSyncMode))
        if (fMuxPixmap->UnLock()==13)
            cout << "MGImage::DoRedraw - tried to unlock mutex locked by other thread." << endl;
}

void MGImage::DrawImg16(unsigned short *d, char *s, char *e)
{
    // d=destination, s=source, e=end
    // rrrrrggg gggbbbbb
    //
    while (s<e)
    {
        //      11111100    11111000      11111000
        *d++ = (*s&0xfc) | (*s&0xf8)<<5 | (*s&0xf8)<<11;
        s++;
    }
}

void MGImage::DrawImg24(char *d, char *s, char *e)
{
    // d=destination, s=source, e=end
    // rrrrrrrr gggggggg bbbbbbbb aaaaaaaa
    //
    while (s<e)
    {
        *d++ = *s;
        *d++ = *s;
        *d++ = *s++;
        d++;
    }
}

void MGImage::DrawImg(const byte *buffer)
{
    if (TestBit(kSyncMode))
        while (fMuxPixmap->Lock()==13)
            usleep(1);
    else
    {
        const Int_t rc = fMuxPixmap->Lock();
        if (rc==13)
            cout << "MGImage::DrawImg - mutex is already locked by this thread" << endl;
        if (rc)
            return;
    }

    switch (gVirtualX->GetDepth())
    {
    case 8:
        memcpy(fImage->data, buffer, fWidth*fHeight);
        break;
    case 16:
        DrawImg16((unsigned short*)fImage->data, (char*)buffer, (char*)(buffer+fWidth*fHeight));
        break;
    case 24:
        DrawImg24(fImage->data, (char*)buffer, (char*)(buffer+fWidth*fHeight));
        break;
    default:
        cout << "Sorry, " << gVirtualX->GetDepth() << "bit color depth not yet implemented." << endl;
    }

    SetBit(kNeedRedraw);

    if (fMuxPixmap->UnLock()==13)
        cout << "MGImage::DrawImage - tried to unlock mutex locked by other thread." << endl;
}

void MGImage::DrawColImg16(unsigned short *d, char *s1, char *s2, char *e)
{
    // d=destination, s1=source1, s2=source2, e=end
    // d:  rrrrrggg gggbbbbb
    // s2:          00rrggbb
    //
    while (s1<e)
    {
        if (*s2)
        {    
            //      00000011   00001100        00110000
            //*d++ = (*s2&0x3) | (*s2&0xb)<<3 | (*s2&0x30)<<7;
            *d++ = (*s2&0x3)<<3 | (*s2&0xb)<<6 | (*s2&0x30)<<10;
        }
        else
        {
            //      11111100     11111000        11111100
            *d++ = (*s1&0xfc) | (*s1&0xf8)<<5 | (*s1&0xfc)<<11;
        }
        s1++;
        s2++;
    }
}

void MGImage::DrawColImg24(char *d, char *s1, char *s2, char *e)
{
    // d=destination, s1=source1, s2=source2, e=end
    while (s1<e)
    {
        if (*s2)
        {
            *d++ = ((*s2>>4)&0x3)*85;
            *d++ = ((*s2>>2)&0x3)*85;
            *d++ = ((*s2++ )&0x3)*85;
            d++;
            s1++;
        }
        else
        {
            *d++ = *s1;
            *d++ = *s1;
            *d++ = *s1++;
            d++;
            s2++;
        }
    }
}

void MGImage::DrawColImg(const byte *gbuf, const byte *cbuf)
{
    if (TestBit(kSyncMode))
        while (fMuxPixmap->Lock()==13)
            usleep(1);
    else
    {
        const Int_t rc = fMuxPixmap->Lock();
        if (rc==13)
            cout << "MGImage::DrawColImg - mutex is already locked by this thread" << endl;
        if (rc)
            return;
    }

    // FROM libAfterImage:
    // -------------------
    //#define ALPHA_TRANSPARENT      	0x00
    //#define ALPHA_SEMI_TRANSPARENT 	0x7F
    //#define ALPHA_SOLID            	0xFF
    // * Lowermost 8 bits - Blue channel
    // * bits  8 to 15    - Green channel
    // * bits 16 to 23    - Red channel
    // * bits 24 to 31    - Alpha channel
    //#define ARGB32_White    		0xFFFFFFFF
    //#define ARGB32_Black    		0xFF000000

    // FIXME: This loop depends on the screen color depth
    switch (gVirtualX->GetDepth())
    {
    case 16:
        DrawColImg16((unsigned short*)fImage->data, (char*)gbuf, (char*)cbuf, (char*)(gbuf+fWidth*fHeight));
        break;
    case 24:
        DrawColImg24(fImage->data, (char*)gbuf, (char*)cbuf, (char*)(gbuf+fWidth*fHeight));
        break;
    default:
        cout << "Sorry, " << gVirtualX->GetDepth() << "bit color depth not yet implemented." << endl;
    }

    SetBit(kNeedRedraw);

    if (fMuxPixmap->UnLock()==13)
        cout << "MGImage::DrawColImage - tried to unlock mutex locked by other thread." << endl;
}
