#pragma once
#include "../../stdafx.h"

#include "../glLibMain.h"

namespace glLib { namespace Effect { namespace Font {

typedef map<string,TTF_Font*> font_map_type;

void glLibInternal_init_fonts(void);
void glLibInternal_deinit_fonts(void);

class font {
	friend void glLibInternal_init(void);
	friend void glLibInternal_deinit(void);
	friend void glLibInternal_init_fonts(void);
	friend void glLibInternal_deinit_fonts(void);

	public:
		string name;
		unsigned char size;
	private:
		string* glLibInternal_current_text;

		static font_map_type* glLibInternal_fonts;

		#ifdef GLLIB_ERROR_MODE_REPORT_ERRORS
		static bool glLibInternal_fonts_okay;
		static string* glLibInternal_font_error;
		#endif

		::glLib::Texture::texture_2D* glLibInternal_rendered_texture;

	public:
		font(const  char* font_name, unsigned char size) {
			glLibInternal_init(font_name,size);
			glLibInternal_load_font();
		}
		font(const string font_name, unsigned char size) {
			glLibInternal_init(font_name,size);
			glLibInternal_load_font();
		}
		~font(void) {
			glLibInternal_deinit();
		}

		                                    const ::glLib::Texture::texture_2D* render(const  char* text, const    float color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text,color_struct);
		}
		                                    const ::glLib::Texture::texture_2D* render(const  char* text, const   double color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text,color_struct);
		}
		template <typename type_int> inline const ::glLib::Texture::texture_2D* render(const  char* text, const type_int color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text,color_struct);
		}
		                                    const ::glLib::Texture::texture_2D* render(const string text, const    float color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text.c_str(),color_struct);
		}
		                                    const ::glLib::Texture::texture_2D* render(const string text, const   double color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text.c_str(),color_struct);
		}
		template <typename type_int> inline const ::glLib::Texture::texture_2D* render(const string text, const type_int color[3]) {
			if ((*glLibInternal_current_text==text)&&(glLibInternal_rendered_texture!=NULL)) return glLibInternal_rendered_texture;
			SDL_Color color_struct; glLibInternal_get_color(color,&color_struct);
			return glLibInternal_render(text.c_str(),color_struct);
		}
	private:
		void glLibInternal_get_color(const  float color[3], SDL_Color* color_struct) const {
			color_struct->r = Math::Misc::rnduchar(255.0*color[0]);
			color_struct->g = Math::Misc::rnduchar(255.0*color[1]);
			color_struct->b = Math::Misc::rnduchar(255.0*color[2]);
		}
		void glLibInternal_get_color(const double color[3], SDL_Color* color_struct) const {
			color_struct->r = Math::Misc::rnduchar(255.0*color[0]);
			color_struct->g = Math::Misc::rnduchar(255.0*color[1]);
			color_struct->b = Math::Misc::rnduchar(255.0*color[2]);
		}
		template <typename type_int> void glLibInternal_get_color(const type_int color[3], SDL_Color* color_struct) const {
			color_struct->r = color[0];
			color_struct->g = color[1];
			color_struct->b = color[2];
		}

		const ::glLib::Texture::texture_2D* glLibInternal_render(const char* text, const SDL_Color color) {
			SDL_Surface* surf;
			surf = TTF_RenderText_Blended((*glLibInternal_fonts)[name+to_string(size)],text,color);
			#ifdef GLLIB_ERROR_MODE_REPORT_ERRORS
			if (surf==NULL) throw GLLIB_ERROR_TYPE_ERROR("Could not render text ("+string(SDL_GetError())+")!");
			#endif

			if (glLibInternal_rendered_texture) delete glLibInternal_rendered_texture;
			glLibInternal_rendered_texture = new ::glLib::Texture::texture_2D(surf,GLLIB_ALL,GLLIB_RGBA,GLLIB_FILTER_BILINEAR_MIPMAP_BILINEAR,GLLIB_FILTER_BILINEAR);

			SDL_FreeSurface(surf);

			return glLibInternal_rendered_texture;
		}

		void glLibInternal_init(const string new_name, int new_size) {
			name = new_name;
			size = new_size;

			glLibInternal_rendered_texture = NULL;

			glLibInternal_current_text = new string("");
		}
		void glLibInternal_deinit(void) {
			if (glLibInternal_rendered_texture) delete glLibInternal_rendered_texture;
			delete glLibInternal_current_text;
		}

		void glLibInternal_load_font(void) {
			#ifdef GLLIB_ERROR_MODE_REPORT_ERRORS
			if (!glLibInternal_fonts_okay) {
				throw GLLIB_ERROR_TYPE_ERROR("Fonts not available ("+(*glLibInternal_font_error)+")!");
			}
			#endif

			string key_name = name + to_string(size);

			font_map_type::const_iterator iter = glLibInternal_fonts->find(key_name);
			if (iter==glLibInternal_fonts->end()) {
				TTF_Font* font = TTF_OpenFont(name.c_str(),size); //TODO: test/handle failure
				(*glLibInternal_fonts)[key_name] = font;
			}
		}
};

};};};