#define GF_EXPOSE_FONT

#include <gf_pre.h>

/* External library */

/* Interface */
#include <gf_font.h>

/* Engine */
#include <gf_log.h>
#include <gf_texture.h>

/* Standard */
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

gf_font_glyph_t* gf_font_get(gf_font_t* font, int code) {
	int i;
	if(code < 0x20) return NULL;
	for(i = 0; i < font->count; i++) {
		if(font->glyph[i] != NULL && font->glyph[i]->code == code) {
			return font->glyph[i];
		}
	}
	return NULL;
}

#ifdef _MSC_VER
#define gf_stat _stat
#else
#define gf_stat stat
#endif

GF_DECLARE_TYPE(font_store, {
	int	       glyph_index;
	int	       line_index;
	unsigned char* buffer;
});

/**
 * This is a private method to parse BDF line
 */
void gf_font_parse_line(gf_draw_t* draw, const char* path, gf_font_store_t* store, gf_font_t* font, char* line) {
	int   i;
	char* args[32];
	int   argc = 0;
	int   incr = 0;
	int   dq   = 0;
	for(i = 0;; i++) {
		if((dq == 0 && line[i] == ' ') || line[i] == 0) {
			char oldc = line[i];
			line[i]	  = 0;

			args[argc] = line + incr;
			if(args[argc][0] == '"') args[argc]++;
			if(args[argc][strlen(args[argc]) - 1] == '"') args[argc][strlen(args[argc]) - 1] = 0;

			argc++;

			incr = i + 1;

			if(oldc == 0) break;
		} else if(line[i] == '"') {
			dq = 1 - dq;
		}
	}
	if(store->line_index != -1 && store->line_index < font->glyph[store->glyph_index]->bbox.height) {
		int	       wid     = font->glyph[store->glyph_index]->bbox.width;
		unsigned char* linebuf = store->buffer + store->line_index * wid * 4;
		for(i = 0; line[i] != 0; i++) {
			int n = 0;
			int j;
			if('0' <= line[i] && line[i] <= '9') {
				n = line[i] - '0';
			} else if('a' <= line[i] && line[i] <= 'f') {
				n = 10 + line[i] - 'a';
			} else if('A' <= line[i] && line[i] <= 'F') {
				n = 10 + line[i] - 'A';
			}
			for(j = 0; j < (wid > 4 ? 4 : wid); j++) {
				if((n >> 3) & 1) {
					memset(linebuf + 16 * i + 4 * j, 255, 4);
				}
				n = n << 1;
			}
			wid -= 4;
		}
		store->line_index++;
	} else if(argc > 0 && strcmp(args[0], "STARTCHAR") == 0) {
		font->glyph[store->glyph_index] = malloc(sizeof(**font->glyph));
		memset(font->glyph[store->glyph_index], 0, sizeof(**font->glyph));
	} else if(argc > 0 && strcmp(args[0], "ENDCHAR") == 0) {
		font->glyph[store->glyph_index]->texture = gf_texture_create(draw, font->glyph[store->glyph_index]->bbox.width, font->glyph[store->glyph_index]->bbox.height, store->buffer);
		free(store->buffer);
		store->glyph_index++;
		store->line_index = -1;
	} else if(argc > 0 && strcmp(args[0], "BITMAP") == 0) {
		store->line_index = 0;
	} else if(argc == 2) {
		if(strcmp(args[0], "COPYRIGHT") == 0) {
			gf_log_function(NULL, "%s: %s", path, args[1]);
		} else if(strcmp(args[0], "NOTICE") == 0) {
			gf_log_function(NULL, "%s: %s", path, args[1]);
		} else if(strcmp(args[0], "FOUNDRY") == 0) {
			gf_log_function(NULL, "%s: Made by %s", path, args[1]);
		} else if(strcmp(args[0], "CHARS") == 0) {
			int j;
			gf_log_function(NULL, "%s: %s characters", path, args[1]);
			font->count = atoi(args[1]);
			font->glyph = malloc(font->count * sizeof(*font->glyph));
			for(j = 0; j < font->count; j++) font->glyph[j] = NULL;
		} else if(strcmp(args[0], "ENCODING") == 0) {
			font->glyph[store->glyph_index]->code = atoi(args[1]);
		}
	} else if(argc == 3) {
		if(strcmp(args[0], "DWIDTH") == 0) {
			font->glyph[store->glyph_index]->dwidth[0] = atoi(args[1]);
			font->glyph[store->glyph_index]->dwidth[1] = atoi(args[2]);
		}
	} else if(argc == 5) {
		if(strcmp(args[0], "FONTBOUNDINGBOX") == 0) {
			font->bbox.width  = atoi(args[1]);
			font->bbox.height = atoi(args[2]);
			font->bbox.x	  = atoi(args[3]);
			font->bbox.y	  = atoi(args[4]);
		} else if(strcmp(args[0], "BBX") == 0) {
			font->glyph[store->glyph_index]->bbox.width  = atoi(args[1]);
			font->glyph[store->glyph_index]->bbox.height = atoi(args[2]);
			font->glyph[store->glyph_index]->bbox.x	     = atoi(args[3]);
			font->glyph[store->glyph_index]->bbox.y	     = atoi(args[4]);

			store->buffer = malloc(atoi(args[1]) * atoi(args[2]) * 4);
			memset(store->buffer, 0, atoi(args[1]) * atoi(args[2]) * 4);
		}
	}
}

gf_font_t* gf_font_create(gf_draw_t* draw, const char* path) {
	gf_font_t*	font = malloc(sizeof(*font));
	struct gf_stat	s;
	char*		buf;
	FILE*		f;
	int		i    = 0;
	int		incr = 0;
	gf_font_store_t store;
	store.line_index  = -1;
	store.glyph_index = 0;
	memset(font, 0, sizeof(*font));
	if(gf_stat(path, &s) != 0) {
		free(font);
		return NULL;
	}
	gf_log_function(NULL, "%s: %lu bytes", path, (unsigned long)s.st_size);
	buf	       = malloc(s.st_size + 1);
	buf[s.st_size] = 0;
	f	       = fopen(path, "r");
	fread(buf, s.st_size, 1, f);
	fclose(f);

	for(i = 0;; i++) {
		if(buf[i] == 0 || buf[i] == '\n') {
			char  oldc = buf[i];
			char* line = buf + incr;
			buf[i]	   = 0;
			incr	   = i + 1;

			gf_font_parse_line(draw, path, &store, font, line);

			if(oldc == 0) break;
		}
	}

	free(buf);
	return font;
}
