#include "font.h"
#include "platform/platform.h"


FontT *fontFromRAMLoad(uint8_t *pointer) {
	FontT    *font = NULL;
	uint32_t  size = 0;

	font = (FontT *)malloc(sizeof(FontT));
	if (!font) {
		return NULL;
	}

	font->width  = pointer[0];  // Width of character in pixels
	font->height = pointer[1];  // Height of character in pixels
	font->span   = pointer[2];  // Width of atlas in characters
	font->count  = pointer[3];  // Number of characters in atlas - 1

	font->widthPixels  = font->width * font->span;                          // Width of atlas in pixels
	font->heightPixels = font->height * ((font->count + 1) / font->span);   // Height of atlas in pixels
	size               = font->widthPixels * font->heightPixels / 8;        // Bits required to store atlas

	font->bits = (uint8_t *)malloc(size);
	if (!font->bits) {
		DEL(font);
		return NULL;
	}

	memcpy(font->bits, &pointer[4], size);

	return font;
}


uint16_t fontHeightGet(FontT *font) {
	return font->height;
}


FontT *fontLoad(char *filename) {
	FILE     *in     = NULL;
	size_t    size   = 0;
	uint8_t  *buffer = NULL;
	FontT    *font   = NULL;

	in = fopen(filename, "rb");
	if (!in) return NULL;

	fseek(in, 0, SEEK_END);
	size = ftell(in);
	fseek(in, 0, SEEK_SET);

	buffer = (uint8_t *)malloc(size);
	if (!buffer) {
		fclose(in);
		return NULL;
	}

	fread(buffer, size, 1, in);
	fclose(in);

	font = fontFromRAMLoad(buffer);

	DEL(buffer);

	return font;
}


void fontRender(FontT *font, char *string, ColorT foreground, ColorT background, uint16_t x, uint16_t y) {
	uint8_t  cx;
	uint8_t  cy;
	uint16_t offset;
	uint8_t  yl;
	uint16_t yp;
	uint8_t  data;
	uint8_t  character;
	uint8_t  c;
	uint8_t  odd;

	//***TODO*** This only handles 4xY and 8xY fonts.

	for (c=0; c<strlen(string); c++) {
		character = string[c];

		// Find character position in font grid.
		cx = character % font->span;
		cy = character / font->span;

		// Half byte or full byte font?
		if (font->width == 4) {
			// Half Byte.

			// Which half of the byte do we want?
			odd = ((cx & 1) == 1);

			// Find offset byte based on font bits.
			offset = cy * (font->span / 2) * font->height + cx * ((float)font->width / 8.0f);

			for (yl=0; yl<font->height; yl++) {
				// We do 4 pixels unrolled hoping it's fast.
				data = font->bits[offset];
				offset += (font->span / 2);

				if (odd) {
					videoSurfacePixelSet(x,     yp, (data & 0x08) ? foreground : background);
					videoSurfacePixelSet(x + 1, yp, (data & 0x04) ? foreground : background);
					videoSurfacePixelSet(x + 2, yp, (data & 0x02) ? foreground : background);
					videoSurfacePixelSet(x + 3, yp, (data & 0x01) ? foreground : background);
				} else {
					videoSurfacePixelSet(x,     yp, (data & 0x80) ? foreground : background);
					videoSurfacePixelSet(x + 1, yp, (data & 0x40) ? foreground : background);
					videoSurfacePixelSet(x + 2, yp, (data & 0x20) ? foreground : background);
					videoSurfacePixelSet(x + 3, yp, (data & 0x10) ? foreground : background);
				}

				yp++;
			}
		} else {
			// Full Byte.

			// Find offset byte based on font bits.
			offset = cy * font->span * font->height + cx;

			// Draw out 8 lines.
			yp = y;
			for (yl=0; yl<font->height; yl++) {
				// We do 8 pixels unrolled hoping it's fast.
				data = font->bits[offset];
				offset += font->span;

				videoSurfacePixelSet(x,     yp, (data & 0x80) ? foreground : background);
				videoSurfacePixelSet(x + 1, yp, (data & 0x40) ? foreground : background);
				videoSurfacePixelSet(x + 2, yp, (data & 0x20) ? foreground : background);
				videoSurfacePixelSet(x + 3, yp, (data & 0x10) ? foreground : background);
				videoSurfacePixelSet(x + 4, yp, (data & 0x08) ? foreground : background);
				videoSurfacePixelSet(x + 5, yp, (data & 0x04) ? foreground : background);
				videoSurfacePixelSet(x + 6, yp, (data & 0x02) ? foreground : background);
				videoSurfacePixelSet(x + 7, yp, (data & 0x01) ? foreground : background);

				yp++;
			}
		}

		x += font->width;
	}
}


void fontUnload(FontT **font) {
	FontT *f = *font;

	free(f->bits);
	free(f);
	f = NULL;
}


uint16_t fontWidthGet(FontT *font) {
	return font->width;
}
