/*
	This file is part of Floculate.
	Copyright (C) 2008  Bill Whitacre

	Floculate 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.

	Floculate 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, see <http://www.gnu.org/licenses/>.
*/

#if defined(_MSC_VER)||defined(__WIN32__)
#	include <windows.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#if defined(__APPLE__)
#	include <OpenGL/gl.h>
#else
#	include <GL/gl.h>
#endif

#include "font.h"

static FT_Library ftlib;

int rat_start_font_system()
{
	return !(FT_Init_FreeType(&ftlib));
}

void rat_stop_font_system()
{
	FT_Done_FreeType(ftlib);
}

rat_glyph_font *rat_glyph_font_load(char *filename,int pt)
{
	rat_glyph_font *font=(rat_glyph_font *)malloc(sizeof(rat_glyph_font));

	printf("Loading font from file \"%s\" at ptsize %i...",filename,pt);

	// load the font from the file
	if (FT_New_Face(ftlib,filename,0,&(font->face)))
	{
		printf("failed load!\n");
		free((void *)font);
		return NULL;
	}

	// freetype measures fonts in 64ths of pixels, which
	// I will never understand.  6 left bit shift multiplies
	// the pt size by 64.
	FT_Set_Char_Size(font->face,pt<<6,pt<<6,96,96);
	font->pt=pt;

	printf("done.\n");
	return font;
}

void rat_glyph_font_destroy(rat_glyph_font *font)
{
	printf("Destroying glyph font...");
	FT_Done_Face(font->face);
	free((void *)font);
	printf("done.\n");
}

static unsigned int _pow2(unsigned int i)
{
	register unsigned int p2;
	for (p2=1; p2<i; p2<<=1);
	return p2;
}

static int make_glyph_texture(rat_glyph_font *gf,rat_texture_font *tf,unsigned char ch)
{
	register unsigned int i,j;
	FT_Face face=gf->face;
	FT_BitmapGlyph bitmap_glyph;
	FT_Glyph glyph;
	FT_Bitmap bitmap;
	unsigned int *textures=tf->textures;
	unsigned int width,height;
	float texx,texy;
	GLubyte* expanded_data;

	FT_Load_Glyph(face,FT_Get_Char_Index(face,ch),FT_LOAD_DEFAULT);
	FT_Get_Glyph(face->glyph,&glyph);

	FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
    bitmap_glyph=(FT_BitmapGlyph)glyph;
	bitmap=bitmap_glyph->bitmap;

	width=_pow2(bitmap.width);
	height=_pow2(bitmap.rows);

	expanded_data=(GLubyte *)malloc(sizeof(GLubyte)*2*width*height);

	for (j=0; j<height;j++)
	{
		for (i=0; i<width; i++)
		{
			expanded_data[2*(i+j*width)]=
			expanded_data[2*(i+j*width)+1]=
				(i>=bitmap.width||j>=bitmap.rows)?
				0:bitmap.buffer[i+bitmap.width*j];
		}
	}

    glBindTexture(GL_TEXTURE_2D,textures[ch]);

	glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D,0,GL_ALPHA,width,height,
		0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,expanded_data);

    free((void *)expanded_data);

	tf->wids[ch]=(float)(face->glyph->advance.x>>6);
	tf->hoss[ch]=(float)((face->glyph->metrics.horiBearingY-face->glyph->metrics.height)>>6);
	tf->bears[ch]=(float)(face->glyph->metrics.horiBearingX>>6);

	tf->qvws[ch]=bitmap.width;
	tf->qvhs[ch]=bitmap.rows;

	tf->qtws[ch]=(float)bitmap.width/(float)width;
	tf->qths[ch]=(float)bitmap.rows/(float)height;

	return 1;
}

rat_texture_font *rat_texture_font_from_glyph_font(rat_glyph_font *font)
{
	register unsigned char i;
	rat_texture_font *tf=(rat_texture_font *)malloc(sizeof(rat_texture_font));

	tf->pt=font->pt;

	// prepare the OpenGL textures / display lists
	tf->wids=(float *)malloc(sizeof(float)*255);
	tf->bears=(float *)malloc(sizeof(float)*255);
	tf->hoss=(float *)malloc(sizeof(float)*255);
	tf->qvws=(int *)malloc(sizeof(int)*255);
	tf->qvhs=(int *)malloc(sizeof(int)*255);
	tf->qtws=(float *)malloc(sizeof(float)*255);
	tf->qths=(float *)malloc(sizeof(float)*255);
	tf->textures=(unsigned int *)malloc(sizeof(unsigned int)*255);
	glGenTextures(255,tf->textures);

	for (i=0;i<255;i++)
	{
		if (!make_glyph_texture(font,tf,i))
		{
			glDeleteTextures(255,tf->textures);
			free((void *)tf->textures);
			free((void *)tf->wids);
			free((void *)tf->bears);
			free((void *)tf->hoss);
			free((void *)tf->qvws);
			free((void *)tf->qvhs);
			free((void *)tf->qtws);
			free((void *)tf->qths);
			free((void *)tf);
			return NULL;
		}
	}

	return tf;
}

void rat_texture_font_destroy(rat_texture_font *font)
{
	glDeleteTextures(255,font->textures);
	free((void *)font->wids);
	free((void *)font->textures);
	free((void *)font->qvws);
	free((void *)font->qvhs);
	free((void *)font->qtws);
	free((void *)font->qths);
	free((void *)font);
}

