/*************************************************
 *  dibpal.cpp                                   *
 *                                               *
 *  Copyright (C) 1995-1999 Microsoft Inc.       *
 *                                               *
 *************************************************/

// dibpal.cpp : implementation file
//

#include "stdafx.h"
#include "dibpal.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDIBPal

CDIBPal::CDIBPal()
{
}

CDIBPal::~CDIBPal()
{
}

// Create a palette from the color table in a DIB
BOOL CDIBPal::Create(CDIB *pDIB)
{
    DWORD dwColors = pDIB->GetNumClrEntries();
    // Check the DIB has a color table
    if (! dwColors) {
        TRACE("No color table");   
        return FALSE;
    }

    // get a pointer to the RGB quads in the color table
    RGBQUAD * pRGB = pDIB->GetClrTabAddress();

    // allocate a log pal and fill it with the color table info
    LOGPALETTE *pPal = (LOGPALETTE *) malloc(sizeof(LOGPALETTE) 
                     + dwColors * sizeof(PALETTEENTRY));
    if (!pPal) {
        TRACE("Out of memory for logpal");
        return FALSE;
    }
    pPal->palVersion = 0x300; // Windows 3.0
    pPal->palNumEntries = (WORD) dwColors; // table size
    for (DWORD dw=0; dw<dwColors; dw++) {
        pPal->palPalEntry[dw].peRed = pRGB[dw].rgbRed;
        pPal->palPalEntry[dw].peGreen = pRGB[dw].rgbGreen;
        pPal->palPalEntry[dw].peBlue = pRGB[dw].rgbBlue;
        pPal->palPalEntry[dw].peFlags = 0;
    }
    BOOL bResult = CreatePalette(pPal);
    free (pPal);
    return bResult;
}

/////////////////////////////////////////////////////////////////////////////
// CDIBPal commands

int CDIBPal::GetNumColors()
{
    int iColors = 0;
    if (!GetObject(sizeof(iColors), &iColors)) {
        TRACE("Failed to get num pal colors");
        return 0;
    }
    return iColors;
}

void CDIBPal::Draw(CDC *pDC, CRect *pRect, BOOL bBkgnd)
{
    int iColors = GetNumColors();
    CPalette *pOldPal = pDC->SelectPalette(this, bBkgnd);
    pDC->RealizePalette();
    int i, j, top, left, bottom, right;
    for (j=0, top=0; j<16 && iColors; j++, top=bottom) {
        bottom = (j+1) * pRect->bottom / 16 + 1;
        for(i=0, left=0; i<16 && iColors; i++, left=right) {
            right = (i+1) * pRect->right / 16 + 1;
            CBrush br (PALETTEINDEX(j * 16 + i));
            CBrush *brold = pDC->SelectObject(&br);
            pDC->Rectangle(left-1, top-1, right, bottom);
            pDC->SelectObject(brold);
            iColors--;
        }
    }
    pDC->SelectPalette(pOldPal, FALSE);
}

BYTE Color16[16][3] = {{0,0,0},{128,0,0},{0,128,0},{128,128,0},
                       {0,0,128},{128,0,128},{0,128,128},{192,192,192},
                                           {128,128,128},{255,0,0},{0,255,0},{255,255,0},
                                           {0,0,255},{255,0,255},{0,255,255},{255,255,255}};


