/***********************************************************************
*
*   DrvColors.c
*
*   Macintosh device specific color sub-system implementation.
*
*   Copyright (c) 1994-1997, Willows Software Inc.  All rights reserved.
*
************************************************************************/

#include "DrvColors.h"
#include "DrvHook.h"
#include "DrvDC.h"
#include "DrvDP.h"
#include "DrvUtils.h"
#include "Log.h"

//#define USELOCALCLUT					/* Uses a local CLUT and pallette */
//#define CHECKACTUALCOLOR		/* Checks the actual color obtained, which may not be exactly what was desired, causing dithering to occur */

#define		MAX_COLORS	256

/* Local prototypes */

static UINT DrvSystemPaletteUse(LPDRIVERDC, UINT, BOOL);
static HPALETTE DrvGetSystemPalette(LPDRIVERDC);
static UINT DrvSetSystemPalette(LPDRIVERDC, BOOL, LPPALETTEENTRY);
static BOOL DrvUpdateColors(LPDRIVERDC);

static DWORD DrvMakePixel(COLORREF);
static COLORREF DrvGetColorRef(DWORD pixel);
static COLORREF	DrvGetNearestColor(COLORREF);
static BOOL DrvFillRGBTable(RGBQUAD *,int);
static void DrvSetupColors(BOOL init);


typedef struct {
	unsigned long rgb1;
	unsigned long	 rgb2;
} COLORS;

COLORS ctab[] = {
	RGB(0,0,0), 	RGB(255,255,255),
	RGB(128,  0,0), RGB(0,  255,255),
	RGB(0,  128,0), RGB(255,0,  255),
	RGB(128,128,0), RGB(0,  0,  255),
	RGB(0,  0,  128), RGB(255,255,0),
	RGB(128,0  ,128), RGB(  0,255,  0),
	RGB(  0,128,128), RGB(255,  0,  0),
	RGB(192,220,192), RGB(240,202,166),
	RGB(192,192,192), RGB(128,128,128),
	RGB(166,202,240), RGB(240,251,255),
	-1,-1
};


#ifdef USELOCALCLUT	
CTabHandle gDefaultCLUT = NULL;
PaletteHandle gDefaultPalette = NULL;
#endif

/* Local variables */
static BOOL	gTrueColor = FALSE;
static DWORD	pixel_white = 254;	//works at 255
static DWORD	pixel_black =1;			//works at 0



/********************************************************************
*  PrivateColorsHook
*
********************************************************************/
DWORD PrivateColorsHook(WORD wFunc,
	LPARAM dwParam1,
	LPARAM dwParam2,
	LPVOID lpParams)
{
    switch (wFunc) {
	case DSUBSYSTEM_INIT:
	    DrvSetupColors(dwParam2);
	    return 1L;

	case DSUBSYSTEM_GETCAPS:
	case DSUBSYSTEM_EVENTS:
	    return 1L;

	/* PLH_SYSTEMPALETTEUSE - (UINT) get/set system palette use
	 * dwParam1 - (LPDRIVERDC) lpDriverDC
	 * dwParam2 - (UINT) uSystemPaletteUse
	 *            SYSPAL_STATIC - 20 static colors (16 VGA colors + 4)
	 *            SYSPAL_NOSTATIC - 2 static colors (black / white)
	 * lpParams - (BOOL) bSetSystemPaletteUse
	 *            TRUE - set new system palette use
	 *                   get old system palette use
	 *            FALSE - get system palette use
	 */
	case PLH_SYSTEMPALETTEUSE:
	    return DrvSystemPaletteUse((LPDRIVERDC) dwParam1, (UINT) dwParam2,
		(BOOL) lpParams);

	/* PLH_GETSYSTEMPALETTE - (HPALETTE) get system palette
	 * dwParam1 - (LPDRIVERDC) lpDriverDC
	 * dwParam2 - (unused)
	 * lpParams - (unused)
	 */
	case PLH_GETSYSTEMPALETTE:
	    return DrvGetSystemPalette((LPDRIVERDC) dwParam1);

	/* PLH_SETSYSTEMPALETTE - (UINT) set system palette
	 * dwParam1 - (LPDRIVERDC) lpDriverDC
	 * dwParam2 - (BOOL) bInitSystemPalette
	 *            TRUE - mark static system palette entries as used
	 *                   mark non-static system palette entries as unused
	 *                   assign system palette index
	 *            FALSE - assign system palette index
	 * lpParams - (LPPALETTEENTRY) lpPaletteEntry
	 */
	case PLH_SETSYSTEMPALETTE:
	    return DrvSetSystemPalette((LPDRIVERDC) dwParam1, (BOOL) dwParam2,
		(LPPALETTEENTRY) lpParams);

	/* PLH_UPDATECOLORS - (BOOL) update colors after palette change
	 * dwParam1 - (LPDRIVERDC) lpDriverDC
	 * dwParam2 - (unused)
	 * lpParams - (unused)
	 */
	case PLH_UPDATECOLORS:
	    return DrvUpdateColors((LPDRIVERDC) dwParam1);

	case PLH_MAKEPIXEL:
	    return DrvMakePixel((COLORREF)dwParam1);

	case PLH_GETNEARCOLOR:
	    return (DWORD)DrvGetNearestColor((COLORREF)dwParam1);

	case PLH_GETCOLORREF:
	    return (DWORD)DrvGetColorRef(dwParam1);

	case PLH_FILLRGBTABLE:
	    return (DWORD)DrvFillRGBTable((LPRGBQUAD)lpParams,(int)dwParam1);

	default:
	    return 0L;
    }
}

