/*
 * Copyright (c) 1987, Oracle and/or its affiliates. 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 (including the next
 * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
 */

/*
 * This is based on cfbcmap.c.  The functions here are useful independently
 * of cfb, which is the reason for including them here.  How "mi" these
 * are may be debatable.
 */


#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <X11/X.h>
#include <X11/Xproto.h>
#include "scrnintstr.h"
#include "colormapst.h"
#include "resource.h"
#include "globals.h"
#include "micmap.h"

DevPrivateKeyRec micmapScrPrivateKeyRec;

int
miListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps)
{
    if (GetInstalledmiColormap(pScreen)) {
	*pmaps = GetInstalledmiColormap(pScreen)->mid;
	return 1;
    }
    return 0;
}

void
miInstallColormap(ColormapPtr pmap)
{
    ColormapPtr oldpmap = GetInstalledmiColormap(pmap->pScreen);

    if(pmap != oldpmap)
    {
	/* Uninstall pInstalledMap. No hardware changes required, just
	 * notify all interested parties. */
	if(oldpmap != (ColormapPtr)None)
	    WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid);
	/* Install pmap */
	SetInstalledmiColormap(pmap->pScreen, pmap);
	WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid);

    }
}

void
miUninstallColormap(ColormapPtr pmap)
{
    ColormapPtr curpmap = GetInstalledmiColormap(pmap->pScreen);

    if(pmap == curpmap)
    {
	if (pmap->mid != pmap->pScreen->defColormap)
	{
	    dixLookupResourceByType((pointer *)&curpmap,
				    pmap->pScreen->defColormap,
				    RT_COLORMAP, serverClient,
				    DixUseAccess);
	    (*pmap->pScreen->InstallColormap)(curpmap);
	}
    }
}

void
miResolveColor(unsigned short *pred, unsigned short *pgreen,
		unsigned short *pblue, VisualPtr pVisual)
{
    int shift = 16 - pVisual->bitsPerRGBValue;
    unsigned lim = (1 << pVisual->bitsPerRGBValue) - 1;

    if ((pVisual->class | DynamicClass) == GrayScale)
    {
	/* rescale to gray then rgb bits */
	*pred = (30L * *pred + 59L * *pgreen + 11L * *pblue) / 100;
	*pblue = *pgreen = *pred = ((*pred >> shift) * 65535) / lim;
    }
    else
    {
	/* rescale to rgb bits */
	*pred = ((*pred >> shift) * 65535) / lim;
	*pgreen = ((*pgreen >> shift) * 65535) / lim;
	*pblue = ((*pblue >> shift) * 65535) / lim;
    }
}

Bool
miInitializeColormap(ColormapPtr pmap)
{
    unsigned i;
    VisualPtr pVisual;
    unsigned lim, maxent, shift;

    pVisual = pmap->pVisual;
    lim = (1 << pVisual->bitsPerRGBValue) - 1;
    shift = 16 - pVisual->bitsPerRGBValue;
    maxent = pVisual->ColormapEntries - 1;
    if (pVisual->class == TrueColor)
    {
	unsigned limr, limg, limb;

	limr = pVisual->redMask >> pVisual->offsetRed;
	for(i = 0; i <= min(limr,maxent); i++)
	{
	    /* rescale to [0..65535] then rgb bits */
	    pmap->red[i].co.local.red =
		((((i * 65535) / limr) >> shift) * 65535) / lim;
	}
	for(; i <= maxent; i++) pmap->red[i].co.local.red = 65535;
	limg = pVisual->greenMask >> pVisual->offsetGreen;
	for(i = 0; i <= min(limg,maxent); i++)
	{
	    /* rescale to [0..65535] then rgb bits */
	    pmap->green[i].co.local.green =
		((((i * 65535) / limg) >> shift) * 65535) / lim;
	}
	for(; i <= maxent; i++) pmap->green[i].co.local.green = 65535;
	limb = pVisual->blueMask >> pVisual->offsetBlue;
	for(i = 0; i <= min(limb,maxent); i++)
	{
	    /* rescale to [0..65535] then rgb bits */
	    pmap->blue[i].co.local.blue =
		((((i * 65535) / limb) >> shift) * 65535) / lim;
	}
	for(; i <= maxent; i++) pmap->blue[i].co.local.blue = 65535;
    }
    else if (pVisual->class == StaticColor)
    {
	unsigned limr, limg, limb;

	limr = pVisual->redMask >> pVisual->offsetRed;
	limg = pVisual->greenMask >> pVisual->offsetGreen;
	limb = pVisual->blueMask >> pVisual->offsetBlue;
	for(i = 0; i <= maxent; i++)
	{
	    /* rescale to [0..65535] then rgb bits */
	    pmap->red[i].co.local.red =
		((((((i & pVisual->redMask) >> pVisual->offsetRed)
		    * 65535) / limr) >> shift) * 65535) / lim;
	    pmap->red[i].co.local.green =
		((((((i & pVisual->greenMask) >> pVisual->offsetGreen)
		    * 65535) / limg) >> shift) * 65535) / lim;
	    pmap->red[i].co.local.blue =
		((((((i & pVisual->blueMask) >> pVisual->offsetBlue)
		    * 65535) / limb) >> shift) * 65535) / lim;
	}
    }
    else if (pVisual->class == StaticGray)
    {
	for(i = 0; i <= maxent; i++)
	{
	    /* rescale to [0..65535] then rgb bits */
	    pmap->red[i].co.local.red = ((((i * 65535) / maxent) >> shift)
					 * 65535) / lim;
	    pmap->red[i].co.local.green = pmap->red[i].co.local.red;
	    pmap->red[i].co.local.blue = pmap->red[i].co.local.red;
	}
    }
    return TRUE;
}

