/*
 * BMFMAKE: BitMap Font creation utility
 *
 * This simple program takes an input image containing all of the font's glyphs,
 * and outputs a .bmf file for use in the game engine for Rovgend's Labyrinth.
 *
 * The input image must contain every ASCII glyph from '!' to '~' in a
 * horizontal row, each separated by a single column of 'blank' pixels. Black
 * pixels will be used as the background colour, while any other pixel colour
 * will be used for the foreground and represents where the font will be filled
 * in. Since the image will be loaded without transparency, the alpha values
 * don't matter.
 *
 * The program will then generate a BDF file as output. The first byte of the
 * file contains the height of the font, and the highest bit determines whether
 * this is a fixed or dynamic width font (high bit means fixed-width). The font
 * table then follows, containing the widths of each character from ASCII space
 * (' ') to tilde ('~'). The remainder of the file contains the pixel data for
 * each glyph, where a single byte represents a column of pixels in the glyph.
 *
 * Created by: cflip <cflip@cflip.net>
 * Last modified: 2023-09-13
 */

#include <stdio.h>
#include <stdbool.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define MAX_GLYPHS 32 * 3
#define MAX_WIDTH 10

/* Converts a column of pixels from the input image to a single byte
 * where each bit represents a pixel in that column. */
unsigned char make_column_byte(const unsigned char *pixels, int w, int h, int x)
{
	int result = 0;
	for (int y = 0; y < h; y++) {
		if (x < 0 || x > w)
			continue;
		unsigned char srccol = pixels[x + y * w];
		if (srccol > 0) result |= (1 << y);
	}
	return result;
}

int main(int argc, char *argv[])
{
	FILE *fp;
	char *input_path = NULL;
	char *output_path = NULL;

	int imgw, imgh, imgbpp;
	unsigned char *pixels = NULL;

	unsigned char glyph_widths[MAX_GLYPHS];
	unsigned char glyph_data[MAX_GLYPHS][MAX_WIDTH];

	int glyph_idx;
	int fixed_width = 0;
	
	int argn = 0;
	char *argp;

	(void)argc;
	while ((argp = argv[++argn]) != NULL) {
		if (strncmp("-fixed", argp, 6) == 0) {
			argp = argv[++argn];
			fixed_width = atoi(argp);
		} else {
			if (input_path == NULL) {
				input_path = argp;
			} else if (output_path == NULL) {
				output_path = argp;
			}
		}
	}

	if (input_path == NULL || output_path == NULL) {
		fprintf(stderr, "USAGE: %s [options] <input> <output>\n", argv[0]);
		return EXIT_FAILURE;
	}

	pixels = stbi_load(input_path, &imgw, &imgh, &imgbpp, 1);
	if (pixels == NULL) {
		fprintf(stderr, "ERROR: Unable to load image from %s\n", input_path);
		return EXIT_FAILURE;
	}

	printf("Image: %dx%d @ %d bytes per pixel (forced to 1)\n", imgw, imgh, imgbpp);

	fp = fopen(output_path, "wb");
	if (fp == NULL) {
		fprintf(stderr, "ERROR: Failed to open %s for writing.\n", output_path);
		return EXIT_FAILURE;
	}

	memset(glyph_widths, 0, MAX_GLYPHS);
	memset(glyph_data, 0, MAX_GLYPHS * MAX_WIDTH);

	/* Space isn't included in the input image, just assume it's 3 chars wide. */
	glyph_widths[0] = 3;
	
	glyph_idx = 1;
	int xx = 0;
	for (int xoffs = 0; xoffs < imgw; xoffs++) {
		unsigned char column_byte = make_column_byte(pixels, imgw, imgh, xoffs);

		if (fixed_width > 0) {
			if (xx == fixed_width) {
				/* We are in the blank space between characters,
				 * reset xx and continue to the next glyph in
				 * the next pixel column. */
				glyph_idx++;
				xx = 0;
				continue;
			}
			glyph_widths[glyph_idx] = fixed_width;
		} else {
			/* If the column is empty, skip this and move on to the
			 * next glyph. */
			if (column_byte == 0) {
				glyph_idx++;
				xx = 0;
				continue;
			}
			glyph_widths[glyph_idx]++;
		}
		
		glyph_data[glyph_idx][xx] = column_byte;
		xx++;
	}

	if (fixed_width > 0) {
		/* Make sure the highest bit is set to indicate that the font is
		 * fixed-width and only a single width for the entire font will
		 * follow instead of a width table. */
		imgh |= (1 << 7);
	}
	
	fwrite(&imgh, 1, 1, fp);
	fwrite(glyph_widths, 1, (fixed_width == 0 ? MAX_GLYPHS : 1), fp);

	for (int i = 0; i < MAX_GLYPHS; i++) {
		fwrite(&glyph_data[i][0], glyph_widths[i], 1, fp);
	}

	char width_type_str[32];
	if (fixed_width > 0) {
		snprintf(width_type_str, 32, "fixed-width (%dpx)", fixed_width);
	} else {
		size_t n = strlen("dynamic-width");
		strcpy(width_type_str, "dynamic-width");
		width_type_str[n] = '\0';
	}

	printf("Wrote %s: %dpx %s font.\n", output_path, (imgh & 0x7f), width_type_str);

	fclose(fp);
	stbi_image_free(pixels);

	return EXIT_SUCCESS;
}