BOOL CDIBPal::SetSysPalColors()
{
    BOOL bResult = FALSE;
    int i, iSysColors, iPalEntries;
    HPALETTE hpalOld;
        int nColorMode;
    // Get a screen DC to work with
    HWND hwndActive = ::GetActiveWindow();
    HDC hdcScreen = ::GetDC(hwndActive);
    ASSERT(hdcScreen);

    // Make sure we are on a palettized device
    if (!(GetDeviceCaps(hdcScreen, RASTERCAPS) & RC_PALETTE)) {
        TRACE("Not a palettized device");
        goto abort;
    }

    // Get the number of system colors and the number of palette entries
    // Note that on a palletized device the number of colors is the
    // number of guaranteed colors.  I.e. the number of reserved system colors
    iSysColors = GetDeviceCaps(hdcScreen, NUMCOLORS);
    iPalEntries = GetDeviceCaps(hdcScreen, SIZEPALETTE);
        if (iSysColors > 256) goto abort;

        if (iSysColors == -1)
                nColorMode = 16;
        else if (iPalEntries == 0)
                nColorMode = 4;
        else
                nColorMode = 8;
    
    SetSystemPaletteUse(hdcScreen, SYSPAL_NOSTATIC);
    SetSystemPaletteUse(hdcScreen, SYSPAL_STATIC);

    hpalOld = ::SelectPalette(hdcScreen,
                              (HPALETTE)m_hObject, // our hpal
                              FALSE);
    ::RealizePalette(hdcScreen);
    ::SelectPalette(hdcScreen, hpalOld, FALSE);

    PALETTEENTRY pe[256];
        switch (nColorMode)
        {
                case 4:
                {
                        iPalEntries = 16;                       
                        for (i = 0; i <iPalEntries ; i++) 
                        {
                        pe[i].peFlags = PC_NOCOLLAPSE;
                                pe[i].peRed = Color16[i][0];
                                pe[i].peGreen = Color16[i][1];
                                pe[i].peBlue = Color16[i][2];
                        }
                }
                break;
                case 16:
                    iPalEntries = 256;                      
                    iSysColors  = 20;
                    break;
                case 8:
                {
                  int nCount =  GetSystemPaletteEntries(hdcScreen, 
                            0,
                            iPalEntries,
                            pe);

                for (i = 0; i < iSysColors/2; i++) 
                        pe[i].peFlags = 0;
                for (; i < iPalEntries-iSysColors/2; i++) 
                        pe[i].peFlags = PC_NOCOLLAPSE;
                for (; i < iPalEntries; i++) 
                        pe[i].peFlags = 0;
                }                                                                   
                break;
                
        }
    ResizePalette(iPalEntries);

    SetPaletteEntries(0, iPalEntries, pe);
//      for (i=0; i<iPalEntries; i++)
//      {
//              TRACE("%d>>%d:%d:%d\n",i,pe[i].peRed,pe[i].peGreen,pe[i].peBlue);
//      }
    bResult = TRUE;

abort:
    ::ReleaseDC(hwndActive, hdcScreen);
    return bResult;
}

// Load a palette from a named file
BOOL CDIBPal::Load(char *pszFileName)
{
    CString strFile;    

    if ((pszFileName == NULL) 
    ||  (strlen(pszFileName) == 0)) {

        // Show an open file dialog to get the name
        CFileDialog dlg   (TRUE,    // open
                           NULL,    // no default extension
                           NULL,    // no initial file name
                           OFN_FILEMUSTEXIST
                             | OFN_HIDEREADONLY,
                           "Palette files (*.PAL)|*.PAL|All files (*.*)|*.*||");
        if (dlg.DoModal() == IDOK) {
            strFile = dlg.GetPathName();
        } else {
            return FALSE;
        }
    } else {    
        // copy the supplied file path
        strFile = pszFileName;                    
    }

    // Try to open the file for read access
    CFile file;
    if (! file.Open(strFile,
                    CFile::modeRead | CFile::shareDenyWrite)) {
        AfxMessageBox("Failed to open file");
        return FALSE;
    }

    BOOL bResult = Load(&file);
    file.Close();
    if (!bResult) AfxMessageBox("Failed to load file");
    return bResult;
}

// Load a palette from an open CFile object
BOOL CDIBPal::Load(CFile *fp)
{
    return Load(fp->m_hFile);
}

// Load a palette from an open file handle
BOOL CDIBPal::Load(UINT_PTR hFile)
{
    HMMIO hmmio;
    MMIOINFO info;
    memset(&info, 0, sizeof(info));
    info.adwInfo[0] = (DWORD)hFile;
    hmmio = mmioOpen(NULL,
                     &info,
                     MMIO_READ | MMIO_ALLOCBUF);
    if (!hmmio) {
        TRACE("mmioOpen failed");
        return FALSE;
    }
    BOOL bResult = Load(hmmio);
    mmioClose(hmmio, MMIO_FHOPEN);
    return bResult;
}

