/*
 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
 *
 *Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 *"Software"), to deal in the Software without restriction, including
 *without limitation the rights to use, copy, modify, merge, publish,
 *distribute, sublicense, and/or sell copies of the Software, and to
 *permit persons to whom the Software is furnished to do so, subject to
 *the following conditions:
 *
 *The above copyright notice and this permission notice shall be
 *included in all copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *Except as contained in this notice, the name of the XFree86 Project
 *shall not be used in advertising or otherwise to promote the sale, use
 *or other dealings in this Software without prior written authorization
 *from the XFree86 Project.
 *
 * Authors:	Dakshinamurthy Karra
 *		Suhaib M Siddiqi
 *		Peter Busch
 *		Harold L Hunt II
 */

#ifdef HAVE_XWIN_CONFIG_H
#include <xwin-config.h>
#endif
#include "win.h"

/*
 * Local prototypes
 */

static int
 winListInstalledColormaps(ScreenPtr pScreen, Colormap * pmaps);

static void
 winStoreColors(ColormapPtr pmap, int ndef, xColorItem * pdefs);

static void
 winInstallColormap(ColormapPtr pmap);

static void
 winUninstallColormap(ColormapPtr pmap);

static void

winResolveColor(unsigned short *pred,
                unsigned short *pgreen,
                unsigned short *pblue, VisualPtr pVisual);

static Bool
 winCreateColormap(ColormapPtr pmap);

static void
 winDestroyColormap(ColormapPtr pmap);

static Bool
 winGetPaletteDIB(ScreenPtr pScreen, ColormapPtr pcmap);

static Bool
 winGetPaletteDD(ScreenPtr pScreen, ColormapPtr pcmap);

/*
 * Set screen functions for colormaps
 */

void
winSetColormapFunctions(ScreenPtr pScreen)
{
    pScreen->CreateColormap = winCreateColormap;
    pScreen->DestroyColormap = winDestroyColormap;
    pScreen->InstallColormap = winInstallColormap;
    pScreen->UninstallColormap = winUninstallColormap;
    pScreen->ListInstalledColormaps = winListInstalledColormaps;
    pScreen->StoreColors = winStoreColors;
    pScreen->ResolveColor = winResolveColor;
}

/* See Porting Layer Definition - p. 30 */
/*
 * Walk the list of installed colormaps, filling the pmaps list
 * with the resource ids of the installed maps, and return
 * a count of the total number of installed maps.
 */
static int
winListInstalledColormaps(ScreenPtr pScreen, Colormap * pmaps)
{
    winScreenPriv(pScreen);

    /*
     * There will only be one installed colormap, so we only need
     * to return one id, and the count of installed maps will always
     * be one.
     */
    *pmaps = pScreenPriv->pcmapInstalled->mid;
    return 1;
}

/* See Porting Layer Definition - p. 30 */
/* See Programming Windows - p. 663 */
static void
winInstallColormap(ColormapPtr pColormap)
{
    ScreenPtr pScreen = pColormap->pScreen;

    winScreenPriv(pScreen);
    ColormapPtr oldpmap = pScreenPriv->pcmapInstalled;

    winDebug("winInstallColormap\n");

    /* Did the colormap actually change? */
    if (pColormap != oldpmap) {
        winDebug("winInstallColormap - Colormap has changed, attempt "
                 "to install.\n");

        /* Was there a previous colormap? */
        if (oldpmap != (ColormapPtr) None) {
            /* There was a previous colormap; tell clients it is gone */
            WalkTree(pColormap->pScreen, TellLostMap, (char *) &oldpmap->mid);
        }

        /* Install new colormap */
        pScreenPriv->pcmapInstalled = pColormap;
        WalkTree(pColormap->pScreen, TellGainedMap, (char *) &pColormap->mid);

        /* Call the engine specific colormap install procedure */
        if (!((*pScreenPriv->pwinInstallColormap) (pColormap))) {
            ErrorF ("winInstallColormap - Screen specific colormap install "
                    "procedure failed.  Continuing, but colors may be "
                    "messed up from now on.\n");
        }
    }

    /* Save a pointer to the newly installed colormap */
    pScreenPriv->pcmapInstalled = pColormap;
}

/* See Porting Layer Definition - p. 30 */
static void
winUninstallColormap(ColormapPtr pmap)
{
    winScreenPriv(pmap->pScreen);
    ColormapPtr curpmap = pScreenPriv->pcmapInstalled;

    winDebug("winUninstallColormap\n");

    /* Is the colormap currently installed? */
    if (pmap != curpmap) {
        /* Colormap not installed, nothing to do */
        return;
    }

    /* Clear the installed colormap flag */
    pScreenPriv->pcmapInstalled = NULL;

    /*
     * NOTE: The default colormap does not get "uninstalled" before
     * it is destroyed.
     */

    /* Install the default cmap in place of the cmap to be uninstalled */
    if (pmap->mid != pmap->pScreen->defColormap) {
        dixLookupResourceByType((pointer) &curpmap, pmap->pScreen->defColormap,
                                RT_COLORMAP, NullClient, DixUnknownAccess);
        (*pmap->pScreen->InstallColormap) (curpmap);
    }
}

