#include <stdlib.h>
#include <stdio.h>
#include <gd.h>
#include <string.h>
#include <stdint.h>
#include <wchar.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <iconv.h>
/* to determine codepoint categories */
#include <unicode/uchar.h>
/* to output UTF-32 codepoints in proper encoding for terminal */
#include <unicode/ustdio.h>

#include <sys/types.h>
#include <limits.h>
#include <math.h>

#define SEMICOLON 59


int utf8_encode(char *out, uint32_t utf)
{
  if (utf <= 0x7F) {
    // Plain ASCII
    out[0] = (char) utf;
    out[1] = '\0';
    return 1;
  } else if (utf <= 0x07FF) {
    // 2-byte unicode
    out[0] = (char) (((utf >> 6) & 0x1F) | 0xC0);
    out[1] = (char) (((utf >> 0) & 0x3F) | 0x80);
    out[2] = '\0';
    return 2;
  } else if (utf <= 0xFFFF) {
    // 3-byte unicode
    out[0] = (char) (((utf >> 12) & 0x0F) | 0xE0);
    out[1] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[2] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[3] = '\0';
    return 3;
  } else if (utf <= 0x001FFFFF) {
    // 4-byte unicode
    out[0] = (char) (((utf >> 18) & 0x07) | 0xF0);
    out[1] = (char) (((utf >> 12) & 0x3F) | 0x80);
    out[2] = (char) (((utf >>  6) & 0x3F) | 0x80);
    out[3] = (char) (((utf >>  0) & 0x3F) | 0x80);
    out[4] = '\0';
    return 4;
  }else{
    // error - use replacement character
    out[0] = (char) 0xEF;  
    out[1] = (char) 0xBF;
    out[2] = (char) 0xBD;
    out[3] = 0;
    return 0;
  }


}


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


