#include "localisation.h"
#include "platform.h"
#include "sys_log.h"
#include "stringmap.h"
#include <string.h>
#include <stdlib.h>

#define magic   (0x44537279ul)
#define version (0x00000000ul)

struct stringsdb {
	char* blob;
	yrStringmap* map;
}
def = {0}, loc = {0}, sub = {0};

static int load_stringsdb(struct stringsdb* sd, const char* file);
static void free_stringsdb(struct stringsdb* sd);
const char* emptystring = "";

int yrLocalise_init(const char* stringsdb, const char* locale, const char* subloc)
{
	//create filenames
	size_t len_def = strlen(stringsdb) + sizeof(".ysd");
	size_t len_loc = len_def + sizeof(".") + (locale ? strlen(locale) : 0);
	size_t len_sub = len_loc + sizeof("-") + (subloc ? strlen(subloc) : 0);

	char* fname_def = NULL;
	char* fname_loc = NULL;
	char* fname_sub = NULL;
	fname_def = malloc(len_def);		if(!fname_def) {yrLog(0, "Out of memory"); goto onerror;}
	strcpy(fname_def, stringsdb);
	strcat(fname_def, ".ysd");

	if(locale) {
		fname_loc = malloc(len_loc);	if(!fname_loc) {yrLog(0, "Out of memory"); goto onerror;}
		strcpy(fname_def, stringsdb);
		strcat(fname_def, ".");
		strcat(fname_def, locale);
		strcat(fname_def, ".ysd");
	}

	if(subloc) {
		fname_loc = malloc(len_sub);	if(!fname_sub) {yrLog(0, "Out of memory"); goto onerror;}
		strcpy(fname_def, stringsdb);
		strcat(fname_def, ".");
		strcat(fname_def, locale);
		strcat(fname_def, "-");
		strcat(fname_def, subloc);
		strcat(fname_def, ".ysd");
	}

	//load strings dbs
	int err = load_stringsdb(&def, fname_def);
	if(err) goto onerror;
	if(locale)	load_stringsdb(&loc, fname_loc); //load errors here will get logged but don't cause abort
	if(subloc)	load_stringsdb(&sub, fname_sub);

	//done
	free(fname_def);
	free(fname_loc);
	free(fname_sub);
	return 0;

onerror:
	free_stringsdb(&def);
	free_stringsdb(&loc);
	free_stringsdb(&sub);
	free(fname_def);
	free(fname_loc);
	free(fname_sub);
	return -1;
}

void yrLocalise_cleanup(void)
{
	free_stringsdb(&def);
	free_stringsdb(&loc);
	free_stringsdb(&sub);
}

const char* yrLocalise_get_string(const char* id)
{
	const char* out = NULL;
	if(sub.map)			out = yrStringmap_lookup(sub.map, id);
	if(!out && loc.map)	out = yrStringmap_lookup(loc.map, id);
	if(!out && def.map)	out = yrStringmap_lookup(def.map, id);
	if(!out)			{ out = emptystring; yrLog(0, "No string with id '%s' found", id); }
	return out;
}

static int load_stringsdb(struct stringsdb* sd, const char* file)
{
	//open file
	yrFile f = yrFile_open(file, yrF_read);
	if(!f) return -1;

	//check magic value and version
	size_t rw;
	uint32_t val;
	rw = yrFile_read(f, 4, &val);			if(rw != 4) goto onerror;
											if(val != magic) {yrLog(0, "Not a strings db file"); goto onerror;}
	rw = yrFile_read(f, 4, &val);			if(rw != 4) goto onerror;
											if(val != version) {yrLog(0, "Bad version"); goto onerror;}

	//read blob
	rw = yrFile_read(f, 4, &val);			if(rw != 4) goto onerror;
	sd->blob = malloc(val);					if(!sd->blob) {yrLog(0, "Out of memory"); goto onerror;}
	rw = yrFile_read(f, val, sd->blob);		if(rw != val) {yrLog(0, "Invalid strings db file"); goto onerror;}

	//create and fill stringmap
	unsigned char idlen;
	char idval[256] = {0};
	uint32_t offset;
	rw = yrFile_read(f, 4, &val);			if(rw != 4) goto onerror;
	sd->map = yrStringmap_create(val);		if(!sd->map) goto onerror;
	for(uint64_t i = 0; i < val; ++i)
	{
		rw = yrFile_read(f, 1, &idlen);		if(rw != 1)		goto onerror;
		rw = yrFile_read(f, idlen, idval);	if(rw != idlen) goto onerror;
		rw = yrFile_read(f, 4, &offset);	if(rw != 4)		goto onerror;
		int err = yrStringmap_insert(sd->map, idval, sd->blob + offset);
		if(err) yrLog(0, "Ignored string with id '%s'", idval);
	}

	//done
	yrFile_close(f);
	return 0;

onerror:
	yrLog(0, "Strings db '%s' not used.", file);
	yrStringmap_destroy(sd->map, 0);
	free(sd->blob);
	sd->map = NULL;
	sd->blob = NULL;
	if(f) yrFile_close(f);
	return -1;
}

static void free_stringsdb(struct stringsdb* sd)
{
	yrStringmap_destroy(sd->map, 0);
	free(sd->blob);
	sd->map = NULL;
	sd->blob = NULL;
}