
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>

#include "cprs.h"

static void usage(char* argv[]) {
	fprintf(stderr, "Usage: %s <input.bin> <output.lz77>\n"
	                "Use '-' for stdin/stdout (not implicit!)\n",
		argv[0]);
}

static RECORD get_file_bytes(FILE* f) {
	RECORD ret;
	ret.width = 1;
	ret.height = 0;
	ret.data = NULL;

	if (fseek(f, 0, SEEK_END) < 0) {
		// aieee, data stream! (eg. stdin)
		const size_t blocksiz = 4096;
		size_t cap = blocksiz, size = 0, stat = 0;
		uint8_t* data = static_cast<uint8_t*>(calloc(cap, 1));
		ptrdiff_t readpos = 0;

		while (!feof(f)) {
			if (readpos + blocksiz > cap) {
				cap <<= 1;
				data = static_cast<uint8_t*>(realloc(data, cap));
			}

			stat = fread(data+readpos, 1, blocksiz, f);
			if (ferror(f)) {
				fprintf(stderr, "Error reading input!\n");
				free(data);
				return ret;
			}

			size += stat;
			readpos += stat;
			if (size >= (1LL<<31)) {
				fprintf(stderr, "Input too large!\n");
				free(data);
				return ret;
			}
		}

		ret.height = static_cast<int>(size);
		ret.data = data;
		return ret;
	} else {
		// file on disk
		long len = ftell(f);
		fseek(f, 0, SEEK_SET);
		if (len >= (1LL<<31) || len < 0) {
			fprintf(stderr, "Input file too large!\n");
			return ret;
		}
		size_t siz = static_cast<size_t>(len);

		void* data = calloc(siz, 1);
		if (fread(data, 1, siz, f) != siz) {
			fprintf(stderr, "Error reading intput file!\n");
			free(data);
			return ret;
		}
		ret.height = static_cast<int>(len);
		ret.data = static_cast<uint8_t*>(data);
		return ret;
	}
}

int main(int argc, char* argv[]) {
	if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || argc != 3) {
		usage(argv);
		return (argc == 2) ? 0 : 1;
	}

	const char* inpath = argv[1], *outpath = argv[2];
	if (!strcmp( inpath, "-"))  inpath = "/dev/stdin" ;
	if (!strcmp(outpath, "-")) outpath = "/dev/stdout";

	FILE* input = fopen(inpath, "rb");
	if (!input) {
		fprintf(stderr, "Can't open input file '%s'\n", argv[1]);
		return 1;
	}
	FILE* output = fopen(outpath, "wb");
	if (!output) {
		fprintf(stderr, "Can't open output file '%s'\n", argv[1]);
		return 1;
	}

	RECORD inbuf = get_file_bytes(input),
	       outbuf;
	outbuf.data = NULL;

	char* pname = basename(argv[0]);
	uint32_t tag;
	if (!strcmp(pname, "lz77c")) {
		tag = 1<<4;
		lz77gba_compress(&outbuf, &inbuf);
	} else if (!strcmp(pname, "huffc")) {
		tag = 2<<4;
		huffgba_compress(&outbuf, &inbuf, 8);
	} else {
		free(inbuf.data);
		fprintf(stderr,
			"Unknown compression mode '%s', expecting 'lz77c' or 'huffc' (argv[0])\n", argv[0]);
		return 1;
	}

	// borked as fuck
	//uint32_t header = cprs_create_header(CPRS_LZ77_TAG, rec_size(&inbuf));

	*reinterpret_cast<uint32_t*>(outbuf.data) = tag
		| ((static_cast<size_t>(rec_size(&inbuf))<<8)&0xFFFFFF00uLL);

	fwrite(outbuf.data, outbuf.width,outbuf.height, output);

	free(inbuf.data);
	free(outbuf.data);

	if (ferror(output)) {
		fprintf(stderr, "Can't write output\n");
		return 1;
	}

	return 0;
}

