/*
 * Kangaroo Punch MultiPlayer Game Server Mark II
 * Copyright (C) 2020-2021  Scott Duensing
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */


#include "font.h"
#include "surface.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++) {
				data = font->bits[offset];
				offset += (font->span / 2);

				if (odd) {
					surfacePutPixel(x,     yp, data & 0x08 ? foreground : background);
					surfacePutPixel(x + 1, yp, data & 0x04 ? foreground : background);
					surfacePutPixel(x + 2, yp, data & 0x02 ? foreground : background);
					surfacePutPixel(x + 3, yp, data & 0x01 ? foreground : background);
				} else {
					surfacePutPixel(x,     yp, data & 0x80 ? foreground : background);
					surfacePutPixel(x + 1, yp, data & 0x40 ? foreground : background);
					surfacePutPixel(x + 2, yp, data & 0x20 ? foreground : background);
					surfacePutPixel(x + 3, yp, data & 0x10 ? foreground : background);
				}
			}
		} 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;
#ifndef FONT_DEBUGGING
				surfacePutPixel(x,     yp, data & 0x80 ? foreground : background);
				surfacePutPixel(x + 1, yp, data & 0x40 ? foreground : background);
				surfacePutPixel(x + 2, yp, data & 0x20 ? foreground : background);
				surfacePutPixel(x + 3, yp, data & 0x10 ? foreground : background);
				surfacePutPixel(x + 4, yp, data & 0x08 ? foreground : background);
				surfacePutPixel(x + 5, yp, data & 0x04 ? foreground : background);
				surfacePutPixel(x + 6, yp, data & 0x02 ? foreground : background);
				surfacePutPixel(x + 7, yp, data & 0x01 ? foreground : background);
#endif
				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;
}
