#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <exec/memory.h>
#include "graffiti_font.h"

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

struct GraffitiFont
{
    struct Node Node;
    UBYTE NColors;
    UBYTE Width[256];
    UBYTE Height;
    UBYTE Flags;
    UBYTE BaseLine;
    UBYTE FillColor;
    UBYTE BackColor;
    UBYTE pad0;
    ULONG FontMemSize;
    struct GraffitiColor * ColorMap;
    UBYTE * Colors;
    UBYTE * CharOffsets[256];
    UBYTE * FontData;
};

#define GFF_FIXED	0x01

struct GraffitiFont * ReadFont (struct GraffitiData * gd, STRPTR fontname)
{
    static struct GraffitiColor colormap[256];
    BPTR fh;
    UBYTE line[256], * ptr;
    UBYTE colorchars[256];
    int ncolors, height, width, flags, baseline;
    int infos, t, lineno;
    struct GraffitiFont * font = NULL;

#define SKIPSPACE(x)        while (isspace(*x)) x++
#define SKIPWORD(x)         while (!isspace(*x) && *x) x++; \
			    SKIPSPACE(x)

    strcpy (line, fontname);
    strcat (line, ".gfont");

    if (!(fh = Open (line, MODE_OLDFILE)) )
    {
	fprintf (stderr, "ReadFont: Can't open font '%s'\n", fontname);
	return NULL;
    }

    infos = 0;
    lineno = 0;

    memset (colorchars, 0, sizeof (colorchars));

    while (FGets (fh, line, sizeof (line)))
    {
	lineno ++;

	if (*line == '#' || *line == '\n')
	    continue;

	ptr = line;

	SKIPSPACE(ptr); if (!*ptr) continue;

	if (!Strnicmp (ptr, "COLOR", 5) ||
	    !Strnicmp (ptr, "COLOUR", 6))
	{
	    SKIPWORD(ptr);

	    /* Read { */
	    if (*ptr != '{')
	    {
		if (!FGets (fh, line, sizeof (line)))
		    break;

		lineno ++;
	    }

	    ncolors = 0;

	    while (FGets (fh, line, sizeof (line)))
	    {
		lineno ++;

		if (*line == '#' || *line == '\n')
		    continue;

		ptr = line;

		SKIPSPACE(ptr); if (!*ptr) continue;

		if (*ptr == '}')
		    break;

		colorchars[*ptr] = ncolors;

		SKIPWORD(ptr);

		if (!Strnicmp (ptr, "TRANS", 5))
		{
		    colormap[ncolors].Alpha = 0xFF;
		    colormap[ncolors].R = 0;
		    colormap[ncolors].G = 0;
		    colormap[ncolors].B = 0;
		}
		else if (!Strnicmp (ptr, "FILL", 4))
		{
		    colormap[ncolors].Alpha = 0xFF;
		    colormap[ncolors].R = 0xFF;
		    colormap[ncolors].G = 0xFF;
		    colormap[ncolors].B = 0xFF;
		}
		else if (*ptr == '0' || *ptr == '#' || isxdigit(*ptr))
		{
		    ULONG color;

		    if (*ptr == '0')
			color = strtoul (ptr, NULL, 0);
		    else
		    {
			if (*ptr == '#')
			    ptr ++;

			color = strtoul (ptr, NULL, 16);
		    }

		    colormap[ncolors].Alpha = color >> 24;
		    colormap[ncolors].R = (color >> 16) & 0xFF;
		    colormap[ncolors].G = (color >> 8) & 0xFF;
		    colormap[ncolors].B = color & 0xFF;
		}

		ncolors ++;
	    }

	    infos |= 1;
	}
	else if (!Strnicmp (ptr, "HEIGHT", 6))
	{
	    SKIPWORD(ptr);

	    height = atoi (ptr);
	    infos |= 2;
	}
	else if (!Strnicmp (ptr, "WIDTH", 5))
	{
	    SKIPWORD(ptr);

	    width = atoi (ptr);
	    infos |= 4;
	}
	else if (!Strnicmp (ptr, "BASELINE", 8))
	{
	    SKIPWORD(ptr);

	    baseline = atoi (ptr);
	    infos |= 8;
	}
	else if (!Strnicmp (ptr, "FLAGS", 8))
	{
	    SKIPWORD(ptr);

	    flags = 0;

	    while (*ptr)
	    {
		if (!Strnicmp (ptr, "FIXED", 5))
		    flags |= GFF_FIXED;

		while (*ptr && *ptr != ',') ptr ++;
		SKIPSPACE(ptr);
	    }
	}
	else if (!Strnicmp (ptr, "CHAR", 4))
	{
	    int x, y;
	    UBYTE * c;

	    if (infos != 31)
	    {
		fprintf (stderr, "ReadChar: First char before header was complete in line %d\n", lineno);
		goto Error;
	    }

	    SKIPWORD(ptr);

	    if (isdigit(*ptr))
		t = strtol (ptr, NULL, 0);
	    else if (*ptr == '\'')
		t = ptr[1];
	    else
	    {
		fprintf (stderr, "ReadChar: Missing parameter for CHAR in line %d\n", lineno);
		goto Error;
	    }

	    c = font->CharOffsets[t];

	    SKIPWORD(ptr);

	    /* Read { */
	    if (*ptr != '{')
	    {
		if (!FGets (fh, line, sizeof (line)))
		    break;

		lineno ++;
	    }

	    y = 0;

	    while (FGets (fh, line, sizeof (line)))
	    {
		lineno ++;

		ptr = line;

		SKIPSPACE(ptr);

		if (*ptr == '}')
		    break;

		if (y >= height)
		    continue;

		x = 0;

		while (*ptr && *ptr != '\n')
		{
		    if (x >= width)
			break;

		    c[x+y*width] = colorchars[*ptr ++];

		    x ++;
		}

		y ++;
	    }
	}

	if (infos == 15)
	{
	    t = sizeof (struct GraffitiFont) +
		(ULONG)ncolors * sizeof (struct GraffitiColor) +
		ncolors +
		(ULONG)width * (ULONG)height * 256L;

	    if (!(font = AllocMem (t, MEMF_ANY)) )
	    {
		fprintf (stderr, "ReadChar: Out of memory\n");
		goto Error;
	    }

	    font->FontMemSize = t;
	    font->Node.ln_Succ = NULL;

	    for (t=0; t<256; t++)
		font->Width[t] = width;

	    font->Height   = height;
	    font->NColors  = ncolors;
	    font->ColorMap = (struct GraffitiColor *)(((UBYTE *)font)
			    + sizeof (struct GraffitiFont));
	    font->Colors   = ((UBYTE *)(font->ColorMap))
			    + ncolors * sizeof (struct GraffitiColor);
	    font->FontData = ((UBYTE *)(font->Colors)) + ncolors;

	    font->CharOffsets[0] = font->FontData;

	    for (t=1; t<256; t++)
		font->CharOffsets[t] = font->CharOffsets[t-1] + width*height;

	    for (t=0; t<ncolors; t++)
	    {
		font->ColorMap[t] = colormap[t];

		if (colormap[t].Alpha == 0xFF)
		{
		    if (colormap[t].R == 0xFF)
			font->FillColor = t;
		    if (colormap[t].R == 0x00)
			font->BackColor = t;
		}
		else if (colormap[t].Alpha == 0x00)
		{
		    font->Colors[t] = Graffiti_FindBestMatch (GH(gd),
			colormap[t].R,
			colormap[t].G,
			colormap[t].B
		    );
		}
	    }

	    infos |= 16;
	}
    }

