#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <math.h>

#define SEMICOLON 59

static int utf8_encode(char *out, const uint32_t utf){
	int i = 0;
	static const uint8_t s0 = 0;
	static const uint8_t s1 = 6;
	static const uint8_t s2 = 12;
	static const uint8_t s3 = 18;
	static const uint8_t a0 = 0x07;
	static const uint8_t a1 = 0x0F;
	static const uint8_t a2 = 0x1F;
	static const uint8_t a3 = 0x3F;
	static const uint8_t o0 = 0x80;
	static const uint8_t o1 = 0xC0;
	static const uint8_t o2 = 0xE0;
	static const uint8_t o3 = 0xF0;
	if(utf < 0x0080){
		//Plain ASCII
		out[i++] = (char)utf;
		return i;
	}else if(utf < 0x0800){
		//2-byte unicode
		out[i++] = (char)(((utf >> s1) & a2) | o1);
		out[i++] = (char)(((utf >> s0) & a3) | o0);
		return i;
	}else if(utf < 0x10000){ //65,536
		//3-byte unicode
		out[i++] = (char)(((utf >> s2) & a1) | o2);
		out[i++] = (char)(((utf >> s1) & a3) | o0);
		out[i++] = (char)(((utf >> s0) & a3) | o0);
		return i;
	}else if(utf < 0x110000){ //RFC3629 §3 limit
		//4-byte unicode
		out[i++] = (char)(((utf >> s3) & a0) | o3);
		out[i++] = (char)(((utf >> s2) & a3) | o0);
		out[i++] = (char)(((utf >> s1) & a3) | o0);
		out[i++] = (char)(((utf >> s0) & a3) | o0);
		return i;
	}else{
		//utf-8 outside of current unicode code-space
		printf("invalid utf-8 range\n");
		return i;
	}
}

struct Glyph_Metrics{
	char str[6];
	int xmin;
	int xmax;
	int ymin;
	int ymax;
	int orig_x;
	int orig_y;
	int bearingx;
	int bearingy;
	int advance;
	int height;
	int width;
};

static void Set_strex_flags(gdFTStringExtraPtr strex){
	strex->flags |= gdFTEX_LINESPACE;
	strex->flags |= gdFTEX_CHARMAP;
	strex->flags |= gdFTEX_RESOLUTION;
	strex->flags |= gdFTEX_DISABLE_KERNING;
	strex->flags |= gdFTEX_XSHOW;
	strex->flags |= gdFTEX_RETURNFONTPATHNAME;
        //*flags |= gdFTEX_FONTPATHNAME;
        //*flags |= gdFTEX_FONTCONFIG;
        //*flags |= gdFTEX_FONTPATHNAME;
	strex->linespacing = 1.00;
	strex->charmap = gdFTEX_Unicode;
	//FT_ENCODING_UNICODE
	//strex->charmap = gdFTEX_Big5;
	//strex->charmap = gdFTEX_Shift_JIS;
	//strex->charmap = gdFTEX_Adobe_Custom;
	strex->hdpi=187;
	strex->vdpi=187;
}

