#include "fonts.h"
#include "render.h"

#include <stdint.h>

// Local function: Get next nibble.
static int ctr = 0;  // offset for next nibble
static int hilo = 0; // 0= high nibble next, 1=low nibble next
static const uint8_t* data;

#define MAXCHR (30 * 20)
static uint8_t charBuf[MAXCHR];

// Get next nibble
static uint8_t gnn()
{
	static uint8_t byte;
	uint8_t val;
	if (hilo == 1)
		ctr++;
	hilo = 1 - hilo;
	if (hilo == 1) {
		byte = data[ctr];
		val = byte >> 4;
	} else {
		val = byte & 0x0f;
	};
	return val;
}

// Local function: Unpack "long run".
static int upl(int off)
{
	int retval;

	while ((retval = gnn()) == 0) {
		off++;
	};
	while (off-- > 0) {
		retval = retval << 4;
		retval += gnn();
	};
	return retval;
}

uint8_t* rad1o_pk_decode(const uint8_t* ldata, int* len)
{
	ctr = 0;
	hilo = 0;
	data = ldata;
	int length = *len;         // Length of character bytestream
	int height;                // Height of character in bytes
	int hoff;                  // bit position for non-integer heights
	uint8_t* bufptr = charBuf; // Output buffer for decoded character

	height = (rad1o_getFontHeight() - 1) / 8 + 1;
	hoff = rad1o_getFontHeight() % 8;

#define DYN (12)        // Decoder parameter: Fixed value for now.
	int repeat = 0; // Decoder internal: repeat colum?
	int curbit = 0; // Decoder internal: current bit (1 or 0)
	int pos = 0;    // Decoder internal: current bit position (0..7)
	int nyb;        // Decoder internal: current nibble / value

	if (data[ctr] >> 4 == 14) { // Char starts with 1-bits.
		gnn();
		curbit = 1;
	};

	while (ctr < length) { /* Iterate the whole input stream */

		/* Get next encoded nibble and decode */
		nyb = gnn();

		if (nyb == 15) {
			repeat++;
			continue;
		} else if (nyb == 14) {
			nyb = upl(0);
			nyb += 1;
			repeat += nyb;
			continue;
		} else if (nyb > DYN) {
			nyb = (16 * (nyb - DYN - 1)) + gnn() + DYN + 1;
		} else if (nyb == 0) {
			nyb = upl(1);
			nyb += (16 * (13 - DYN) + DYN) - 16;
		};

		/* Generate & output bits */

		while (nyb-- > 0) {
			if (pos == 0) // Clear each byte before we start.
				*bufptr = 0;
			if (curbit == 1) {
				*bufptr |= 1 << (7 - pos);
			};
			pos++;
			if (((bufptr - charBuf) % height) == (height - 1) &&
			    (pos == hoff)) {
				// Finish incomplete last byte per column
				pos = 8;
			};

			if (pos == 8) {
				bufptr++;
				if ((bufptr - charBuf) % height == 0) { // End of column?
					while (repeat > 0) {
						for (int y = 0; y < height; y++) {
							bufptr[0] = bufptr[-height];
							bufptr++;
						};
						repeat--;
					};
				};
				pos = 0;
			};
		};
		curbit = 1 - curbit;
	};

	*len = (bufptr - charBuf) / height; // return size of output buffer.
	return charBuf;
}