/* When simulating DirectColor on PseudoColor hardware, multiple
   entries of the colormap must be updated
 */

#define AddElement(mask) { \
    pixel = red | green | blue; \
    for (i = 0; i < nresult; i++) \
  	if (outdefs[i].pixel == pixel) \
    	    break; \
    if (i == nresult) \
    { \
   	nresult++; \
	outdefs[i].pixel = pixel; \
	outdefs[i].flags = 0; \
    } \
    outdefs[i].flags |= (mask); \
    outdefs[i].red = pmap->red[red >> pVisual->offsetRed].co.local.red; \
    outdefs[i].green = pmap->green[green >> pVisual->offsetGreen].co.local.green; \
    outdefs[i].blue = pmap->blue[blue >> pVisual->offsetBlue].co.local.blue; \
}

int
miExpandDirectColors(ColormapPtr pmap, int ndef, xColorItem *indefs,
			xColorItem *outdefs)
{
    int	        red, green, blue;
    int	        maxred, maxgreen, maxblue;
    int	        stepred, stepgreen, stepblue;
    VisualPtr   pVisual;
    int         pixel;
    int         nresult;
    int         i;

    pVisual = pmap->pVisual;

    stepred = 1 << pVisual->offsetRed;
    stepgreen = 1 << pVisual->offsetGreen;
    stepblue = 1 << pVisual->offsetBlue;
    maxred = pVisual->redMask;
    maxgreen = pVisual->greenMask;
    maxblue = pVisual->blueMask;
    nresult = 0;
    for (;ndef--; indefs++)
    {
	if (indefs->flags & DoRed)
	{
	    red = indefs->pixel & pVisual->redMask;
    	    for (green = 0; green <= maxgreen; green += stepgreen)
    	    {
	    	for (blue = 0; blue <= maxblue; blue += stepblue)
	    	{
		    AddElement (DoRed)
	    	}
    	    }
	}
	if (indefs->flags & DoGreen)
	{
	    green = indefs->pixel & pVisual->greenMask;
    	    for (red = 0; red <= maxred; red += stepred)
    	    {
	    	for (blue = 0; blue <= maxblue; blue += stepblue)
	    	{
		    AddElement (DoGreen)
	    	}
    	    }
	}
	if (indefs->flags & DoBlue)
	{
	    blue = indefs->pixel & pVisual->blueMask;
    	    for (red = 0; red <= maxred; red += stepred)
    	    {
	    	for (green = 0; green <= maxgreen; green += stepgreen)
	    	{
		    AddElement (DoBlue)
	    	}
    	    }
	}
    }
    return nresult;
}

