/*---------------------------------------------------------------------------------
 libBAG  Copyright (C) 2010 - 2012		
  BassAceGold - <BassAceGold@gmail.com>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any
  damages arising from the use of this software.

  Permission is granted to anyone to use this software for any
  purpose, including commercial applications, and to alter it and
  redistribute it freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you
     must not claim that you wrote the original software. If you use
     this software in a product, an acknowledgment in the product
     documentation would be appreciated but is not required.
	 
  2. Altered source versions must be plainly marked as such, and
     must not be misrepresented as being the original software.
	 
  3. Restricting the usage of or portions of code in this library
     from other software developers and or content creators
     is strictly prohibited in applications with software creation capabilities
     and or with the intent to profit from said restrictions.
	 
  4. This notice may not be removed or altered from any source
     distribution.
---------------------------------------------------------------------------------*/

#include "FTTextSys.h"
#include "filesystem.h"
#include "../text.h"

FTFnt_t *BAG_Freetype_FTStruct(FNTObj_t *font){
	return font->freetype_font;
}

char BAG_Freetype_isUsed(FNTObj_t *font){
	if((*BAG_Font_GetType(font)) == TYPE_FREETYPE && BAG_Freetype_FTStruct(font) != NULL)
		return ERR_NONE;
	return ERR_END;
}


/*============================================================================
Accessing data in the freetype struct
============================================================================*/
static FT_Error *BAG_Freetype_GetErr(FNTObj_t *font){
	return (FT_Error *)&BAG_Freetype_FTStruct(font)->err;
}

static FT_Library *BAG_Freetype_GetLibrary(FNTObj_t *font){
	if(!BAG_Freetype_isUsed(font))
		return NULL;
		
	return (FT_Library *)&BAG_Freetype_FTStruct(font)->ft_lib;
}

static FT_Face *BAG_Freetype_GetFace(FNTObj_t *font){
	if(!BAG_Freetype_isUsed(font))
		return NULL;
		
	return (FT_Face *)&BAG_Freetype_FTStruct(font)->ft_face;
}

static u32 *BAG_Freetype_GetFontFileSize(FNTObj_t *font){
	return (u32*)&BAG_Freetype_FTStruct(font)->size;
}

void BAG_Freetype_SetFontFileSize(FNTObj_t *font, u32 size){
	BAG_Freetype_FTStruct(font)->size = size;
}

u32 *BAG_Freetype_GetFontDrawSize(FNTObj_t *font){
	return (u32*)&BAG_Freetype_FTStruct(font)->fontSize;
}

void BAG_Freetype_SetFontDrawSize(FNTObj_t *font, int drawSize){
	u32 *temp = BAG_Freetype_GetFontDrawSize(font);
	(*temp) = drawSize;
	FT_Set_Char_Size((*BAG_Freetype_GetFace(font)), 0, (*temp)<<6, 72, 72);
}


static u8 *BAG_Freetype_GetFontBuffer(FNTObj_t *font){
	if(!BAG_Freetype_isUsed(font))
		return NULL;
		
	return (u8*)&BAG_Freetype_FTStruct(font)->buffer;
}

void BAG_FreeType_FreeFontBuffer(FNTObj_t *font){
	if(BAG_Freetype_GetFontBuffer(font) != NULL)
		free(BAG_Freetype_GetFontBuffer(font));	

	BAG_Freetype_FTStruct(font)->buffer = NULL;
}

bool *BAG_Freetype_GetInitStatus(FNTObj_t *font){
	if(!BAG_Freetype_isUsed(font))
		return NULL;
		
	return (bool*)&BAG_Freetype_FTStruct(font)->init;
}

static void BAG_Freetype_SetInitStatus(FNTObj_t *font, bool state){
	bool *temp = BAG_Freetype_GetInitStatus(font);
	if(temp!=NULL)
		(*temp) = state;
}

static u8 *BAG_Freetype_ReadFont(const char *filename, FNTObj_t *font){
	BAG_Freetype_FTStruct(font)->buffer = (u8*)BAG_Filesystem_LoadFile(filename);
	return BAG_Freetype_GetFontBuffer(font);
}
/*============================================================================
Font loading routines
============================================================================*/

void BAG_Freetype_UnloadFont(FNTObj_t *font){
	if(!BAG_Freetype_isUsed(font))
		return;
	//free the freetype resources
	FT_Done_Face(*BAG_Freetype_GetFace(font));
	(*BAG_Freetype_GetFace(font)) = NULL;
	FT_Done_FreeType(*BAG_Freetype_GetLibrary(font));
	(*BAG_Freetype_GetLibrary(font)) = NULL;
	
	//clear buffer
	BAG_FreeType_FreeFontBuffer(font);
	BAG_Freetype_SetFontFileSize(font, 0);
	BAG_Freetype_SetInitStatus(font, 0);
	
	free(BAG_Freetype_FTStruct(font));
	font->freetype_font = NULL;
}


int BAG_Freetype_LoadFont(const char *filename, FNTObj_t *font){
	u32 size = BAG_Filesystem_GetFileSize(filename);
	if(size == 0){
		BAG_DBG_LibMsg(OPEN_FAIL_MSG, filename);
		return ERR_END;
	}
	
	BAG_Freetype_SetFontFileSize(font, size);
	if(BAG_Freetype_ReadFont(filename, font) == NULL){
		BAG_DBG_LibMsg(OPEN_FAIL_MSG, filename);
		return ERR_END;
	}
	
	(*BAG_Freetype_GetInitStatus(font)) = FT_Init_FreeType(BAG_Freetype_GetLibrary(font));
	if((*BAG_Freetype_GetInitStatus(font)) != 0){
		BAG_DBG_LibMsg("BAG:Error initiating freetype font\n");
		return (*BAG_Freetype_GetInitStatus(font));
	}

	return FT_New_Memory_Face(font->freetype_font->ft_lib, font->freetype_font->buffer, font->freetype_font->size, 0, &font->freetype_font->ft_face);
}