static UINT DrvSystemPaletteUse(LPDRIVERDC lpDriverDC,
	UINT uSystemPaletteUse,
	BOOL bSetSystemPaletteUse)
{
	ERRSTR((LF_ERR, "STUB: PLH_SYSTEMPALETTEUSE: DrvSystemPaletteUse\n"));
	return (SYSPAL_ERROR);

/* **** SAMPLE CODE FOLLOWS ***

	UINT uOldSystemPaletteUse;

	APISTR((LF_API, "DrvSystemPaletteUse: lpDriverDC %p\n"
		" uSystemPaletteUse %d bSetSystemPaletteUse %d\n",
		lpDriverDC, uSystemPaletteUse, bSetSystemPaletteUse));

	uOldSystemPaletteUse = lpDriverDC->dp->uSystemPaletteUse;

	if (bSetSystemPaletteUse)
	{
		lpDriverDC->dp->uSystemPaletteUse = uSystemPaletteUse;
		// *** mac specific code goes here ***
	}

	return (uOldSystemPaletteUse);

 */
}

static HPALETTE DrvGetSystemPalette(LPDRIVERDC lpDriverDC)
{
	ERRSTR((LF_ERR, "STUB: PLH_GETSYSTEMPALETTE: DrvGetSystemPalette\n"));
	return ((HPALETTE) 0);
}

static UINT DrvSetSystemPalette(LPDRIVERDC lpDriverDC,
	BOOL bInitSystemPalette,
	LPPALETTEENTRY lpPaletteEntry)
{
	ERRSTR((LF_ERR, "STUB: PLH_SETSYSTEMPALETTE: DrvSetSystemPalette\n"));
	return ((UINT) 0);
}

static BOOL DrvUpdateColors(LPDRIVERDC lpDriverDC)
{
	ERRSTR((LF_ERR, "STUB: PLH_UPDATECOLORS: DrvUpdateColors\n"));
	return (FALSE);
}

/********************************************************************
*   Color2BWPixmap
*
*	Changes color pixels to black and white color pixels.
*	Used for preparing pixmap to make the jump to a 1 bit deep pixmap.
********************************************************************/
void Color2BWPixmap(PixMapHandle pixmap, DWORD backColorRef)
{
char cMemTags;
char *data;
long bytes, i, actualRowBytes;
long indexColor;

	if ((**pixmap).pixelSize != 8)			// LATER: handle odd sized pixmaps.
		return;

	actualRowBytes = (**pixmap).rowBytes & 0x7FFF;	
	bytes = actualRowBytes * ((**pixmap).bounds.bottom - (**pixmap).bounds.top);

	cMemTags = HGetState((Handle)pixmap); 
	HLock((Handle) pixmap);

	data = (**pixmap).baseAddr;

	indexColor = DrvMakePixel(backColorRef);

	/* loop through and get/set the pixels */
	for (i=0; i<bytes; i++)
		data[i] = (data[i] == indexColor) ? pixel_white : pixel_black;
	
	HSetState((Handle)pixmap, cMemTags);

}



/********************************************************************
*   DrvGetNearestColor
*
*  Get the nearest matching COLORREF.
********************************************************************/
static COLORREF DrvGetNearestColor(COLORREF cr)
{
DWORD pixel;
#ifdef CHECKACTUALCOLOR
RGBColor rgbColor;
#endif

    if (gTrueColor)
		return cr;

    /* allocate color, or get nearest... */
    pixel = DrvMakePixel(cr);	

#ifdef CHECKACTUALCOLOR
	Index2Color(pixel, &rgbColor);

	rgbColor.red >>= 8;			/* Divide by 256 */
	rgbColor.green >>= 8;
	rgbColor.blue >>= 8;

    return RGB(rgbColor.red, rgbColor.green, rgbColor.blue);
#else
	return(cr);
#endif

}