// Load a palette from an open MMIO handle
BOOL CDIBPal::Load(HMMIO hmmio)
{
    // Check it's a RIFF PAL file
    MMCKINFO ckFile;
    ckFile.fccType = mmioFOURCC('P','A','L',' ');
    if (mmioDescend(hmmio,
                    &ckFile,
                    NULL,
                    MMIO_FINDRIFF) != 0) {
        TRACE("Not a RIFF or PAL file");
        return FALSE;
    }
    // Find the 'data' chunk
    MMCKINFO ckChunk;
    ckChunk.ckid = mmioFOURCC('d','a','t','a');
    if (mmioDescend(hmmio,
                    &ckChunk,
                    &ckFile,
                    MMIO_FINDCHUNK) != 0) {
        TRACE("No data chunk in file");
        return FALSE;
    }
    // allocate some memory for the data chunk
    int iSize = ckChunk.cksize;
    void *pdata = malloc(iSize);
    if (!pdata) {
        TRACE("No mem for data");
        return FALSE;
    }
    // read the data chunk
    if (mmioRead(hmmio,
                 (char *)pdata,
                 iSize) != iSize) {
        TRACE("Failed to read data chunk");
        free(pdata);
        return FALSE;
    }
    // The data chunk should be a LOGPALETTE structure
        // which we can create a palette from
        LOGPALETTE* pLogPal = (LOGPALETTE*)pdata;
        if (pLogPal->palVersion != 0x300) {
                TRACE("Invalid version number");
        free(pdata);
        return FALSE;
        }
        // Get the number of entries
        int iColors = pLogPal->palNumEntries;
        if (iColors <= 0) {
                TRACE("No colors in palette");
        free(pdata);
        return FALSE;
        }
        return CreatePalette(pLogPal);
}

// Save a palette to an open CFile object
BOOL CDIBPal::Save(CFile *fp)
{
    return Save(fp->m_hFile);
}

// Save a palette to an open file handle
BOOL CDIBPal::Save(UINT_PTR hFile)
{
    HMMIO hmmio;
    MMIOINFO info;
    memset(&info, 0, sizeof(info));
    info.adwInfo[0] = (DWORD)hFile;
    hmmio = mmioOpen(NULL,
                     &info,
                     MMIO_WRITE | MMIO_CREATE | MMIO_ALLOCBUF);
    if (!hmmio) {
        TRACE("mmioOpen failed");
        return FALSE;
    }
    BOOL bResult = Save(hmmio);
    mmioClose(hmmio, MMIO_FHOPEN);
    return bResult;
}

// Save a palette to an open MMIO handle
BOOL CDIBPal::Save(HMMIO hmmio)
{
        // Create a RIFF chunk for a PAL file
    MMCKINFO ckFile;
        ckFile.cksize = 0; // corrected later
    ckFile.fccType = mmioFOURCC('P','A','L',' ');
    if (mmioCreateChunk(hmmio,
                        &ckFile,
                        MMIO_CREATERIFF) != 0) {
        TRACE("Failed to create RIFF-PAL chunk");
        return FALSE;
    }
        // create the LOGPALETTE data which will become
        // the data chunk
        int iColors = GetNumColors();
        ASSERT(iColors > 0);
        int iSize = sizeof(LOGPALETTE)
                                + (iColors-1) * sizeof(PALETTEENTRY);
        LOGPALETTE* plp = (LOGPALETTE*) malloc(iSize);
        ASSERT(plp);
        plp->palVersion = 0x300;
        plp->palNumEntries = (unsigned short) iColors;
        GetPaletteEntries(0, iColors, plp->palPalEntry);
        // create the data chunk
    MMCKINFO ckData;
        ckData.cksize = iSize; 
    ckData.ckid = mmioFOURCC('d','a','t','a');
    if (mmioCreateChunk(hmmio,
                        &ckData,
                        0) != 0) {
        TRACE("Failed to create data chunk");
        return FALSE;
    }
        // write the data chunk
    if (mmioWrite(hmmio,
                 (char*)plp,
                 iSize) != iSize) {
        TRACE("Failed to write data chunk");
        free(plp);
        return FALSE;
    }
        free(plp);
        // Ascend from the data chunk which will correct the length
        mmioAscend(hmmio, &ckData, 0);
        // Ascend from the RIFF/PAL chunk
        mmioAscend(hmmio, &ckFile, 0);

        return TRUE;
}