/*
 *  Fenix - Videogame compiler/interpreter
 *  Current release       : FENIX - PROJECT 1.0 - R 0.84
 *  Last stable release   :
 *  Project documentation : http://fenix.divsite.net
 *
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  Copyright © 1999 José Luis Cebrián Pagüe
 *  Copyright © 2002 Fenix Team
 *
 */

/*
 * FILE        : g_texts.c
 * DESCRIPTION : Text manipulation routines
 *
 * HISTORY:	0.82 - New extended FONT format. Font functions moved to g_font.
 *          0.74 - gr_text_new_var modified to support data types
 *			0.74 - gr_text_put_all modified to suppor new data types
 *			0.72 - Corrected gr_text_bitmap (WRITE_IN_MAP)
 */

#ifdef TARGET_BEOS
#include <posix/assert.h>
#else
#include <assert.h>
#endif

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

#include "fxi.h"

#define MAX_TEXTS 512

int fntcolor8 = -1 ;
Uint16 fntcolor16 = 0xFFFF ;

typedef struct _text
{
    int id ;
	int on ;		/* 1 - Texto ; 2 - VarSTR; 3 - VarINT; 4 - VarFLOAT; 5 - VarWORD; 6 - VarBYTE */
	int fontid ;
	int x ;
	int y ;
	int z ;
	int alignment ;
	int color8 ;
	int color16 ;
	int objectid ;
	int last_value ;
	char * text ;           /* Memoria dinámica */
	const void * var  ;		/* CHANGED TO VOID to allow diff. data types */
} TEXT;

TEXT texts[MAX_TEXTS] ;

int text_nextid = 1 ;
int text_count  = 0 ;

#ifndef WIN32
char *strrev(char *str)
{
      char *p1, *p2;

      if (! str || ! *str)
            return str;
      for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
      {
            *p1 ^= *p2;
            *p2 ^= *p1;
            *p1 ^= *p2;
      }
      return str;
}
#endif

/*
 *  FUNCTION : get_text
 *
 *  Returns the character string of a given text
 *  (may be the representation of a integer or float value)
 *
 *  PARAMS :
 *		text			Pointer to the text object
 *
 *  RETURN VALUE :
 *      String contained within the text
 *
 */

static const char * get_text (TEXT * text)
{
	static char buffer[128];
	char * aux;
	int w;

	switch (text->on)
	{
		case TEXT_TEXT:
			return text->text;
		case TEXT_STRING:
			return string_get(*(int*)text->var) ;
		case TEXT_INT:
			sprintf (buffer, "%d", *(int *)text->var) ;
			return buffer ;
		case TEXT_DWORD:
			sprintf (buffer, "%u", *(int *)text->var) ;
			return buffer ;
		case TEXT_FLOAT:
			sprintf (buffer, "%f", *(float *)text->var) ;
			/* REMOVING TRAILING 0s */
			aux = strrev(buffer) ;
			w=0 ;
			while (*aux=='0') w++, aux++;
			if (*aux=='.') aux-- ;
			strcpy(buffer,aux) ;
			strrev(buffer) ;
			return buffer ;
		case TEXT_BYTE:
			sprintf (buffer, "%d", *(Uint8 *)text->var) ;
			return buffer ;
		case TEXT_SBYTE:
			sprintf (buffer, "%d", *(Sint8 *)text->var) ;
			return buffer ;
		case TEXT_CHAR:
			sprintf (buffer, "%c", *(Uint8 *)text->var) ;
			return buffer ;
		case TEXT_WORD:
			sprintf (buffer, "%d", *(Uint16 *)text->var) ;
			return buffer ;
		case TEXT_SHORT:
			sprintf (buffer, "%d", *(Sint16 *)text->var) ;
			return buffer ;
		case TEXT_CHARARRAY:
			return (const char *)(text->var);
	}

	return NULL;
}

/*
 *  FUNCTION : info_text
 *
 *  Returns information about a text object
 *
 *  PARAMS :
 *		text			Pointer to the text object
 *		bbox			Region to update with the text bounding box
 *
 *  RETURN VALUE :
 *      1 if the text has changed since last frame
 *
 */