int main(int argc, char **argv){
	FILE *image_out;
	FILE *font_fnt_out;
	FILE *input_hex;

	gdImagePtr im;
	int black;
	int white;
	int background_color;
	int foreground_color;
	int transparency_color;
	//double tmp1, tmp2;
	int brect[8];
	int x = 0;
	int y = 0;
	char *err;
	//int status;
	char space[2];
	int image_width = 0;
	int image_height = 0;
	char linestring[1024];
	char test[64];
	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;

	int extra_char_spacing = 1.0;
	int image_border = 40;
	double pixel_pt_size = 5.75;

//	char *input_ttf = "/mnt/sdb1/pub/test/gdfont/iosevka-fixed-slab-extended.ttf";
//char *input_ttf = "/mnt/sdb1/pub/test/gdfont/LiberationMono-Regular.ttf";
char *input_ttf = "/mnt/sdb1/pub/test/gdfont/DejaVuSansMono.ttf";


	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;
	space[0] = ' ';
	space[1] = '\0';
	char *xshow_ptr = strex->xshow;

	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;
	//strex->flags |= gdFTEX_FONTPATHNAME;
	//strex->flags |= gdFTEX_FONTCONFIG;
	//strex->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;


	//count how many chars we have
        if( ( input_hex = fopen("hex.txt", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening %s\n", "hex.txt" );
                return 1;
        }
	memset ((void*) linestring, '\0', 1024);

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

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

	x=0;
	y=0;

	//populate glyph_metrics
	while( fgets(linestring, 1024, input_hex) != NULL){
		//make sure we skip last newline
		if(linestring[0] == '\n')
			break;
		//replace newline with null
		for(num1=0; num1<1024; num1++){
			if(linestring[num1] == '\n'){
				linestring[num1] = '\0';
				break;
			}
		}
		memset ((void*)test, '\0', 64);
		hexchar = (uint32_t)strtoul((const char *)linestring, NULL, 16);
		(void)utf8_encode(test, hexchar);

		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;}

		memset ((void*)glyph_metrics[cur_char].str, '\0', 16);
		(void)strcpy(glyph_metrics[cur_char].str, (const char *)test );
		//all these should be in relation to the requested x,y coordinate
		//glyph bounding box offsets from x/y
		glyph_metrics[cur_char].xmin = x - brect[0]; //brect[0] < x ? brect[0] : brect[0]
		glyph_metrics[cur_char].xmax = brect[4] - x;
		glyph_metrics[cur_char].ymin = brect[3] - y;
		glyph_metrics[cur_char].ymax = y - brect[7];

		//set these to zero
		//xshow seems to be x at the glyph origin
		glyph_metrics[cur_char].orig_x = 0;//atof((const char *)strex->xshow);
		//can't know this for sure
		glyph_metrics[cur_char].orig_y = 0;

		//bearings should be included in brect output
		glyph_metrics[cur_char].bearingx = glyph_metrics[cur_char].xmin - glyph_metrics[cur_char].orig_x;
		glyph_metrics[cur_char].bearingy = glyph_metrics[cur_char].orig_y - glyph_metrics[cur_char].ymax;

		glyph_metrics[cur_char].height = abs(glyph_metrics[cur_char].ymin + glyph_metrics[cur_char].ymax);
		glyph_metrics[cur_char].width = glyph_metrics[cur_char].xmax + glyph_metrics[cur_char].xmin;

		gdFree((void *)strex->xshow);

		//need back to back chars to calculate advance
		(void)strcat((char *)test, (const char *)space);
		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;}

		xshow_ptr = strex->xshow;
		while(*(xshow_ptr) != ' ')
			xshow_ptr++;
		//x offset to next glyph's origin
		glyph_metrics[cur_char].advance = atoi((const char *)xshow_ptr) - x;
		cur_char++;

	}
	fclose(input_hex);
	gdFree((void *)strex->xshow);


	//find global bounding box dimensions
	for(cur_char=0; cur_char<totalchars; cur_char++){
		if(glyph_metrics[cur_char].advance > global_max_advance)
			global_max_advance = glyph_metrics[cur_char].advance;
		if(glyph_metrics[cur_char].ymax > global_ymax)
			global_ymax = glyph_metrics[cur_char].ymax;
		if(glyph_metrics[cur_char].ymin > global_ymin)
			global_ymin = glyph_metrics[cur_char].ymin;
	}
	global_bb_height = global_ymin + global_ymax;
	global_bb_width = global_max_advance;

	printf("global_bb_width: %d global_bb_height: %d global_max_advance: %d\n", global_bb_width, global_bb_height, global_max_advance);
	printf("added_spacing: %d\n", extra_char_spacing);
	image_height = image_border*2 + global_bb_height;
	image_width  = image_border*2 + (global_bb_width+extra_char_spacing)*totalchars;
	//create image
	im = gdImageCreateTrueColor(image_width, image_height);


	//white = gdImageColorResolve(im, 940, 940, 940);
	//black = gdImageColorResolve(im, 0, 255, 0);
background_color = gdImageColorResolve(im, 24, 24, 24);
foreground_color = gdImageColorResolve(im, 0, 255, 0);
transparency_color = gdImageColorResolve(im, 255, 0, 255);


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);

	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;
	//strex->flags |= gdFTEX_FONTPATHNAME;
	//strex->flags |= gdFTEX_FONTCONFIG;
	//strex->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;

	gdImageSetResolution(im,strex->hdpi,strex->vdpi);

	//open output font config file
	if( ( font_fnt_out = fopen("font.fnt", "w" ) ) == NULL ) {
		fprintf( stderr, "Error opening %s\n", "font.fnt" );
		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 = global_bb_height + image_border;
	for(cur_char=0; cur_char<totalchars; cur_char++){

		err = gdImageStringFTEx(im,brect,foreground_color,input_ttf,pixel_pt_size,0.0,x,y,(const char *)glyph_metrics[cur_char].str,strex);
                if (err) {fprintf(stderr,err); return 1;}
		fprintf(font_fnt_out, "\"%s\"=%d,%d,%d,%d\n", glyph_metrics[cur_char].str, x, y-global_ymax, global_bb_width+extra_char_spacing, y+glyph_metrics[cur_char].ymin - y+global_ymax);
		x += global_bb_width+extra_char_spacing;
	}


	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("test01.png", "w");
        gdImageInterlace(im, 1);
        gdImagePng(im, image_out);

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

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