/* Copyright 1990 by Christopher A. Wichura.
   See file GIFMachine.doc for full description of rights.
*/

#include "GIFMachine.h"

extern struct GIFdescriptor gdesc;
EXTERNBITPLANE;

extern char *AbortMsg;

extern BOOL Laced;

UWORD *SHAMmem;

struct RGB Palette[16];

extern UBYTE PaletteBuf[MAXCOLOURS];
extern ULONG ColourBuf[MAXCOLOURS];
static ULONG ErrBuf[MAXCOLOURS];

#define ISLACED (Laced && (y != gdesc.gd_Height - 1))

void GIFtoSHAM(void)
{
	register UWORD x;
	register UWORD current;
	register int index;
	register int index2;
	ULONG error;
	UWORD y;
	int Pal;
	int ShamIndex;
	int colours;

	ULONG TotalErr, LineErr;

	ULONG bestpal;
	ULONG besterr;

	UBYTE CurrentRed,  CurrentGreen,  CurrentBlue;
	UBYTE PreviousRed, PreviousGreen, PreviousBlue;

	MyPrintf("...Converting to SHAM format.\n......Line");

	ShamIndex = TotalErr = 0;

	/* palette zero is always the background colour.  regardless of
	   what the GIF specified for the background, we always use black.
	   this is a kludge to get around a hardware `feature' that causes
	   all overscanned screens to have a black background regardless
	   of what the background was specified to be.
	*/

	Palette[0].rgb_Red = Palette[0].rgb_Green = Palette[0].rgb_Blue = 0;

	for (y = 0; y < gdesc.gd_Height; y += (ISLACED ? 2 : 1)) {
		if (ISLACED)
			MyPrintf("s %5ld-%5ld", y, y + 1);
		else
			MyPrintf(" %5ld", y);

		if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
			MyPrintf("\n%s", AbortMsg);
			MyExit(ABORTEXITVAL);
		}

		memset((char *)ColourBuf, 0, sizeof(ColourBuf));

		for (colours = index2 = 0; index2 < (ISLACED ? 2 : 1); index2++)
			for (x = 0; x < gdesc.gd_Width; x++) {
				current = GetValue(x, y + index2);

				if (ColourBuf[current]++ == 0)
					colours++;
			}

		MyPrintf(", %4ld unique colours", colours);

		memset((char *)PaletteBuf, 0, sizeof(PaletteBuf));

		for (index = 0; index < MAXCOLOURS; index++) {
			if (ColourBuf[index] == 0)
				continue;

			ErrBuf[index] = RGBdiff(GetRed(index),
						GetGreen(index),
						GetBlue(index),
						Palette[0].rgb_Red,
						Palette[0].rgb_Green,
						Palette[0].rgb_Blue);
		}

		for (Pal = 1; Pal < 16; Pal++) {
			for (besterr = index = 0; index < MAXCOLOURS; index++) {
				if (ColourBuf[index] == 0)
					continue;

				if (ErrBuf[index] * ColourBuf[index] >= besterr) {
					bestpal = index;
					besterr = ErrBuf[index] * ColourBuf[index];
				}
			}

			Palette[Pal].rgb_Red   = GetRed(bestpal);
			Palette[Pal].rgb_Green = GetGreen(bestpal);
			Palette[Pal].rgb_Blue  = GetBlue(bestpal);

			for (index = 0; index < MAXCOLOURS; index++) {
				if (ColourBuf[index] == 0)
					continue;

				error = RGBdiff(GetRed(index),
						GetGreen(index),
						GetBlue(index),
						Palette[Pal].rgb_Red,
						Palette[Pal].rgb_Green,
						Palette[Pal].rgb_Blue);

				if (error < ErrBuf[index]) {
					ErrBuf[index] = error;
					PaletteBuf[index] = Pal;
				}
			}
		}

		for (index = 0; index < 16; index++)
			SHAMmem[ShamIndex++] = (UWORD)(
				Palette[index].rgb_Red   << 8 |
				Palette[index].rgb_Green << 4 |
				Palette[index].rgb_Blue);

		for (index2 = 0; index2 < (ISLACED ? 2 : 1); index2++) {
			PreviousRed   = Palette[0].rgb_Red;
			PreviousGreen = Palette[0].rgb_Green;
			PreviousBlue  = Palette[0].rgb_Blue;

			for (LineErr = x = 0; x < gdesc.gd_Width; x++) {
				current = GetValue(x, y + index2);

				CurrentRed   = GetRed(current);
				CurrentGreen = GetGreen(current);
				CurrentBlue  = GetBlue(current);

				besterr = ErrBuf[current];
				bestpal = PaletteBuf[current];

				error = RGBdiff(
					CurrentRed,
					CurrentGreen,
					CurrentBlue,
					CurrentRed,
					PreviousGreen,
					PreviousBlue);

				if (error < besterr) {
					besterr = error;
					bestpal = 16;
				}

				error = RGBdiff(
					CurrentRed,
					CurrentGreen,
					CurrentBlue,
					PreviousRed,
					CurrentGreen,
					PreviousBlue);

				if (error < besterr) {
					besterr = error;
					bestpal = 17;
				}

				error = RGBdiff(
					CurrentRed,
					CurrentGreen,
					CurrentBlue,
					PreviousRed,
					PreviousGreen,
					CurrentBlue);

				if (error < besterr) {
					besterr = error;
					bestpal = 18;
				}

				if (bestpal < 16) {
					PutValue(x, y + index2, bestpal);

					PreviousRed   = Palette[bestpal].rgb_Red;
					PreviousGreen = Palette[bestpal].rgb_Green;
					PreviousBlue  = Palette[bestpal].rgb_Blue;
				} else if (bestpal == 16) {
					PutValue(x, y + index2, CurrentRed | 0x20);

					PreviousRed = CurrentRed;
				} else if (bestpal == 17) {
					PutValue(x, y + index2, CurrentGreen | 0x30);

					PreviousGreen = CurrentGreen;
				} else {
					PutValue(x, y + index2, CurrentBlue | 0x10);

					PreviousBlue = CurrentBlue;
				}

				LineErr += besterr;
			}

			TotalErr += LineErr;
		}

		MyPrintf("\x9B" "%sD\x9BK", (ISLACED ? "34" : "27"));
	}

	{
		ULONG	ErrPerPixel;
		char	TextBuf[10];

		ErrPerPixel = ((TotalErr / gdesc.gd_Height) * 1000) / gdesc.gd_Width;
		MySPrintf(TextBuf, "%ld.%03ld", ErrPerPixel / 1000, ErrPerPixel % 1000);

		MyPrintf("\x9B" "4DTotal colour error = %ld, Mean per line = %ld, Per pixel = %s.\n",
			TotalErr, TotalErr / gdesc.gd_Height, TextBuf);
	}
}
