#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>

#include "text.h"
#include "common.h"

FT_Library TextRender::library;
int TextRender::renderCount = 0;

TextRender::TextRender()
{
	if(!renderCount)
	{
		// Initialize the library
		FT_Error error = FT_Init_FreeType(&library);
		
		if(error)
		{
			// Better handling in the future
			exit(0);
		}
	}
	
	// Keep track of instances
	renderCount++;
	
	// Init the sprite Buffer
	memset(spriteBuf, 0, sizeof(Buffer) * 256);
	
	// Always render top left
	_renderx = 0;
	_rendery = 0;
	
	// Nothing rendered yet
	_renderedwidth = -1;
	_renderedheight = -1;
}

TextRender::~TextRender()
{
	// Keep track of instances
	renderCount--;
	
	if(!renderCount)
	{
		// Free the library (and all resources)
		FT_Done_FreeType(library);
	}
	
	// Free any sprites
	for(int i = 0; i < 256; i++)
	{
		if(spriteBuf[i].data) { free(spriteBuf[i].data); }
	}
}

void TextRender::setFont(const char *font)
{
	// Get filename from face
	char tFontPath[MAXPATHLEN];
	windowManager.getThemePath(tFontPath);
	
	// Try to init from the themes directory
	char tFont[MAXPATHLEN];
	sprintf(tFont, "%sfonts/%s.ttf", tFontPath, font);
	
	// Initialize a font
	FT_Error error = FT_New_Face(library, tFont, 0, &face);
	
	if ( error )
	{
		// Try initializing from default path
		sprintf(tFont, "%sfonts/%s.ttf", DATAPATH, font);
		
		// Initialize a font
		FT_Error error = FT_New_Face(library, tFont, 0, &face);
		
		if ( error )
		{
			// Try initializing from default font
			sprintf(tFont, "%sfonts/dejavu.ttf", DATAPATH);
			
			// Initialize a font
			FT_Error error = FT_New_Face(library, tFont, 0, &face);
			
			if ( error )
			{
				// Better handling in the future
				exit(0);
			}
		}
	}
	
	// Free any sprites
	for(int i = 0; i < 256; i++)
	{
		if(spriteBuf[i].data) { free(spriteBuf[i].data); }
	}
	
	// Init the sprite Buffer
	memset(spriteBuf, 0, sizeof(Buffer) * 256);
}

void TextRender::setColor(Color c)
{
	_color = c;
}

void TextRender::setSize(int s)
{
	// Set up the font face (see freetype samples for magic values)
	FT_Set_Pixel_Sizes(face, 0, s);
	
	_fontheight = s;
}

void TextRender::setBuffer(uint8_t *buf, int width, int height)
{
	// Set up a Buffer to render to (RGBA quads, most likely to be blitted into a DrawableImage and rendered using libwiisprite)
	_buf = buf;
	_width = width;
	_height = height;
}

void TextRender::setRenderPosition(int x, int y)
{
	// Move default render position
	_renderx = x;
	_rendery = y;
}

int TextRender::getRenderedWidth()
{
	// Return already rendered width
	return _renderedwidth;
}

int TextRender::getRenderedHeight()
{
	// Return already rendered height
	return _renderedheight;
}

void TextRender::Blit(uint8_t *bmpBuf, int runWidth, int runHeight, int left, int top)
{
	// If the buffer isn't set, exit
	if(!_buf) { return; }
	
	// Remember original widths
	int oWidth = runWidth;
	
	// Precalculate the width
	if((left + runWidth) >= _width)
	{
		// Run height needs adjustment
		runWidth = _width - left;
	}
	
	// Precalculate the height
	if((top + runHeight) >= _height)
	{
		// Run height needs adjustment
		runHeight = _height - top;
	}
	
	// Precalculate color
	uint32_t c = _color.r << 24 | _color.g << 16 | _color.b << 8;
	
	// Copy alpha data over, setting the color to the predefined color and the alpha to the value of the glyph
	for(int y = 0; y < runHeight; y++)
	{
		// Precalculate
		int sywidth = (y * oWidth);
		int dywidth = ((y + top) * _width);
		
		for(int x = 0; x < runWidth; x++)
		{
			// Precalculate
			int srcloc = x + sywidth;
			int dstloc = ((x + left) + dywidth) << 2;
			
			// Copy in alpha
			c &= 0xFFFFFF00;
			c |= bmpBuf[srcloc];
			
			// Copy data over
			memcpy(_buf + dstloc, &c, 4);
		}
	}
}

