#include "X.h"
#include "Xproto.h"
#include "scrnintstr.h"
#include "cmapst.h"
#include "resource.h"
#include "vga.h"

static ColormapPtr InstalledMaps[MAXSCREENS];

static ColormapPtr vgaPriv[16];	/* pointers to the last 16 cmaps */
static int next_index;

int
vgaListInstalledColormaps(pScreen, pmaps)
ScreenPtr	pScreen;
Colormap	*pmaps;
{
#if MAXSCREENS == 1
	*pmaps = InstalledMaps[0]->mid;
#else
	*pmaps = InstalledMaps[pScreen->myNum]->mid;
#endif
	return 1;
}

void
vgaInstallColormap(pmap)
    ColormapPtr	pmap;
{
#if MAXSCREENS == 1
#define index 0
#else
    int index = pmap->pScreen->myNum;
#endif
    ColormapPtr oldpmap = InstalledMaps[index];
    int hardware_index = (int)(long)pmap->devPriv;

    if(pmap != oldpmap)
    {
	register Entry *pent = pmap->red;
	u_char red, green, blue;
	int i;

	if(oldpmap != (ColormapPtr)None)
	    WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid);

	if (hardware_index < 0) {
	    hardware_index = next_index;
	    while (vgaPriv[hardware_index]) {
		    hardware_index = (hardware_index + 1) & 15;
		    if (hardware_index == next_index) {
			    hardware_index = (hardware_index - 1) & 15;
			    break;
		    }
	    }
	    if (vgaPriv[hardware_index])
		vgaPriv[hardware_index]->devPriv = (pointer)(-1);
	    vgaPriv[hardware_index] = pmap;
	    pmap->devPriv = (pointer)(long)hardware_index;
	    /* Install pmap */
	    outp(0x3c8, hardware_index << 4);
	    for (i = 0; i < pmap->pVisual->ColormapEntries; i++, pent++) {
		if (pent->fShared) {
		    red = (u_char)((u_char)(pent->co.shco.red->color >> 8) >> 2);
		    green = (u_char)((u_char)(pent->co.shco.green->color >> 8) >> 2);
		    blue = (u_char)((u_char)(pent->co.shco.blue->color >> 8) >> 2);
		} else {
		    red = (u_char)((u_char)(pent->co.local.red >> 8) >> 2);
		    green = (u_char)((u_char)(pent->co.local.green >> 8) >> 2);
		    blue = (u_char)((u_char)(pent->co.local.blue >> 8) >> 2);
		}
		outp(0x3c9, red);
		outp(0x3c9, green);
		outp(0x3c9, blue);
	    }
	}
	/* Select this hardware map previously (or recently) installed. */
	outp(0x3c0, 0x34);	/* color select register */
	outp(0x3c0, hardware_index);		/* hardware index */
	InstalledMaps[index] = pmap;
	WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid);
    }
#undef index
}

void
vgaUninstallColormap(pmap)
    ColormapPtr	pmap;
{
    ColormapPtr defmap = (ColormapPtr)
	    LookupIDByType(pmap->pScreen->defColormap, RT_COLORMAP);

    /* Simple - just install the default colormap.  vgaInstallColormap
     * will uninstall this map. */
    vgaInstallColormap(defmap);
}

void
vgaResolveColor(pred, pgreen, pblue, pVisual)
unsigned short	*pred, *pgreen, *pblue;
VisualPtr	pVisual;
{
	/* We get 6 bits for each color.  round to nearest value */
	if (*pred < 0xfc00)
		*pred += 0x0200;
	*pred &= 0xfc00;
	if (*pgreen < 0xfc00)
		*pgreen += 0x0200;
	*pgreen &= 0xfc00;
	if (*pblue < 0xfc00)
		*pblue += 0x0200;
	*pblue &= 0xfc00;
}

Bool
vgaCreateColormap(pmap)
ColormapPtr pmap;
{
	/* This code from cfbInitialize332Colormap() cfb/cfbcmap.c */
	int i;

	/* Each color is 6 bits, assume ColormapEntries is 16. */
	pmap->devPriv = (pointer)(-1);	/* not installed */
	for (i = 0; i < pmap->pVisual->ColormapEntries; i++) {
		pmap->red[i].co.local.red = (i & 3) << 14;
		pmap->red[i].co.local.green = ((i & 4) | (i & 4) >> 1) << 13;
		pmap->red[i].co.local.blue = ((i & 8)|(i & 8) >> 1) << 12;
	}
	return TRUE;
}

void
vgaDestroyColormap(pmap)
ColormapPtr pmap;
{
	int hardware_index = (int)(long)pmap->devPriv;

	if (hardware_index >= 0 && vgaPriv[hardware_index] == pmap)
		vgaPriv[hardware_index] = 0;
}

void
vgaStoreColors(pmap, ndef, pdefs)
ColormapPtr pmap;
int ndef;
xColorItem *pdefs;
{
	int pixel;
	register Entry *pent;
	unsigned char red, green, blue;
	int hardware_index = (int)(long)pmap->devPriv;

	if (hardware_index < 0)
		return;
	while (ndef-- > 0) {
		pixel = (int)pdefs->pixel;
		/* We have to store red, green, and blue, so ignore
		 * pdefs->flags and get the values out of the colormap
		 * itself.
		 */
		pent = pmap->red + pixel;
		if (pent->fShared) {
		    red = (u_char)((u_char)(pent->co.shco.red->color >> 8) >> 2);
		    green = (u_char)((u_char)(pent->co.shco.green->color >> 8) >> 2);
		    blue = (u_char)((u_char)(pent->co.shco.blue->color >> 8) >> 2);
		} else {
		    red = (u_char)((u_char)(pent->co.local.red >> 8) >> 2);
		    green = (u_char)((u_char)(pent->co.local.green >> 8) >> 2);
		    blue = (u_char)((u_char)(pent->co.local.blue >> 8) >> 2);
		}
		outp(0x3c8,  (hardware_index << 4) + pixel);
		outp(0x3c9, red);
		outp(0x3c9, green);
		outp(0x3c9, blue);
		pdefs++;
	}
}