/* See Porting Layer Definition - p. 30 */
static void
winStoreColors(ColormapPtr pmap, int ndef, xColorItem * pdefs)
{
    ScreenPtr pScreen = pmap->pScreen;

    winScreenPriv(pScreen);
    winCmapPriv(pmap);
    int i;
    unsigned short nRed, nGreen, nBlue;

#ifdef WINDBG
    if (ndef != 1)
        winDebug("winStoreColors - ndef: %d\n", ndef);
#endif

    /* Save the new colors in the colormap privates */
    for (i = 0; i < ndef; ++i) {
        /* Adjust the colors from the X color spec to the Windows color spec */
        nRed = pdefs[i].red >> 8;
        nGreen = pdefs[i].green >> 8;
        nBlue = pdefs[i].blue >> 8;

        /* Copy the colors to a palette entry table */
        pCmapPriv->peColors[pdefs[0].pixel + i].peRed = nRed;
        pCmapPriv->peColors[pdefs[0].pixel + i].peGreen = nGreen;
        pCmapPriv->peColors[pdefs[0].pixel + i].peBlue = nBlue;

        /* Copy the colors to a RGBQUAD table */
        pCmapPriv->rgbColors[pdefs[0].pixel + i].rgbRed = nRed;
        pCmapPriv->rgbColors[pdefs[0].pixel + i].rgbGreen = nGreen;
        pCmapPriv->rgbColors[pdefs[0].pixel + i].rgbBlue = nBlue;

        winDebug("winStoreColors - nRed %d nGreen %d nBlue %d\n",
                 nRed, nGreen, nBlue);
    }

    /* Call the engine specific store colors procedure */
    if (!((pScreenPriv->pwinStoreColors) (pmap, ndef, pdefs))) {
        ErrorF ("winStoreColors - Engine cpecific color storage procedure "
                "failed.  Continuing, but colors may be messed up from now "
                "on.\n");
    }
}

/* See Porting Layer Definition - p. 30 */
static void
winResolveColor(unsigned short *pred,
                unsigned short *pgreen,
                unsigned short *pblue, VisualPtr pVisual)
{
    winDebug("winResolveColor ()\n");

    miResolveColor(pred, pgreen, pblue, pVisual);
}

/* See Porting Layer Definition - p. 29 */
static Bool
winCreateColormap(ColormapPtr pmap)
{
    winPrivCmapPtr pCmapPriv = NULL;
    ScreenPtr pScreen = pmap->pScreen;

    winScreenPriv(pScreen);

    winDebug("winCreateColormap\n");

    /* Allocate colormap privates */
    if (!winAllocateCmapPrivates(pmap)) {
        ErrorF("winCreateColorma - Couldn't allocate cmap privates\n");
        return FALSE;
    }

    /* Get a pointer to the newly allocated privates */
    pCmapPriv = winGetCmapPriv(pmap);

    /*
     * FIXME: This is some evil hackery to help in handling some X clients
     * that expect the top pixel to be white.  This "help" only lasts until
     * some client overwrites the top colormap entry.
     * 
     * We don't want to actually allocate the top entry, as that causes
     * problems with X clients that need 7 planes (128 colors) in the default
     * colormap, such as Magic 7.1.
     */
    pCmapPriv->rgbColors[WIN_NUM_PALETTE_ENTRIES - 1].rgbRed = 255;
    pCmapPriv->rgbColors[WIN_NUM_PALETTE_ENTRIES - 1].rgbGreen = 255;
    pCmapPriv->rgbColors[WIN_NUM_PALETTE_ENTRIES - 1].rgbBlue = 255;
    pCmapPriv->peColors[WIN_NUM_PALETTE_ENTRIES - 1].peRed = 255;
    pCmapPriv->peColors[WIN_NUM_PALETTE_ENTRIES - 1].peGreen = 255;
    pCmapPriv->peColors[WIN_NUM_PALETTE_ENTRIES - 1].peBlue = 255;

    /* Call the engine specific colormap initialization procedure */
    if (!((*pScreenPriv->pwinCreateColormap) (pmap))) {
        ErrorF("winCreateColormap - Engine specific colormap creation "
               "procedure failed.  Aborting.\n");
        return FALSE;
    }

    return TRUE;
}