static int Gen_Charset(const char *input_ttf, const char *hex_file, const char *image_file, const char *font_file, const int *bg, const int *fg, const int *trans, const double pixel_pt_size){
	FILE *image_out;
	FILE *font_fnt_out;
	FILE *input_hex;
	FILE *out_test;
//FILE *entity;

	static const char *space = " ";
	static const int extra_char_spacing = 1.0;
	static const int image_border = 40;
	gdImagePtr im;
	int background_color;
	int foreground_color;
	int transparency_color;
	int brect[8];
	int x = 0;
	int y = 0;
	char *err;
	int image_width = 0;
	int image_height = 0;
	static const int linestr_size = 32;
	char linestring[linestr_size];
	//char test[6];
	char *test;
	uint32_t test_idx = 0;
	uint32_t test_offset = 0;
	uint32_t hexchar = 0;
	int num1 = 0;
	int global_bb_width = 0;
	int global_bb_height = 0;
	int global_ymin = 0;
	int global_ymax = 0;
	int global_max_advance = 0;
	struct Glyph_Metrics *glyph_metrics;
	gdFTStringExtra Strex = {0,0,0,0,0,NULL,NULL};
	//gdFTStringExtraPtr strex = &Strex;
	int cur_char = 0;
	size_t totalchars = 0;
	char *xshow_ptr = Strex.xshow;
//	const char *prefix = "&#x";
//	const char *suffix = ";";

	Set_strex_flags(&Strex);
//printf("here 3\n");


//	int utf_bytes = 0;
        if( ( out_test = fopen("dec.txt", "w" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "dec.txt" );
                return 1;
        }

//        if( ( entity = fopen("entities.txt", "r" ) ) == NULL ) {
  //              fprintf( stderr, "Error opening %s\n", "entities.txt" );
    //            return 1;
      //  }




	//count how many chars we have
        if( ( input_hex = fopen(hex_file, "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", hex_file );
                return 1;
        }


	//test = (char *)malloc();


	memset ((void*) linestring, '\0', linestr_size);

        while( fgets(linestring, linestr_size, input_hex) != NULL){
		if(linestring[0] != '\n')
			totalchars++;
	}
	if(fseek(input_hex, 0, SEEK_SET) != 0)
		return 1;

	test = (char *)malloc(4*totalchars+1);
	memset ((void*)test, '\0', 4*totalchars+1);

	glyph_metrics = (struct Glyph_Metrics *)malloc(totalchars * sizeof(struct Glyph_Metrics));

	//for(num1=0; num1<totalchars; num1++){
	//}

	x=0;
	y=0;

	//populate glyph_metrics
	while( fgets(linestring, linestr_size, input_hex) != NULL){
		//make sure we skip last newline
		if(linestring[0] == '\n')
			break;
		//replace newline with null
		for(num1=0; num1<linestr_size; num1++){
			if(linestring[num1] == '\n'){
				linestring[num1] = '\0';
				break;
			}
		}


//memset ((void*)test, '\0', 6+30);
//hexchar = (uint32_t)strtoul((const char *)linestring, NULL, 16);
//strcpy(test, prefix);
//strcat(test, (const char*)linestring);
//strcat(test, suffix);


//&# ;
		//memset ((void*)test, '\0', 6);
		hexchar = (uint32_t)strtoul((const char *)linestring, NULL, 16);

//strcpy(test, (const char *)prefix);
//strcat(test, (const char *)linestring);
//strcat(test, (const char *)suffix);

//fprintf(out_test, "%s", test);

//sprintf(test, "%s%u%s", prefix, hexchar, suffix);
//fprintf(out_test, "%s%u%s", prefix, hexchar, suffix);

//fprintf(out_test, "%s, %d, ", linestring, hexchar);

		test_offset = utf8_encode(&test[test_idx], hexchar);
		if(test_offset == 0)
			exit(1);
//utf_bytes = utf_to_dec((const char *)test, &hexchar);
//fprintf(out_test, "%d, %d\n", utf_bytes, hexchar);
		test_idx += test_offset;



	}

	err = gdImageStringFTEx(NULL, brect, 0, input_ttf, pixel_pt_size, 0.0, x, y, (const char *)test, &Strex);
	if (err) {fprintf(stderr,err); return 1;}


	gdFree((void *)Strex.xshow);

image_width = brect[2]-brect[6] + image_border*2;
image_height = brect[3]-brect[7] + image_border*2;

//	fclose(entity);
	fclose(out_test);
	fclose(input_hex);

	//create image
	im = gdImageCreateTrueColor(image_width, image_height);


	//white = gdImageColorResolve(im, 940, 940, 940);
	//black = gdImageColorResolve(im, 0, 255, 0);
background_color = gdImageColorResolve(im, bg[0], bg[1], bg[2]);
foreground_color = gdImageColorResolve(im, fg[0], fg[1], fg[2]);
transparency_color = gdImageColorResolve(im, trans[0], trans[1], trans[2]);


gdImageFill(im, 0, 0, background_color);

//gdImageSetAntiAliasedDontBlend(im, transparency_color, 0);
//gdImageSetAntiAliasedDontBlend(im, background_color, 1);

//gdImageFilledRectangle(im, 0, 0, image_width, image_height, background_color);

	Set_strex_flags(&Strex);

	gdImageSetResolution(im,Strex.hdpi,Strex.vdpi);

	//open output font config file
	if( ( font_fnt_out = fopen(font_file, "w" ) ) == NULL ) {
		fprintf( stderr, "Error opening %s\n", font_file );
		return 1;
	}

//	fprintf(font_fnt_out, "%c image file\n", SEMICOLON);
//	fprintf(font_fnt_out, "image=font.png\n");
//	fprintf(font_fnt_out, "%c \"character\"=x,y,width,height\n", SEMICOLON);

x = image_border;
y = image_border;
err = gdImageStringFTEx(im, brect, foreground_color, input_ttf, pixel_pt_size, 0.0, x, y, (const char *)test, &Strex);
if (err) {fprintf(stderr,err); return 1;}


	printf("total chars: %ld\n", totalchars);


	gdFree((void *)Strex.xshow);
	free(glyph_metrics);

	//replace all background color with transparency
	for(x=0; x<image_width; x++){
		for(y=0; y<image_height; y++){
			if(gdImageGetPixel(im, x, y) == background_color)
				gdImageSetPixel(im, x, y, transparency_color);
		}
	}


        image_out = fopen(image_file, "w");
        gdImageInterlace(im, 1);
        gdImagePng(im, image_out);

        fclose(image_out);
        gdImageDestroy(im);
	free(test);
	printf("image size: %d x %d\n", image_width, image_height);

	//free(outputstring);
	//fclose(input_hex);
	fclose(font_fnt_out);
	return 0;
}


int main(int argc, char **argv){
	const char *font_suffix = ".fnt";
	const char *image_suffix = ".png";
	int cur_color;
	char fnt[64];
	char image[64];

	#define COLORS 13
	const char *color_name[COLORS] = {"red", "green", "limegreen", "blue", "yellow", "orange", "cyan", "teal", "white", "gray", "lightgray", "saddlebrown", "purple"};
	const int bg[3] = {24, 24, 24};
	const int trans[3] = {255, 0, 255};
	const int fg[COLORS][3] = {
					//red
					{255, 0, 0},
					//green
					{0, 128, 0},
					//limegreen
					{50, 205, 50},
					//blue
					{0, 0, 255},
					//yellow
					{255, 255, 0},
					//orange
					{255, 165, 0},
					//cyan
					{0, 255, 255},
					//teal
					{0, 128, 128},
					//white
					{255, 255, 255},
					//gray
					{128, 128, 128},
					//lightgray
					{211, 211, 211},
					//saddlebrown
					{139, 69, 19},
					//purple
					{128, 0, 128}
				};
	const char *ttf_dejavu = "./DejaVuSansMono.ttf";
	const char *hex_dejavu = "./DejaVu-Sans-Mono-Book/hex.txt";

//const char *hex_dejavu = "./entities.txt";
	const char *image_dejavu = "./DejaVu-Sans-Mono-Book/font";
	const char *fnt_dejavu = "./DejaVu-Sans-Mono-Book/font";
	const char *ttf_liberation = "./LiberationMono-Regular.ttf";
	const char *hex_liberation = "./Liberation-Mono-Regular/hex.txt";
	const char *image_liberation = "./Liberation-Mono-Regular/font";
	const char *fnt_liberation = "./Liberation-Mono-Regular/font";

	const char *ttf_iosevka = "./iosevka-fixed-slab-extended.ttf";
	const char *hex_iosevka = "./Iosevka-Fixed-Slab-Extended/hex.txt";
//const char *hex_iosevka = "./entities.txt";
	const char *image_iosevka = "./Iosevka-Fixed-Slab-Extended/font";
	const char *fnt_iosevka = "./Iosevka-Fixed-Slab-Extended/font";

	const char *ttf_plex = "./IBMPlexMono-Regular.ttf";
	const char *hex_plex = "./IBM-Plex-Mono-Regular/hex.txt";
	const char *image_plex = "./IBM-Plex-Mono-Regular/font";
	const char *fnt_plex = "./IBM-Plex-Mono-Regular/font";

	const double font_size_dejavu = 5.75;
	const double font_size_liberation = 6.5;
	const double font_size_iosevka = 4.5;
	const double font_size_plex = 5.75;



//cur_color=2;
//                (void)sprintf(image, "%s-%s%s", image_iosevka, color_name[cur_color], image_suffix);
                //(void)sprintf(fnt, "%s-%s%s", fnt_iosevka, color_name[cur_color], font_suffix);
//                (void)sprintf(fnt, "%s%s", fnt_iosevka, font_suffix);
//                (void)Gen_Charset(ttf_iosevka, hex_iosevka, (const char *)image, (const char *)fnt, bg, fg[cur_color], trans, font_size_iosevka);

//return 0;



	for(cur_color=0; cur_color<COLORS; cur_color++){
		(void)sprintf(image, "%s-%s%s", image_dejavu, color_name[cur_color], image_suffix);
		//(void)sprintf(fnt, "%s-%s%s", fnt_dejavu, color_name[cur_color], font_suffix);
		(void)sprintf(fnt, "%s%s", fnt_dejavu, font_suffix);
//printf("here 1\n");
		(void)Gen_Charset(ttf_dejavu, hex_dejavu, (const char *)image, (const char *)fnt, bg, fg[cur_color], trans, font_size_dejavu);
//printf("here 2\n");

		(void)sprintf(image, "%s-%s%s", image_liberation, color_name[cur_color], image_suffix);
		//(void)sprintf(fnt, "%s-%s%s", fnt_liberation, color_name[cur_color], font_suffix);
		(void)sprintf(fnt, "%s%s", fnt_liberation, font_suffix);
		(void)Gen_Charset(ttf_liberation, hex_liberation, (const char *)image, (const char *)fnt, bg, fg[cur_color], trans, font_size_liberation);

		(void)sprintf(image, "%s-%s%s", image_iosevka, color_name[cur_color], image_suffix);
		//(void)sprintf(fnt, "%s-%s%s", fnt_iosevka, color_name[cur_color], font_suffix);
		(void)sprintf(fnt, "%s%s", fnt_iosevka, font_suffix);
		(void)Gen_Charset(ttf_iosevka, hex_iosevka, (const char *)image, (const char *)fnt, bg, fg[cur_color], trans, font_size_iosevka);

		(void)sprintf(image, "%s-%s%s", image_plex, color_name[cur_color], image_suffix);
		//(void)sprintf(fnt, "%s-%s%s", fnt_plex, color_name[cur_color], font_suffix);
		(void)sprintf(fnt, "%s%s", fnt_plex, font_suffix);
		(void)Gen_Charset(ttf_plex, hex_plex, (const char *)image, (const char *)fnt, bg, fg[cur_color], trans, font_size_plex);
	}



	return 0;
}