int convertUTF(const char *input, uint32_t &pos)
{
	if(!(input[pos] & 0x80)) // single byte
	{	
		return input[pos];
	}
	else if((input[pos] & 0xE0) == 0xC0) // two bytes
	{
		int tByte = ((input[pos] & 0x1F) << 6) | (input[pos + 1] & 0x3F);
		pos++; // incriment one now, one later
		
		return tByte;
	}
	else if((input[pos] & 0xF0) == 0xE0) // three bytes
	{
		int tByte = ((input[pos] & 0x0F) << 12) | ((input[pos + 1] & 0x3F) << 6) | (input[pos + 2] & 0x3F);		
		pos += 2; // incriment two now, one later
		
		return tByte;
	}
	else if((input[pos] & 0xF8) == 0xF0) // four bytes
	{
		int tByte = ((input[pos] & 0x07) << 15) | ((input[pos + 1] & 0x3F) << 12) | ((input[pos + 2] & 0x3F) << 6) | (input[pos + 3] & 0x3F);
		pos += 3; // incriment three now, one later
		
		return tByte;
	}
	
	return '?';
}

void TextRender::renderSimple(const char *out)
{
	// Remember x position
	int x = DEFAULT_X + _renderx;
	int y = DEFAULT_Y + _rendery;
	
	// Reset rendered width
	_renderedwidth = -1;
	
	// Shortcut from examples
	FT_GlyphSlot slot = face->glyph;
	
	// Render
	uint32_t i = 0;
	
	while(i < strlen(out))
	{
		int charOut = convertUTF(out, i);
		i++; // Last increment is always here
		
		if(charOut == '\r' || charOut == '\n')
		{
			if(x > _renderedwidth)
			{
				// Ensure we remember this width
				_renderedwidth = x;
			}
			
			// Newline
			x = DEFAULT_X;
			y += _fontheight + DEFAULT_Y_CUSHION;
			
			continue;
		}
		else if(charOut == '\t')
		{
			// Truncate to floor (nearest bounds)
			x /= DEFAULT_TAB_SPACE;
			x *= DEFAULT_TAB_SPACE;
			
			// Add tab stop
			x += DEFAULT_TAB_SPACE;
			
			continue;
		}
		
		if(charOut != 0)
		{		
			// Check if it's cached
			if(charOut < 256 && spriteBuf[charOut].data)
			{
				// It is!  Blit glyph to surface
				Blit(spriteBuf[charOut].data, spriteBuf[charOut].width, spriteBuf[charOut].rows, x + spriteBuf[charOut].left, (y + _fontheight) - spriteBuf[charOut].top);
				
				// Advance
				x += spriteBuf[charOut].advance;
			}
			else
			{
				// Render glyph
				FT_Error error = FT_Load_Char(face, charOut, FT_LOAD_RENDER);
				if(error) continue;  /* ignore errors */
				
				// Blit glyph to surface
				Blit(slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, x + slot->bitmap_left, (y + _fontheight) - slot->bitmap_top);
				
				// Advance the position
				x += slot->advance.x >> 6;
				
				if(charOut < 256)
				{
					// Create Buffer
					spriteBuf[charOut].data = (uint8_t *)malloc(slot->bitmap.width * slot->bitmap.rows);
					
					// Set aux data
					spriteBuf[charOut].width = slot->bitmap.width;
					spriteBuf[charOut].rows = slot->bitmap.rows;
					spriteBuf[charOut].advance = slot->advance.x >> 6;
					spriteBuf[charOut].left = slot->bitmap_left;
					spriteBuf[charOut].top = slot->bitmap_top;
					
					// Set pixel data
					memcpy(spriteBuf[charOut].data, slot->bitmap.buffer, slot->bitmap.width * slot->bitmap.rows);
				}
			}
		}
		
		// No sound stutters
		if(!(i % 5))
		{
			// Sound Buffer update
			sound.update();
		}
	}
	
	// Return metrics
	if(x > _renderedwidth)
	{
		// Ensure we remember this width
		_renderedwidth = x;
	}
	
	// height is easier to calculate
	_renderedheight = y + _fontheight;
}

void TextRender::render(const char *fmt, ...)
{
	// Need to make room for the sprintf'd text
	char *out = (char *)memalign(32, 1024);
	
	// Build using sprintf
	va_list marker;
	va_start(marker,fmt);
	vsprintf(out,fmt,marker);
	va_end(marker);
	
	// Call rendering engine
	renderSimple(out);
	
	// Free memory
	free(out);
}