static float _textrgba[4]={1.0f,1.0f,1.0f,1.0f};

void rat_set_text_color(float *rgba)
{
	memcpy(_textrgba,rgba,4*sizeof(float));
}

void rat_get_text_color(float *rgba)
{
	memcpy(rgba,_textrgba,4*sizeof(float));
}

float rat_texture_font_height(rat_texture_font *font)
{
	return font->pt;
}

float rat_texture_font_text_length(rat_texture_font *font,char *text)
{
	register float len=0;
	char *ch=text;
	for (; *ch; ch++) len+=font->wids[*ch];
	return len;
}

float rat_texture_font_glyph_length(rat_texture_font *font,char ch)
{
	return font->wids[ch];
}

void rat_texture_font_render_text(rat_texture_font *font,float x,float y,char *text)
{
	char *ch;

	glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		glMatrixMode(GL_MODELVIEW);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glColor4fv(_textrgba);

		glPushMatrix();
			glScalef(1,-1,1);
			for (ch=text; *ch; ch++)
			{
				glPushMatrix();
					glTranslatef(x+font->bears[*ch],-y-(font->pt-font->hoss[*ch]),0);
					glBindTexture(GL_TEXTURE_2D,font->textures[*ch]);

					glBegin(GL_QUADS);
						glTexCoord2f(0,0);
						glVertex2f(0,font->qvhs[*ch]);

						glTexCoord2f(0,font->qths[*ch]);
						glVertex2f(0,0);

						glTexCoord2f(font->qtws[*ch],font->qths[*ch]);
						glVertex2f(font->qvws[*ch],0);

						glTexCoord2f(font->qtws[*ch],0);
						glVertex2f(font->qvws[*ch],font->qvhs[*ch]);
					glEnd();
				glPopMatrix();
				glTranslatef(font->wids[*ch],0,0);
			}
		glPopMatrix();
	glPopAttrib();
}

// upper left corner is always zero
void rat_texture_font_render_text_notform(rat_texture_font *font,char *text)
{
	char *ch;

	glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		glMatrixMode(GL_MODELVIEW);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glColor4fv(_textrgba);

		glPushMatrix();
			for (ch=text; *ch; ch++)
			{
				glBindTexture(GL_TEXTURE_2D,font->textures[*ch]);

				glBegin(GL_QUADS);
					glTexCoord2f(0,0);
					glVertex2f(0,font->qvhs[*ch]);

					glTexCoord2f(0,font->qths[*ch]);
					glVertex2f(0,0);

					glTexCoord2f(font->qtws[*ch],font->qths[*ch]);
					glVertex2f(font->qvws[*ch],0);

					glTexCoord2f(font->qtws[*ch],0);
					glVertex2f(font->qvws[*ch],font->qvhs[*ch]);
				glEnd();
				glTranslatef(font->wids[*ch],0,0);
			}
		glPopMatrix();
	glPopAttrib();
}

void rat_texture_font_render_glyph_at(rat_texture_font *font,float x,float y,char ch)
{
	glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		glMatrixMode(GL_MODELVIEW);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glColor4fv(_textrgba);

		glPushMatrix();
			glBindTexture(GL_TEXTURE_2D,font->textures[ch]);

			glScalef(1,-1,1);
			glTranslatef(x+font->bears[ch],-y-(font->pt-font->hoss[ch]),0);

			glBegin(GL_QUADS);
				glTexCoord2f(0,0);
				glVertex2f(0,font->qvhs[ch]);

				glTexCoord2f(0,font->qths[ch]);
				glVertex2f(0,0);

				glTexCoord2f(font->qtws[ch],font->qths[ch]);
				glVertex2f(font->qvws[ch],0);

				glTexCoord2f(font->qtws[ch],0);
				glVertex2f(font->qvws[ch],font->qvhs[ch]);
			glEnd();
		glPopMatrix();
	glPopAttrib();
}

void rat_texture_font_render_glyph_centered(rat_texture_font *font,float x,float y,float ang,float sclx,float scly,char ch)
{
	glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		glMatrixMode(GL_MODELVIEW);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glColor4fv(_textrgba);

		glPushMatrix();
			glBindTexture(GL_TEXTURE_2D,font->textures[ch]);
			glScalef(1,-1,1);
			glTranslatef(x,-y-(font->pt-font->hoss[ch]),0);
			glScalef(sclx,scly,1);
			glRotatef(ang,0,0,1);
			glBegin(GL_QUADS);
				glTexCoord2f(0,0);
				glVertex2f(-font->qvws[ch]/2.0f,font->qvhs[ch]/2.0f);

				glTexCoord2f(0,font->qths[ch]);
				glVertex2f(-font->qvws[ch]/2.0f,-font->qvhs[ch]/2.0f);

				glTexCoord2f(font->qtws[ch],font->qths[ch]);
				glVertex2f(font->qvws[ch]/2.0f,-font->qvhs[ch]/2.0f);

				glTexCoord2f(font->qtws[ch],0);
				glVertex2f(font->qvws[ch]/2.0f,font->qvhs[ch]/2.0f);
			glEnd();
		glPopMatrix();
	glPopAttrib();
}