Bool
miCreateDefColormap(ScreenPtr pScreen)
{
/* 
 * In the following sources PC X server vendors may want to delete 
 * "_not_tog" from "#ifdef WIN32_not_tog"
 */
#ifdef WIN32_not_tog
    /*  
     * these are the MS-Windows desktop colors, adjusted for X's 16-bit 
     * color specifications.
     */
    static xColorItem citems[] = {
	{   0,      0,      0,      0, 0, 0 },
	{   1, 0x8000,      0,      0, 0, 0 },
	{   2,      0, 0x8000,      0, 0, 0 },
	{   3, 0x8000, 0x8000,      0, 0, 0 },
	{   4,      0,      0, 0x8000, 0, 0 },
	{   5, 0x8000,      0, 0x8000, 0, 0 },
	{   6,      0, 0x8000, 0x8000, 0, 0 },
	{   7, 0xc000, 0xc000, 0xc000, 0, 0 },
	{   8, 0xc000, 0xdc00, 0xc000, 0, 0 },
	{   9, 0xa600, 0xca00, 0xf000, 0, 0 },
	{ 246, 0xff00, 0xfb00, 0xf000, 0, 0 },
	{ 247, 0xa000, 0xa000, 0xa400, 0, 0 },
	{ 248, 0x8000, 0x8000, 0x8000, 0, 0 },
	{ 249, 0xff00,      0,      0, 0, 0 },
	{ 250,      0, 0xff00,      0, 0, 0 },
	{ 251, 0xff00, 0xff00,      0, 0, 0 },
	{ 252,      0,      0, 0xff00, 0, 0 },
	{ 253, 0xff00,      0, 0xff00, 0, 0 },
	{ 254,      0, 0xff00, 0xff00, 0, 0 },
	{ 255, 0xff00, 0xff00, 0xff00, 0, 0 }
    };
#define NUM_DESKTOP_COLORS sizeof citems / sizeof citems[0]
    int i;
#else
    unsigned short	zero = 0, ones = 0xFFFF;
#endif
    Pixel wp, bp;
    VisualPtr	pVisual;
    ColormapPtr	cmap;
    int alloctype;
    
    if (!dixRegisterPrivateKey(&micmapScrPrivateKeyRec, PRIVATE_SCREEN, 0))
	return FALSE;

    for (pVisual = pScreen->visuals;
	 pVisual->vid != pScreen->rootVisual;
	 pVisual++)
	;

    if (pScreen->rootDepth == 1 || (pVisual->class & DynamicClass))
	alloctype = AllocNone;
    else
	alloctype = AllocAll;

    if (CreateColormap(pScreen->defColormap, pScreen, pVisual, &cmap,
		       alloctype, 0) != Success)
	return FALSE;

    if (pScreen->rootDepth > 1) {
	wp = pScreen->whitePixel;
	bp = pScreen->blackPixel;
#ifdef WIN32_not_tog
	for (i = 0; i < NUM_DESKTOP_COLORS; i++) {
	    if (AllocColor (cmap,
			    &citems[i].red, &citems[i].green, &citems[i].blue,
			    &citems[i].pixel, 0) != Success)
		return FALSE;
	}
#else
	if ((AllocColor(cmap, &ones, &ones, &ones, &wp, 0) !=
       	       Success) ||
	    (AllocColor(cmap, &zero, &zero, &zero, &bp, 0) !=
       	       Success))
    	    return FALSE;
	pScreen->whitePixel = wp;
	pScreen->blackPixel = bp;
#endif
    }

    (*pScreen->InstallColormap)(cmap);
    return TRUE;
}

/*
 * Default true color bitmasks, should be overridden by
 * driver
 */

#define _RZ(d) ((d + 2) / 3)
#define _RS(d) 0
#define _RM(d) ((1 << _RZ(d)) - 1)
#define _GZ(d) ((d - _RZ(d) + 1) / 2)
#define _GS(d) _RZ(d)
#define _GM(d) (((1 << _GZ(d)) - 1) << _GS(d))
#define _BZ(d) (d - _RZ(d) - _GZ(d))
#define _BS(d) (_RZ(d) + _GZ(d))
#define _BM(d) (((1 << _BZ(d)) - 1) << _BS(d))
#define _CE(d) (1 << _RZ(d))

typedef struct _miVisuals {
    struct _miVisuals	*next;
    int			depth;
    int			bitsPerRGB;
    int			visuals;
    int			count;
    int			preferredCVC;
    Pixel		redMask, greenMask, blueMask;
} miVisualsRec, *miVisualsPtr;