static int info_text (TEXT * text, REGION * bbox)
{
	const char * str = get_text(text);
	int x, y, width, height;
	REGION prev = *bbox;
	FONT * font;

    // Splinter
    if (!str) return 1;

	font = gr_font_get (text->fontid);
	if (font == NULL) {
		return 1;
	}

	/* Calculate the text dimensions */

	x = text->x;
	y = text->y;
	width = gr_text_width (text->fontid, str);
	height = gr_text_height (text->fontid, str);

	/* Update the font's maxheight (if needed) */

	if (font->maxheight == 0)
	{
		int c;

		for (c = 0 ; c < 256 ; c++)
		{
			if (font->glyph[c].bitmap == NULL)
				continue;
			if (font->maxheight < (int)font->glyph[c].bitmap->height + font->glyph[c].yoffset)
				font->maxheight = (int)font->glyph[c].bitmap->height + font->glyph[c].yoffset;
		}
	}

	/* Adjust top-left coordinates for text alignment */

	switch (text->alignment)
	{
		case 1:
		case 4:
		case 7:
			x -= width/2;
			break;
		case 2:
		case 5:
		case 8:
			x -= width-1;
			break;
	}

	switch (text->alignment)
	{
		case 3:
		case 4:
		case 5:
			y -= font->maxheight/2;
			break;
		case 6:
		case 7:
		case 8:
			y -= font->maxheight-1;
			break;
	}

	/* Fill the bounding box */

	bbox->x = x;
	bbox->y = y;
	bbox->x2 = x+width-1;
	bbox->y2 = y+height-1;

	/* Check if the var has changed since last call */

	if (bbox->x  != prev.x  || bbox->y  != prev.y ||
		bbox->x2 != prev.x2 || bbox->y2 != prev.y2)
		return 1;

	switch (text->on)
	{
		case TEXT_TEXT:
			return 0;
		case TEXT_STRING:
		case TEXT_FLOAT:
		case TEXT_INT:
		case TEXT_DWORD:
			if (text->last_value == *(int *)text->var)
				return 0;
			text->last_value = *(int *)text->var;
			return 1;
		case TEXT_BYTE:
		case TEXT_SBYTE:
		case TEXT_CHAR:
			if (text->last_value == *(Uint8 *)text->var)
				return 0;
			text->last_value = *(Uint8 *)text->var;
			return 1;
		case TEXT_WORD:
		case TEXT_SHORT:
			if (text->last_value == *(Uint16 *)text->var)
				return 0;
			text->last_value = *(Uint16 *)text->var;
			return 1;
		case TEXT_CHARARRAY:
			return 1;
	}
	return 0;
}

/*
 *  FUNCTION : draw_text
 *
 *  Draws a text object
 *
 *  PARAMS :
 *		text			Pointer to the text object
 *		clip			Clipping region
 *
 *  RETURN VALUE :
 *      None
 *
 */

void draw_text (TEXT * text, REGION * clip)
{
	const char * str = get_text(text);
	int save8, save16;
	int x, y, width, height;
	FONT * font;
	int ret;

    // Splinter
    if (!str) return;

	font = gr_font_get (text->fontid);
	if (font == NULL) {
	    gr_text_destroy(text->id);
	    return;
	}

	/* Calculate the text dimensions */

	x = text->x;
	y = text->y;
	width = gr_text_width (text->fontid, str);
	height = gr_text_height (text->fontid, str);

	/* Update the font's maxheight (if needed) */
	if (font->maxheight == 0)
	{
		int c;

		for (c = 0 ; c < 256 ; c++)
		{
			if (font->glyph[c].bitmap == NULL)
				continue;
			if (font->maxheight < (int)font->glyph[c].bitmap->height + font->glyph[c].yoffset)
				font->maxheight = (int)font->glyph[c].bitmap->height + font->glyph[c].yoffset;
		}
	}

	/* Adjust top-left coordinates for text alignment */

	switch (text->alignment)
	{
		case 1:
		case 4:
		case 7:
			x -= width/2;
			break;
		case 2:
		case 5:
		case 8:
			x -= width-1;
			break;
	}

	switch (text->alignment)
	{
		case 3:
		case 4:
		case 5:
			y -= font->maxheight/2;
			break;
		case 6:
		case 7:
		case 8:
			y -= font->maxheight-1;
			break;
	}

	/* Draw the text */

	save8 = fntcolor8;
	save16 = fntcolor16;
	fntcolor8 = text->color8;
	fntcolor16 = text->color16;

    if(!gr_text_put (0, clip, text->fontid, x, y, str))
	    gr_text_destroy(text->id);

	fntcolor8 = save8;
	fntcolor16 = save16;
}

/*
 *  FUNCTION : gr_text_new
 *
 *  Create a new text, using a fixed text string
 *
 *  PARAMS :
 *		fontid			Font number
 *		x, y			Screen coordinates
 *		alignment		Alignment
 *		text			Pointer to text
 *
 *  RETURN VALUE :
 *      None
 *
 */

