/* GnarlPlot font handler C code V1.21 5/2/08
   Copyright 2008 Jeffrey Lee
   This file is part of GnarlPlot.
   GnarlPlot is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   GnarlPlot is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with GnarlPlot.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef _GP_FONT_C
#define _GP_FONT_C

#include <stdlib.h>

#include "font.h"
#include "col.h"

static gp_font *handles = 0; /* List of font handles */
static int fage = 0;

int gp_font_old = 5;

static const int recolour(int a,int b)
{
	int ar,ag,ab,br,bg,bb;
	ar = a & 0xFF;
	ag = (a & 0xFF00) >> 8;
	ab = (a & 0xFF0000) >> 16;
	br = b & 0xFF;
	bg = (b & 0xFF00) >> 8;
	bb = (b & 0xFF0000) >> 16;
	ar*=br+1;
	ag*=bg+1;
	ab*=bb+1;
	ar=ar >> 8;
	ag=ag >> 8;
	ab=ab >> 8;
	return ar+(ag << 8)+(ab << 16);
}

gp_font *gp_loadfont(gp_font_def *style,int width,int fgcol,int bgcol,int ps)
{
	/* Load a font */
	gp_font *h,*fh;
	gp_spr **s;
	int d;
	int fx,fy,dx,dy,ddx,ddy; /* Font x&y pos, definition x&y pos (16.16), and definition x&y step (16.16) */
	int i;
	gp_sprfmt *f;
	ddx = ddy = 0;
	/* First, check to see if the font is already loaded */
	h = handles;
	fh = 0;
	i = 0;
	while (h)
	{
		if ((h->style == style) && (h->width == width) && (h->fgcol == fgcol) && (h->bgcol == bgcol) && (h->ps == ps))
		{
			if (h->nref < 0)
				return 0; /* Fonts with <0 references represent unavailable fonts */
			h->nref++;
			return h;
		}
		else if (h->nref == 0)
		{
			i++; /* Count of free fonts */
			if ((fh == 0) || (fh->age > h->age)) /* Go for oldest font (i.e. one with youngest age) */
				fh = h;
		}
		h = h->next;
	}
	/* Else load */
	if ((fh == 0) || (i < gp_font_old))
	{
		fh = malloc(sizeof(gp_font));
		if (fh == 0)
			return 0;
		fh->next = handles;
		handles = fh;
	}
	else
	{
		/* Dispose of old font data */
		for (i=0;i<256;i++)
			if ((fh->f[i] != fh->f[32]) || (i == 32))
				(fh->f[i]->f->delete)(fh->f[i]);
	}
	fh->style = style;
	fh->width = width;
	fh->height = 0;
	fh->fgcol = fgcol;
	fh->bgcol = bgcol;
	fh->ps = ps;
	fh->nref = 1;
	fh->age = fage++; /* (Re)set font age */
	/* Find a suitable definition */
	d = 0;
	while ((style[d].flags & GP_FONT_LAST) == 0)
	{
		if (style[d].width == width)
			break; /* Found perfect match */
		if ((style[d].width > width) && (style[d].flags & GP_FONT_DOWN))
			break; /* Font can be scaled down */
		if ((style[d].width < width) && (style[d].flags & GP_FONT_UP))
			break; /* Font can be scaled up */
		d++; /* Else try next font (if any) */
	}
	/* Load the font */
	s = (style[d].load)(&(style[d]));
	if (s == 0)
	{
		fh->nref = -1; /* Dead */
		return 0;
	}
	/* Work out sprite format to use */
	if (bgcol == -1)
	{
		if (ps == 0)
			f = &gp_spr_01;
		else if (ps == 1)
			f = &gp_spr_11;
		else
			f = &gp_spr_21;
	}
	else if (ps == 0)
		f = &gp_spr_00;
	else if (ps == 1)
		f = &gp_spr_10;
	else
		f = &gp_spr_20;
	/* Calculate scale factor */
	int scale = (fh->width*65536)/style[d].width;
	int iscale = (style[d].width*65536)/fh->width;
	/* Scale and recolour */
	for (i=0;i<256;i++)
		if (s[i] != 0)
		{
			int w,h;
			w = ((s[i]->f->getw)(s[i])*scale)>>16;
			h = ((s[i]->f->geth)(s[i])*scale)>>16;
			if(w<1)
				w = 1;
			if(h<1)
				h = 1;
			if(h>fh->height)
				fh->height = h;
			/* Copy & rescale */
			fh->f[i] = (f->create)(f,w,h,0,0);
			dy = 0;
			for (fy=0;fy<h;fy++)
			{
				dx = 0;
				for (fx=0;fx<w;fx++)
				{
					d = (s[i]->f->getpix)(s[i],dx >> 16,dy >> 16,2); /* Get original colour */
					d = recolour(d,fgcol);
					(f->setpix)(fh->f[i],fx,fy,2,d); /* Write it */
					d = (s[i]->f->getmask)(s[i],dx >> 16,dy >> 16,GP_MT_ONOFF,2);
					if (bgcol == -1)
						(f->setmask)(fh->f[i],fx,fy,GP_MT_ONOFF,2,d); /* Set mask if transparent font wanted */
					else if (d == 0)
						(f->setpix)(fh->f[i],fx,fy,2,bgcol); /* Transparent pixels are translated to chosen background colour */
					dx+=iscale;
				}
				dy+=iscale;
			}
		}
	/* Now fill in the blanks and dispose of source */
	for (i=0;i<256;i++)
		if (s[i] == 0)
			fh->f[i] = fh->f[32];
		else
			(s[i]->f->delete)(s[i]);
	free(s);
	return fh;
}