    font->BaseLine = baseline;
    font->Flags    = flags;

    font->Node.ln_Name = fontname;

    AddHead (&gd->FontList, &font->Node);

    Close (fh);

    return font;

Error:
    if (font)
	FreeFont (gd, font);

    if (fh)
	Close (fh);

    return NULL;
} /* ReadFont */

void FreeFont (struct GraffitiData * gd, struct GraffitiFont * font)
{
    if (font->Node.ln_Succ)
	Remove (&font->Node);

    FreeMem (font, font->FontMemSize);
} /* FreeFont */

void Graffiti_DrawText_x (struct GraffitiData * gd, WORD x, WORD y,
    STRPTR text, UWORD len)
{
    int xpos, ypos;
    int w,h,width;
    UBYTE * c, pcol;
    UWORD a,r,g,b;
    UBYTE fg = gd->gh.FG;

    if ((UWORD)y >= gd->Height)
	return;

    while (len)
    {
	if (x < gd->Width)
	{
	    ypos = y;
	    c = gd->CurrentFont->CharOffsets[*text];
	    width = gd->CurrentFont->Width[*text];

/* printf ("Width=%d\n", width);
printf ("FillColor=%d\n", gd->CurrentFont->FillColor);
printf ("BackColor=%d\n", gd->CurrentFont->BackColor); */

	    for (h=0; h<gd->CurrentFont->Height; h++)
	    {
		xpos = x;

		for (w=0; w<width; w++)
		{
		    if (*c == gd->CurrentFont->FillColor)
		    {
/* putchar('*'); */
			Graffiti_SetPixel (GH(gd), xpos, ypos);
		    }
		    else if (*c == gd->CurrentFont->BackColor)
		    {
/* putchar(' '); */
			/* nop */
		    }
		    else if (!gd->CurrentFont->ColorMap[*c].Alpha)
		    {
/* putchar('c'); */
			Graffiti_SetPixelColor (GH(gd), xpos, ypos, gd->CurrentFont->Colors[*c]);
		    }
		    else if (gd->CurrentFont->ColorMap[*c].Alpha != 0xFF)
		    {
/* putchar('a'); */
			a = gd->CurrentFont->ColorMap[*c].Alpha;
			pcol = Graffiti_GetPixel (GH(gd), xpos, ypos);

/* printf ("In: a=%02x, Pixel=%02x%02x%02x Color=%02x%02x%02x ",
    a,
    gd->gh.ColorMap[pcol].r,
    gd->gh.ColorMap[pcol].g,
    gd->gh.ColorMap[pcol].b,
    gd->CurrentFont->ColorMap[*c].R,
    gd->CurrentFont->ColorMap[*c].G,
    gd->CurrentFont->ColorMap[*c].B
); */
			r = ((gd->gh.ColorMap[pcol].R * a) >> 8) +
			    ((gd->CurrentFont->ColorMap[*c].R * (0xFF-a)) >> 8);
			g = ((gd->gh.ColorMap[pcol].G * a) >> 8) +
			    ((gd->CurrentFont->ColorMap[*c].G * (0xFF-a)) >> 8);
			b = ((gd->gh.ColorMap[pcol].B * a) >> 8) +
			    ((gd->CurrentFont->ColorMap[*c].B * (0xFF-a)) >> 8);

			pcol = Graffiti_FindBestMatch (GH(gd), r, g, b);

/* printf ("  Out: Color=%02x%02x%02x Pixel=%02x\n", r, g, b, pcol); */

			Graffiti_SetPixelColor (GH(gd), xpos, ypos, pcol);
		    }

		    c ++;
		    xpos ++;
		}

		ypos ++;
/* putchar ('\n'); */
	    }
	}

	x += width;

	text ++;
	len --;
    }

    Graffiti_SetFG (GH(gd), fg);
} /* Graffiti_DrawText_x */