int gr_text_new (int fontid, int x, int y, int alignment, const char * text)
{
	int textid = text_nextid ;

	if (text_nextid == MAX_TEXTS)
	{
		for (textid = 1 ; textid < MAX_TEXTS ; textid++)
			if (!texts[textid].on) break ;

		if (textid == MAX_TEXTS)
			gr_error ("Demasiados textos en pantalla") ;
	}
	else text_nextid++ ;
	text_count++ ;

	texts[textid].id = textid ;
	texts[textid].on = 1 ;
	texts[textid].fontid = fontid ;
	texts[textid].x = x ;
	texts[textid].y = y ;
	texts[textid].z = GLODWORD(TEXTZ) ;
	texts[textid].alignment = alignment ;
	texts[textid].text = text ? strdup(text) : 0 ;
	texts[textid].color8 = fntcolor8 ;
	texts[textid].color16 = fntcolor16 ;
	texts[textid].objectid = gr_new_object (texts[textid].z, info_text, draw_text, &texts[textid]);
	texts[textid].last_value = 0 ;
	return textid ;
}

int gr_text_new_var (int fontid, int x, int y, int alignment, const void * var, int type)
{
	int textid = gr_text_new (fontid, x, y, alignment, 0) ;

	if (textid < 0) return -1 ;
	texts[textid].on = type ;
	if (type > 1)
		texts[textid].var = var ;

	return textid ;
}

void gr_text_move (int textid, int x, int y)
{
	if (textid > 0 && textid < text_nextid)
	{
		texts[textid].x = x ;
		texts[textid].y = y ;
	}
}

void gr_text_destroy (int textid)
{
	if (textid == 0)
	{
		for (textid = 1 ; textid < text_nextid ; textid++)
		{
			if (texts[textid].on) {
				gr_destroy_object (texts[textid].objectid);
				if (texts[textid].text) free (texts[textid].text) ;
				texts[textid].on = 0 ;
			}
		}
		text_count = 0 ;
		text_nextid = 1 ;
		return ;
	}
	if (textid > 0 && textid < text_nextid)
	{
		if (!texts[textid].on) return ;

		gr_destroy_object (texts[textid].objectid);
		if (texts[textid].text) free (texts[textid].text) ;
		texts[textid].on = 0 ;
		if (textid == text_nextid-1) {
			while (text_nextid > 1 && !texts[text_nextid-1].on)
				text_nextid-- ;
		}
		text_count-- ;
	}
}

int gr_text_width (int fontid, const unsigned char * text)
{
	return gr_text_widthn (fontid, text, strlen(text));
}

int gr_text_widthn (int fontid, const unsigned char * text, int n)
{
	int l = 0 ;
	FONT * f ;

	if (!text || !*text) return 0;

	if (fontid < 0 || fontid > 255 || !fonts[fontid]) {
		gr_con_printf ("Tipo de letra incorrecto (%d)", fontid) ;
		return 0;
	}

	f = fonts[fontid] ;

	while (*text && n--)
	{
		switch (f->charset)
		{
			case CHARSET_ISO8859:
				l += f->glyph[dos_to_win[*text]].xadvance ;
				break;
			case CHARSET_CP850:
				l += f->glyph[*text].xadvance ;
				break;
		}
		text++;
	}
	return l ;
}

int gr_text_margintop (int fontid, const unsigned char * text)
{
	int minyoffset = 0x7FFFFFFF ;
	FONT * f ;

	if (!text || !*text) return 0;

	if (fontid < 0 || fontid > 255 || !fonts[fontid])
	{
		gr_con_printf ("Tipo de letra incorrecto (%d)", fontid) ;
		return 0;
	}

	f = fonts[fontid] ;

	while (*text)
	{
		switch (f->charset)
		{
			case CHARSET_ISO8859:
				if (minyoffset > f->glyph[dos_to_win[*text]].yoffset)
					minyoffset = f->glyph[dos_to_win[*text]].yoffset;
				break;
			case CHARSET_CP850:
				if (minyoffset > f->glyph[*text].yoffset)
					minyoffset = f->glyph[*text].yoffset;
				break;
		}
		text++ ;
	}
	return minyoffset ;
}

