module mqoi;

macro rgb_hash(Rgb px) => ((px.r * 3 + px.g * 5 + px.b * 7) & 0b00111111);
macro mqoi_rgba_hash(Rgba px) => ((px.r * 3 + px.g * 5 + px.b * 7 + px.a * 11) & 0b00111111);

const uint MQOI_HEADER_SIZE = 14;

const char MQOI_MASK_OP_2B = 0b11000000;
const char MQOI_MASK_OP_8B = 0b11111111;

const char MQOI_MASK_OP_LUMA_DG = 0b00111111;
const char MQOI_MASK_OP_RUN = 0b00111111;

// basic types

enum DescErr : uint (uint value) {
	MQOI_DESC_OK = 0, // The descriptor is valid
	MQOI_DESC_INVALID_MAGIC = 1, // The magic value isn't correct
	MQOI_DESC_INVALID_CHANNELS = 2, // The channel number isn't valid
	MQOI_DESC_INVALID_COLORSPACE = 3, // The colorspace isn't valid
}

enum Op : char (char value) {
	MQOI_OP2_INDEX   = (0b00 << 6),
	MQOI_OP2_DIFF    = (0b01 << 6),
	MQOI_OP2_LUMA    = (0b10 << 6),
	MQOI_OP2_RUN     = (0b11 << 6),
	MQOI_OP8_RUN_RGB  = (0b11111110),
	MQOI_OP8_RUN_RGBA = (0b11111111),
}

enum Channels : char (char value) {
	MQOI_CHANNELS_RGB = 3,
	MQOI_CHANNELS_RGBA = 4,
}

enum Colorspace : char (char value) {
	MQOI_COLORSPACE_SRGB = 0,
	MQOI_COLORSPACE_LINEAR = 1,
}

union Rgb {
	struct { char r, g, b; }
	char[3] value;
}

union Rgba{
	struct { char r, g, b, a; }
	char[4] value;
}

struct Desc {
	char head;

	char[4] magic;
	char[4] width; // big-endian width
	char[4] height; // big-endian height
	char channels;
	char colorspace;
}

// ==== chunks ====

union Chunk {
	struct {
		char head;
		union {
			Rgb rgb;
			Rgba rgba;
			char drdb;
		}
	}
	char[5] value;
}

// ==== codecs ====

struct Encoding {
	Rgba[64] hashtable;
	Rgba prev_px;
	Chunk working_chunk;
	char working_chunk_size;
}

struct Dec {
	Rgba[64] hashtable;
	Rgba prev_px;
	Chunk curr_chunk;
	bitstruct : char {
		char curr_chunk_head : 0..3;
		char curr_chunk_size : 4..7;
	}
	uint pix_left;
}

// ==== utilities ====

fn void u32_write(uint * n, char * dest) @extern("mqoi_u32_write");
fn void u32_read(char * src, uint * n) @extern("mqoi_u32_read");

// ==== Desc ====

fn void  desc_init(Desc* desc) @extern("mqoi_desc_init");
fn void  desc_push(Desc* desc, char byte) @extern("mqoi_desc_push");
fn char* desc_pop(Desc* desc) @extern("mqoi_desc_pop");
fn char  desc_verify(Desc* desc, uint* w, uint* h) @extern("mqoi_desc_verify");
fn bool  desc_done(Desc* desc) @extern("mqoi_desc_done");

/* the encoder is still WIP
void mqoi_enc_init(mqoi_enc_t * enc);
void mqoi_enc_push(mqoi_enc_t * enc, Rgba * pix)
Chunk * mqoi_enc_pop(mqoi_enc_t * enc, char * size);
*/

// ==== Dec ====

fn void  dec_init(Dec* dec, uint n_pix) @extern("mqoi_dec_init");
fn void  dec_push(Dec* dec, char byte) @extern("mqoi_dec_push");
fn char  dec_take(Dec* dec, char* bytes) @extern("mqoi_dec_take");
fn Rgba* dec_pop(Dec* dec) @extern("mqoi_dec_pop");
fn bool  dec_done(Dec* dec) @extern("mqoi_dec_done");