void gp_freefont(gp_font *h)
{
	if (h)
		h->nref--;
}

gp_spr **gp_loadfont_paint(gp_font_def *d)
{
	gp_spr **s,**s2;
	int n,i,j;
	s = gp_spr_paint_loadfile((char *) d->id,&n);
	if (s == 0)
		return 0; /* Not found */
	s2 = malloc(sizeof(gp_spr *)*256);
	if (s2 == 0)
	{
		for (i=0;i<n;i++)
			(s[i]->f->delete)(s[i]);
		free(s);
		return 0;
	}
	for (i=0;i<256;i++)
		s2[i] = 0;
	for (i=0;i<n;i++)
	{
		j = atoi(gp_spr_paint_getname(s[i],0));
		if ((j >= 0) && (j < 256))
			s2[j] = s[i];
		else
			(s[i]->f->delete)(s[i]); /* Can't work out where it goes so kill it */
	}
	free(s);
	return s2;
}

int gp_font_puts(const gp_font *h,const char *t,int l,gp_screen *s,int x,int y)
{
	gp_spr *spr;
	if ((h == 0) || (t == 0) || (l == 0) || (s == 0))
		return x;
	while (l-->0)
	{
		spr = h->f[(int) *(t++)];
		(spr->f->plot)(spr,x,y,s);
		x+=(spr->f->getw)(spr);
	}
	return x;
}

int gp_font_puts_scaled(const gp_font *h,const char *t,int l,gp_screen *s,int x,int y,f1616 sc)
{
	gp_spr *spr;
	f1616 inv;
	int hi,w;
	if ((h == 0) || (t == 0) || (l == 0) || (s == 0))
		return x;
	inv = f1616_div(0x10000,sc);
	while (l-->0)
	{
		spr = h->f[(int) *(t++)];
		w = ((spr->f->getw)(spr)*sc) >> 16;
		for (hi=0;hi<(spr->f->geth)(spr)*sc;hi+=65536)
			(spr->f->tplot)(spr,x,y+(hi >> 16),s,w,0,(hi >> 16)*inv,inv,0);
		x+=w;
	}
	return x;
}

int gp_font_getwidth(const gp_font *h,const char *t,int l,f1616 sc)
{
	int x=0;
	gp_spr *spr;
	if((h == 0) || (t == 0) || (l == 0) || (sc == 0))
		return x;
	while (l-->0)
	{
		spr = h->f[(int) *(t++)];
		x+=((spr->f->getw)(spr)*sc) >> 16;
	}
	return x;
}

void gp_font_flush()
{
	/* Flush the font cache of all unused fonts */
	gp_font *h;
	gp_font *next;
	int i;
	h = handles;
	handles = 0; /* Rebuild the list, containing only the in-use fonts */
	while (h)
	{
		next = h->next;
		if (h->nref > 0)
		{
			h->next = handles;
			handles = h;
		}
		else
		{
			/* Kill it */
			if (h->nref == 0) /* Negative nref == no sprites loaded */
				for (i=0;i<256;i++)
					if ((h->f[i] != h->f[32]) || (i == 32))
						(h->f[i]->f->delete)(h->f[i]);
			free(h);
		}
		h = next;
	}
}

#endif