int gr_text_height (int fontid, const unsigned char * text)
{
	int l = 0;
	int margin ;
	FONT * f ;

	if (fontid < 0 || fontid > 255 || !fonts[fontid])
	{
		gr_con_printf ("Tipo de letra incorrecto (%d)", fontid) ;
		return 0;
	}

	if (!text || !*text) return 0;

	margin = gr_text_margintop (fontid, text);
	f = fonts[fontid] ;

	while (*text)
	{
		if (f->glyph[*text].bitmap)
		{
			switch (f->charset)
			{
				case CHARSET_ISO8859:
					if (l < f->glyph[dos_to_win[*text]].yoffset
						  + (int)f->glyph[dos_to_win[*text]].bitmap->height)
					{
						l = f->glyph[dos_to_win[*text]].yoffset
						  + (int)f->glyph[dos_to_win[*text]].bitmap->height ;
					}
					break;
				case CHARSET_CP850:
					if (f->glyph[*text].yoffset + (int)f->glyph[*text].bitmap->height > l)
					{
						l = f->glyph[*text].yoffset + (int)f->glyph[*text].bitmap->height ;
					}
					break;
			}
		}
		text++ ;
	}
	return l - margin ;
}

int gr_text_put (GRAPH * dest, REGION * clip, int fontid, int x, int y, const unsigned char * text)
{
	GRAPH * ch ;
	FONT   * f ;
	Uint8   current_char;
	int flags ;
	int save16, save8;

    if (fontid < 0 || fontid > 255 || !fonts[fontid]) {
        gr_con_printf ("Tipo de letra incorrecto (%d)", fontid) ;
        return 0;
    }

	if (!text || !*text) return 1;

	if (!dest) dest = scrbitmap ;

    f = fonts[fontid] ;

	/* Se permite imprimir texto antes de inicializar los globales */

	flags = globaldata ? GLODWORD(TEXT_FLAGS) : 0 ;

	save8 = syscolor8;
	save16 = syscolor16;

	if (fntcolor8 == -1)
	{
		gr_setcolor (dest->depth == 8 ? gr_find_nearest_color(255,255,255):gr_rgb(255,255,255));
	}
	else
	{
		syscolor8 = fntcolor8;
		syscolor16 = fntcolor16;
	}

	while (*text)
	{
		switch (f->charset)
		{
			case CHARSET_ISO8859:
				current_char = dos_to_win[*text];
				break;
			case CHARSET_CP850:
				current_char = *text;
				break;
			default:
				current_char = 0;
				break;
		}

		ch = f->glyph[current_char].bitmap ;
		if (ch)
		{
			gr_blit (dest, clip, x + f->glyph[current_char].xoffset,
				                 y + f->glyph[current_char].yoffset,
                                 flags, ch) ;
		}
		x += f->glyph[current_char].xadvance ;
		text++ ;
	}

	syscolor8 = save8;
	syscolor16 = save16;

	return 1;
}

GRAPH * gr_text_bitmap (int fontid, const char * text, int alignment)
{
	GRAPH * gr ;
	int x, y ;
	FONT   * f ;

	// Splinter
	if (!text || !*text) return NULL;

    if (fontid < 0 || fontid > 255 || !fonts[fontid]) {
       gr_con_printf ("Tipo de letra incorrecto (%d)", fontid) ;
       return NULL;
    }

    f = fonts[fontid] ;

	/* Un refresco de paleta en mitad de gr_text_put puede provocar efectos
	 * desagradables al modificar el tipo de letra del sistema */

	if (palette_changed) gr_refresh_palette() ;

	gr = bitmap_new_syslib (gr_text_width (fontid, text), gr_text_height (fontid, text), enable_16bits ? 16:8, 1) ;
	assert (gr) ;
	gr_clear (gr) ;
	if (!gr_text_put (gr, 0, fontid, 0, -gr_text_margintop(fontid, text), text)) {
	    bitmap_destroy(gr);
	    return NULL;
    }

	switch (alignment)
	{
		case 0:
		case 1:
		case 2:
			y = 0 ;
			break ;
		case 3:
		case 4:
		case 5:
			y = gr->height/2 ;
			break ;
		default:
			y = gr->height-1 ;
			break ;

	}

	switch (alignment)
	{
		case 0:
		case 3:
		case 6:
			x = 0 ;
			break ;
		case 1:
		case 4:
		case 7:
			x = gr->width/2 ;
			break ;
		default:
			x = gr->width-1 ;
			break ;

	}

	bitmap_add_cpoint (gr, x, y) ;
	return gr ;
}

void gr_text_setcolor (int c)
{
	int r, g, b;

	if (c == 0)
	{
		fntcolor8 = 0;
		fntcolor16 = 0;
	}
	else if (!enable_16bits)
	{
		fntcolor8 = c ;
	}
	else
	{
		gr_get_rgb (c, &r, &g, &b);
		fntcolor8 = gr_find_nearest_color (r, g, b);
		fntcolor16 = c ;
	}
}

int gr_text_getcolor()
{
	return ((enable_16bits)?fntcolor16:fntcolor8) ;
}