/********************************************************************
*   DrvGetColorRef
*
*  Lookup a pixels value and return the COLORREF.
********************************************************************/
static COLORREF DrvGetColorRef(DWORD pixel)
{
RGBColor rgbColor;

#ifdef USELOCALCLUT
	rgbColor = (**gDefaultCLUT).ctTable[pixel].rgb;
#else
	Index2Color(pixel, &rgbColor);
#endif

	if (gTrueColor)
		return(pixel);
	else
		return (MACRGBTOCOLORREF(&rgbColor));
}
 
 
/********************************************************************
*   DrvFillRGBTable
*
********************************************************************/
static BOOL DrvFillRGBTable(RGBQUAD *lprgb, int nNumColors)
{
    int i;
    COLORREF cr;

#ifdef LATER
    /* for now it can only be called with nNumColors == 256 */
#else
    if (nNumColors != MAX_COLORS)
	return FALSE;
#endif

    for (i = 0; i < nNumColors; i++) {
		cr = DrvGetColorRef((DWORD)i);

		lprgb[i].rgbBlue = GetBValue(cr);
		lprgb[i].rgbGreen = GetGValue(cr);
		lprgb[i].rgbRed = GetRValue(cr);
		lprgb[i].rgbReserved = 0;
    }
    return TRUE;
}



/********************************************************************
*   DrvMakePixel
*
*  Get the color PIXEL index from the rgb COLOREF.
********************************************************************/
static DWORD DrvMakePixel(COLORREF value)
{
long indexColor;
RGBColor rgbColor;

#ifdef DEBUG
RGBColor whatRGBDidItGet;
short realColorAvail;
#endif

#ifdef USELOCALCLUT	
long min, match, delta, dx;
int err, i;
ColorSpecPtr ctTablePtr;
#endif

	/* Convert the color */
	COLORREFTOMACRGB(value, &rgbColor);

#ifndef USELOCALCLUT	
	/* Get the color's index from the current table */	
	indexColor = Color2Index(&rgbColor);
#else
	/* Use our own clut */
    if(value == RGB(255,255,255))
		return pixel_white;

    if(value == RGB(0,0,0))
		return pixel_black;

	/* LATER: Add support for true color devices */
	min = 65535*65535*65535;
	match = 0;
	
	HLock((Handle)gDefaultCLUT);					/* Lock for ctTable ptr reference */
	ctTablePtr = (**gDefaultCLUT).ctTable;	/* Easier and faster(?) dereferencing */
	/* Search for an identical match */
	for (i=0; i<256; i++) 
		if (ctTablePtr[i].rgb.red == rgbColor.red &&
			 ctTablePtr[i].rgb.green == rgbColor.green &&
			 ctTablePtr[i].rgb.blue == rgbColor.blue) 
		{
			match = i;
			min = 0;
			break;
		}

	/* No identical match was found, look for the closest match */
	if (match == 0)
		for (i=0; i<256; i++) {
			dx = abs(ctTablePtr[i].rgb.red - rgbColor.red);
			delta = dx;
			dx = abs(ctTablePtr[i].rgb.green - rgbColor.green);
			delta += dx;
			dx = abs(ctTablePtr[i].rgb.blue - rgbColor.blue);
			delta += dx;
			
			if (delta < min) {
				min = delta;
				match = i;
			}
		}

	indexColor = match;
	
	HUnlock((Handle)gDefaultCLUT);
#endif


#ifdef DEBUG
	realColorAvail = RealColor(&rgbColor);	/* See if the actual color is available (for debugging)*/
	Index2Color(indexColor, &whatRGBDidItGet);
#endif
	
	return(indexColor);
}


static void DrvSetupColors(BOOL init)
{
	CTabHandle srcCLUT = NULL;
	short entries = 256;

	if (init) {
#ifdef USELOCALCLUT	
		unsigned long index;
		int	last;
		RGBColor macRGB;
		/* Create and use our own CLUT (color lookup table) */
		gDefaultCLUT=GetCTable(128);
		gDefaultPalette = NewPalette(entries, gDefaultCLUT, pmCourteous, 2);
//		gDefaultPalette = GetNewPalette(128);
		
		/* Set the "system" colors */
		last = entries - 1;
		for (index=0; index<10; index++) {
			/* Primary color */
			COLORREFTOMACRGB(ctab[index].rgb1, &macRGB);
//			SetEntryColor(gDefaultPalette, index, &macRGB);
			SetEntryUsage(gDefaultPalette, index, (pmExplicit + pmTolerant), 0);
			/* Complimentary color */
			COLORREFTOMACRGB(ctab[index].rgb2, &macRGB);
//			SetEntryColor(gDefaultPalette, last - index, &macRGB);
			SetEntryUsage(gDefaultPalette, last - index, (pmExplicit + pmTolerant), 0);
		}
			
		/* Set the palette as the default for the application */
//		SetPalette ((WindowPtr) -1, gDefaultPalette, TRUE); 
#endif

		if (gDspl->screenDepth > 8)
			gTrueColor = TRUE;

	}
	else {

#ifdef USELOCALCLUT	
		if (gDefaultCLUT)
			DisposeHandle((Handle)gDefaultCLUT);
		if (gDefaultPalette)
			DisposePalette(gDefaultPalette);
		RestoreDeviceClut(NULL);
#endif
	}
		
}