/* See Porting Layer Definition - p. 29, 30 */
static void
winDestroyColormap(ColormapPtr pColormap)
{
    winScreenPriv(pColormap->pScreen);
    winCmapPriv(pColormap);

    /* Call the engine specific colormap destruction procedure */
    if (!((*pScreenPriv->pwinDestroyColormap) (pColormap))) {
        ErrorF ("winDestroyColormap - Engine specific colormap destruction "
                "procedure failed.  Continuing, but it is possible that memory "
                "was leaked, or that colors will be messed up from now on.\n");
    }

    /* Free the colormap privates */
    free(pCmapPriv);
    winSetCmapPriv(pColormap, NULL);

    winDebug("winDestroyColormap - Returning\n");
}

/*
 * Internal function to load the palette used by the Shadow DIB
 */

static Bool
winGetPaletteDIB(ScreenPtr pScreen, ColormapPtr pcmap)
{
    winScreenPriv(pScreen);
    int i;
    Pixel pixel;                /* Pixel == CARD32 */
    CARD16 nRed, nGreen, nBlue; /* CARD16 == unsigned short */
    UINT uiColorsRetrieved = 0;
    RGBQUAD rgbColors[WIN_NUM_PALETTE_ENTRIES];

    /* Get the color table for the screen */
    uiColorsRetrieved = GetDIBColorTable(pScreenPriv->hdcScreen,
                                         0, WIN_NUM_PALETTE_ENTRIES, rgbColors);
    if (uiColorsRetrieved == 0) {
        ErrorF("winGetPaletteDIB - Could not retrieve screen color table\n");
        return FALSE;
    }

    winDebug("winGetPaletteDIB - Retrieved %d colors from DIB\n",
             uiColorsRetrieved);

    /* Set the DIB color table to the default screen palette */
    if (SetDIBColorTable(pScreenPriv->hdcShadow,
                         0, uiColorsRetrieved, rgbColors) == 0) {
        ErrorF("winGetPaletteDIB - SetDIBColorTable () failed\n");
        return FALSE;
    }

    /* Alloc each color in the DIB color table */
    for (i = 0; i < uiColorsRetrieved; ++i) {
        pixel = i;

        /* Extract the color values for current palette entry */
        nRed = rgbColors[i].rgbRed << 8;
        nGreen = rgbColors[i].rgbGreen << 8;
        nBlue = rgbColors[i].rgbBlue << 8;

        winDebug("winGetPaletteDIB - Allocating a color: %d; "
                 "%d %d %d\n", pixel, nRed, nGreen, nBlue);

        /* Allocate a entry in the X colormap */
        if (AllocColor(pcmap, &nRed, &nGreen, &nBlue, &pixel, 0) != Success) {
            ErrorF("winGetPaletteDIB - AllocColor () failed, pixel %d\n", i);
            return FALSE;
        }

        if (i != pixel
            || nRed != rgbColors[i].rgbRed
            || nGreen != rgbColors[i].rgbGreen
            || nBlue != rgbColors[i].rgbBlue) {
            winDebug("winGetPaletteDIB - Got: %d; "
                     "%d %d %d\n", (int) pixel, nRed, nGreen, nBlue);
        }

        /* FIXME: Not sure that this bit is needed at all */
        pcmap->red[i].co.local.red = nRed;
        pcmap->red[i].co.local.green = nGreen;
        pcmap->red[i].co.local.blue = nBlue;
    }

    /* System is using a colormap */
    /* Set the black and white pixel indices */
    pScreen->whitePixel = uiColorsRetrieved - 1;
    pScreen->blackPixel = 0;

    return TRUE;
}

/*
 * Internal function to load the standard system palette being used by DD
 */