static int  miVisualPriority[] = {
    PseudoColor, GrayScale, StaticColor, TrueColor, DirectColor, StaticGray
};

#define NUM_PRIORITY	6

static miVisualsPtr	miVisuals;

void
miClearVisualTypes(void)
{
    miVisualsPtr v;

    while ((v = miVisuals)) {
	miVisuals = v->next;
	free(v);
    }
}


Bool
miSetVisualTypesAndMasks(int depth, int visuals, int bitsPerRGB, 
			 int preferredCVC,
			 Pixel redMask, Pixel greenMask, Pixel blueMask)
{
    miVisualsPtr   new, *prev, v;
    int		    count;

    new = malloc(sizeof *new);
    if (!new)
	return FALSE;
    if (!redMask || !greenMask || !blueMask)
    {
	redMask = _RM(depth);
	greenMask = _GM(depth);
	blueMask = _BM(depth);
    }
    new->next = 0;
    new->depth = depth;
    new->visuals = visuals;
    new->bitsPerRGB = bitsPerRGB;
    new->preferredCVC = preferredCVC;
    new->redMask = redMask;
    new->greenMask = greenMask;
    new->blueMask = blueMask;
    count = (visuals >> 1) & 033333333333;
    count = visuals - count - ((count >> 1) & 033333333333);
    count = (((count + (count >> 3)) & 030707070707) % 077);	/* HAKMEM 169 */
    new->count = count;
    for (prev = &miVisuals; (v = *prev); prev = &v->next);
    *prev = new;
    return TRUE;
}

Bool
miSetVisualTypes(int depth, int visuals, int bitsPerRGB, int preferredCVC)
{
    return miSetVisualTypesAndMasks (depth, visuals, bitsPerRGB,
				     preferredCVC, 0, 0, 0);
}

int
miGetDefaultVisualMask(int depth)
{
    if (depth > MAX_PSEUDO_DEPTH)
	return LARGE_VISUALS;
    else if (depth >= MIN_TRUE_DEPTH)
	return ALL_VISUALS;
    else if (depth == 1)
	return StaticGrayMask;
    else
	return SMALL_VISUALS;
}

static Bool
miVisualTypesSet (int depth)
{
    miVisualsPtr    visuals;

    for (visuals = miVisuals; visuals; visuals = visuals->next)
	if (visuals->depth == depth)
	    return TRUE;
    return FALSE;
}

Bool
miSetPixmapDepths (void)
{
    int	d, f;
    
    /* Add any unlisted depths from the pixmap formats */
    for (f = 0; f < screenInfo.numPixmapFormats; f++) 
    {
	d = screenInfo.formats[f].depth;
	if (!miVisualTypesSet (d))
	{
	    if (!miSetVisualTypes (d, 0, 0, -1))
		return FALSE;
	}
    }
    return TRUE;
}

/*
 * Distance to least significant one bit
 */
static int
maskShift (Pixel p)
{
    int	s;

    if (!p) return 0;
    s = 0;
    while (!(p & 1))
    {
	s++;
	p >>= 1;
    }
    return s;
}

/*
 * Given a list of formats for a screen, create a list
 * of visuals and depths for the screen which corespond to
 * the set which can be used with this version of cfb.
 */

Bool
miInitVisuals(VisualPtr *visualp, DepthPtr *depthp, int *nvisualp,
		int *ndepthp, int *rootDepthp, VisualID *defaultVisp,
		unsigned long sizes, int bitsPerRGB, int preferredVis)