#ifndef _BAG_DISPLAY_EFFECTS_
static inline u16 BAG_Effects_CalcAlpha(register u32 Color1, register u32 Color2, register u32 Alpha){ // transp in .8 fixed point
	if(Alpha<255){
		register u32 newAlpha = 255 - Alpha;
		return RGB15(  (((Color1&31)*Alpha) + (((Color2&31)*newAlpha)))>>8,//red values
						((((Color1>>5)&31)*Alpha) + (((Color2>>5)&31)*newAlpha))>>8, //green values
						((((Color1>>10)&31)*Alpha) + (((Color2>>10)&31)*newAlpha))>>8 //blue values
					);
	}
	return (u16)Color1;
}
#endif

//standard text drawing
static void BAG_Freetype_DrawFTBitmap(u16 *Dest, FT_Bitmap *bitmap, int destWd, int destHt, s16 x, s16 y, u16 color){
    unsigned char *src = bitmap->buffer;
    u16 *dest = Dest + x + destWd * y;
    s16 width = bitmap->width / 3,
		dest_gap = destWd - width,
		src_gap = bitmap->pitch - bitmap->width;
    s16 x1 = 0,y1 = bitmap->rows;
	do{
		if((y + y1) >= 0 && (y + y1) <= destHt){
			do{
				s16 alpha = *(src++);
				src+=2;
				if((x + x1) > 0 && (x + x1) < destWd){
					if(alpha)
						*dest = BAG_Effects_CalcAlpha(color, (*dest), alpha);
				}
				dest++;
			}while(++x1 < width);
		}
		x1 = 0;
		src += src_gap;
		dest += dest_gap;
	}while(--y1 > 0);
}

//rotated character right
static void BAG_Freetype_DrawFTBitmap_right(u16 *Dest, FT_Bitmap *bitmap, int destWd, int destHt, s16 x, s16 y, u16 color){
    unsigned char *src = bitmap->buffer;
    u16 *dest = Dest + x + destWd * y;
    s16 width = bitmap->width / 3,
		src_gap = bitmap->pitch - bitmap->width;
    s16 x1 = 0,y1 = bitmap->rows;
	do{
		if((y + y1) > 0 && (y + y1) < destHt){
			do{
				s16 alpha = (*src++);
				src+=2;
				if((x + x1) >= 0 && (x + x1) <= destWd){
					if(alpha){
						int pos = y1 + (x1 * destWd);
						dest[pos] = BAG_Effects_CalcAlpha(color, dest[pos], alpha);
					}
				}
			}while(++x1 < width);
		}
		x1 = 0;
		src += src_gap;
	}while(--y1 > 0);
}


void BAG_Freetype_SetCharMode(struct FNTChar_s *character, char rotMode){
	character->rot = rotMode;
	switch(rotMode){
		default:
			character->blitChar = (void*)&BAG_Freetype_DrawFTBitmap;
		break;
		case TEXTROT_RIGHT:
			character->blitChar = (void*)&BAG_Freetype_DrawFTBitmap_right;
		break;	
	}
}


void BAG_Freetype_DrawFTChar(u16 *dest, int destWd, int destHt, struct FNTChar_s *character){
	FT_Face face = character->freetype_font->ft_face;
	
	if(character->character == '\n'){//newline, drop one line down
		int ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, face->size->metrics.y_scale));
		switch(character->rot){
			default: character->drawy += ascent; break;
			case TEXTROT_RIGHT: character->drawx -= ascent; break;
		}
	}
	
	else if(character->character != ' '){//standard character
		switch(character->utf){
			default:
				FT_Load_Char(face, character->character, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD);
			break;
			case 16:
				FT_Load_Char(face, (u32)character->character_uni16, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD);
			case 32:
				FT_Load_Char(face, (u32)character->character_uni32, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD);
			break;
		}
		if(character->blitChar != NULL)
			character->blitChar(dest, &face->glyph->bitmap, destWd, destHt, character->drawx /*+ face->glyph->bitmap_left*/, character->drawy /*- face->glyph->bitmap_top*/ , character->color);	
	}
	
	if(character->character != '\n'){//advance a space if character is not a new line or if the space symbol is entered
		int xinc = FT_CEIL(face->glyph->metrics.horiAdvance);
		switch(character->rot){
			default: character->drawx += xinc; break;
			case TEXTROT_RIGHT: character->drawy += xinc; break;
		}
	}
}

int BAG_Freetype_FontInit(FNTObj_t *font){
	if(BAG_Freetype_FTStruct(font) == NULL){
		font->freetype_font = calloc( 1, sizeof(FTFnt_t));
		if(BAG_Freetype_FTStruct(font) == NULL){
			BAG_DBG_LibMsg(BUFFER_FAILED_MSG, sizeof(FTFnt_t), "Freetype font");
			return ERR_END;
		}
	}

	font->loadFont = (void*)&BAG_Freetype_LoadFont;
	font->unloadFont = (void*)&BAG_Freetype_UnloadFont;
	font->drawChar = (void*)&BAG_Freetype_DrawFTChar;
	font->setRot = (void*)&BAG_Freetype_SetCharMode;
	return ERR_NONE;
}