static Bool
winGetPaletteDD(ScreenPtr pScreen, ColormapPtr pcmap)
{
    int i;
    Pixel pixel;                /* Pixel == CARD32 */
    CARD16 nRed, nGreen, nBlue; /* CARD16 == unsigned short */
    UINT uiSystemPaletteEntries;
    LPPALETTEENTRY ppeColors = NULL;
    HDC hdc = NULL;

    /* Get a DC to obtain the default palette */
    hdc = GetDC(NULL);
    if (hdc == NULL) {
        ErrorF("winGetPaletteDD - Couldn't get a DC\n");
        return FALSE;
    }

    /* Get the number of entries in the system palette */
    uiSystemPaletteEntries = GetSystemPaletteEntries(hdc, 0, 0, NULL);
    if (uiSystemPaletteEntries == 0) {
        ErrorF("winGetPaletteDD - Unable to determine number of "
               "system palette entries\n");
        return FALSE;
    }

    winDebug("winGetPaletteDD - uiSystemPaletteEntries %d\n",
             uiSystemPaletteEntries);

    /* Allocate palette entries structure */
    ppeColors = malloc(uiSystemPaletteEntries * sizeof(PALETTEENTRY));
    if (ppeColors == NULL) {
        ErrorF("winGetPaletteDD - malloc () for colormap failed\n");
        return FALSE;
    }

    /* Get system palette entries */
    GetSystemPaletteEntries(hdc, 0, uiSystemPaletteEntries, ppeColors);

    /* Allocate an X colormap entry for every system palette entry */
    for (i = 0; i < uiSystemPaletteEntries; ++i) {
        pixel = i;

        /* Extract the color values for current palette entry */
        nRed = ppeColors[i].peRed << 8;
        nGreen = ppeColors[i].peGreen << 8;
        nBlue = ppeColors[i].peBlue << 8;
        winDebug("winGetPaletteDD - Allocating a color: %d; "
                 "%d %d %d\n", pixel, nRed, nGreen, nBlue);
        if (AllocColor(pcmap, &nRed, &nGreen, &nBlue, &pixel, 0) != Success) {
            ErrorF("winGetPaletteDD - AllocColor () failed, pixel %d\n", i);
            free(ppeColors);
            ppeColors = NULL;
            return FALSE;
        }

        pcmap->red[i].co.local.red = nRed;
        pcmap->red[i].co.local.green = nGreen;
        pcmap->red[i].co.local.blue = nBlue;
    }

    /* System is using a colormap */
    /* Set the black and white pixel indices */
    pScreen->whitePixel = uiSystemPaletteEntries - 1;
    pScreen->blackPixel = 0;

    /* Free colormap */
    free(ppeColors);
    ppeColors = NULL;

    /* Free the DC */
    if (hdc != NULL) {
        ReleaseDC(NULL, hdc);
        hdc = NULL;
    }

    return TRUE;
}

/*
 * Install the standard fb colormap, or the GDI colormap,
 * depending on the current screen depth.
 */

Bool
winCreateDefColormap(ScreenPtr pScreen)
{
    winScreenPriv(pScreen);
    winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
    unsigned short zero = 0, ones = 0xFFFF;
    VisualPtr pVisual = pScreenPriv->pRootVisual;
    ColormapPtr pcmap = NULL;
    Pixel wp, bp;

    winDebug("winCreateDefColormap\n");

    /* Use standard fb colormaps for non palettized color modes */
    if (pScreenInfo->dwBPP > 8) {
        winDebug("winCreateDefColormap - Deferring to "
                 "fbCreateDefColormap ()\n");
        return fbCreateDefColormap(pScreen);
    }

    /*
     *  AllocAll for non-Dynamic visual classes,
     *  AllocNone for Dynamic visual classes.
     */

    /*
     * Dynamic visual classes allow the colors of the color map
     * to be changed by clients.
     */

    winDebug("winCreateDefColormap - defColormap: %d\n", pScreen->defColormap);

    /* Allocate an X colormap, owned by client 0 */
    if (CreateColormap(pScreen->defColormap,
                       pScreen,
                       pVisual,
                       &pcmap,
                       (pVisual->class & DynamicClass) ? AllocNone : AllocAll,
                       0) != Success) {
        ErrorF("winCreateDefColormap - CreateColormap failed\n");
        return FALSE;
    }
    if (pcmap == NULL) {
        ErrorF("winCreateDefColormap - Colormap could not be created\n");
        return FALSE;
    }

    winDebug("winCreateDefColormap - Created a colormap\n");

    /* Branch on the visual class */
    if (!(pVisual->class & DynamicClass)) {
        /* Branch on engine type */
        if (pScreenInfo->dwEngine == WIN_SERVER_SHADOW_GDI) {
            /* Load the colors being used by the Shadow DIB */
            if (!winGetPaletteDIB(pScreen, pcmap)) {
                ErrorF("winCreateDefColormap - Couldn't get DIB colors\n");
                return FALSE;
            }
        }
        else {
            /* Load the colors from the default system palette */
            if (!winGetPaletteDD(pScreen, pcmap)) {
                ErrorF("winCreateDefColormap - Couldn't get colors "
                       "for DD\n");
                return FALSE;
            }
        }
    }
    else {
        wp = pScreen->whitePixel;
        bp = pScreen->blackPixel;

        /* Allocate a black and white pixel */
        if ((AllocColor(pcmap, &ones, &ones, &ones, &wp, 0) != Success)
            || (AllocColor(pcmap, &zero, &zero, &zero, &bp, 0) != Success)) {
            ErrorF("winCreateDefColormap - Couldn't allocate bp or wp\n");
            return FALSE;
        }

        pScreen->whitePixel = wp;
        pScreen->blackPixel = bp;

    }

    /* Install the created colormap */
    (*pScreen->InstallColormap) (pcmap);

    winDebug("winCreateDefColormap - Returning\n");

    return TRUE;
}