{
    int		i, j = 0, k;
    VisualPtr	visual;
    DepthPtr	depth;
    VisualID	*vid;
    int		d, b;
    int		f;
    int		ndepth, nvisual;
    int		nvtype;
    int		vtype;
    miVisualsPtr   visuals, nextVisuals;
    int		*preferredCVCs, *prefp;
    int		first_depth;

    /* none specified, we'll guess from pixmap formats */
    if (!miVisuals) 
    {
    	for (f = 0; f < screenInfo.numPixmapFormats; f++) 
    	{
	    d = screenInfo.formats[f].depth;
	    b = screenInfo.formats[f].bitsPerPixel;
	    if (sizes & (1 << (b - 1)))
		vtype = miGetDefaultVisualMask(d);
	    else
		vtype = 0;
	    if (!miSetVisualTypes (d, vtype, bitsPerRGB, -1))
		return FALSE;
    	}
    }
    nvisual = 0;
    ndepth = 0;
    for (visuals = miVisuals; visuals; visuals = nextVisuals) 
    {
	nextVisuals = visuals->next;
	ndepth++;
	nvisual += visuals->count;
    }
    depth = malloc(ndepth * sizeof (DepthRec));
    visual = malloc(nvisual * sizeof (VisualRec));
    preferredCVCs = malloc(ndepth * sizeof(int));
    if (!depth || !visual || !preferredCVCs)
    {
	free(depth);
	free(visual);
	free(preferredCVCs);
	return FALSE;
    }
    *depthp = depth;
    *visualp = visual;
    *ndepthp = ndepth;
    *nvisualp = nvisual;
    prefp = preferredCVCs;
    for (visuals = miVisuals; visuals; visuals = nextVisuals) 
    {
	nextVisuals = visuals->next;
	d = visuals->depth;
	vtype = visuals->visuals;
	nvtype = visuals->count;
	*prefp = visuals->preferredCVC;
	prefp++;
	vid = NULL;
	if (nvtype)
	{
	    vid = malloc(nvtype * sizeof (VisualID));
	    if (!vid) {
		free(preferredCVCs);
		return FALSE;
	    }
	}
	depth->depth = d;
	depth->numVids = nvtype;
	depth->vids = vid;
	depth++;
	for (i = 0; i < NUM_PRIORITY; i++) {
	    if (! (vtype & (1 << miVisualPriority[i])))
		continue;
	    visual->class = miVisualPriority[i];
	    visual->bitsPerRGBValue = visuals->bitsPerRGB;
	    visual->ColormapEntries = 1 << d;
	    visual->nplanes = d;
	    visual->vid = *vid = FakeClientID (0);
	    switch (visual->class) {
	    case PseudoColor:
	    case GrayScale:
	    case StaticGray:
		visual->redMask = 0;
		visual->greenMask =  0;
		visual->blueMask =  0;
		visual->offsetRed  =  0;
		visual->offsetGreen = 0;
		visual->offsetBlue =  0;
		break;
	    case DirectColor:
	    case TrueColor:
		visual->ColormapEntries = _CE(d);
		/* fall through */
	    case StaticColor:
		visual->redMask =  visuals->redMask;
		visual->greenMask =  visuals->greenMask;
		visual->blueMask =  visuals->blueMask;
		visual->offsetRed  =  maskShift (visuals->redMask);
		visual->offsetGreen = maskShift (visuals->greenMask);
		visual->offsetBlue =  maskShift (visuals->blueMask);
	    }
	    vid++;
	    visual++;
	}
	free(visuals);
    }
    miVisuals = NULL;
    visual = *visualp;
    depth = *depthp;

    /*
     * if we did not supplyied by a preferred visual class
     * check if there is a preferred class in one of the depth
     * structures - if there is, we want to start looking for the
     * default visual/depth from that depth.
     */
    first_depth = 0;
    if (preferredVis < 0 && defaultColorVisualClass < 0 ) {
       for (i = 0; i < ndepth; i++) {
	  if (preferredCVCs[i] >= 0) {
	     first_depth = i;
	     break;
	  }
       }
    }

    for (i = first_depth; i < ndepth; i++)
    {
	int prefColorVisualClass = -1;

	if (defaultColorVisualClass >= 0)
	    prefColorVisualClass = defaultColorVisualClass;
	else if (preferredVis >= 0)
	    prefColorVisualClass = preferredVis;
	else if (preferredCVCs[i] >= 0)
	    prefColorVisualClass = preferredCVCs[i];

	if (*rootDepthp && *rootDepthp != depth[i].depth)
	    continue;
	
	for (j = 0; j < depth[i].numVids; j++)
	{
	    for (k = 0; k < nvisual; k++)
		if (visual[k].vid == depth[i].vids[j])
		    break;
	    if (k == nvisual)
		continue;
	    if (prefColorVisualClass < 0 ||
		visual[k].class == prefColorVisualClass)
		break;
	}
	if (j != depth[i].numVids)
	    break;
    }
    if (i == ndepth) {
	i = 0;
	j = 0;
    }
    *rootDepthp = depth[i].depth;
    *defaultVisp = depth[i].vids[j];
    free(preferredCVCs);

    return TRUE;
}