#include "base_util.h"
#include "translations.h"
#include "translations_txt.h"
#include "str_util.h"

/*
This code relies on the following variables that must be defined in a
separate file (translations_txt.h and translations_txt.c).
The idea is that those files are automatically generated
by a script from translations file.

// number of languages we support
int g_transLangsCount;

// array of language names so that g_transLangs[i] is a name of
// language. i is 0..g_transLangsCount-1
const char **g_transLangs;

// total number of translated strings
int g_transTranslationsCount;

// array of translated strings.
// it has g_transLangsCount * g_translationsCount elements
// (for simplicity). Translation i for language n is at position
// (n * g_transTranslationsCount) + i
const char **g_transTranslations;
*/

// numeric index of the current language. 0 ... g_transLangsCount-1
static int currLangIdx = 0;

/* 'data'/'data_len' is a text describing all texts we translate.
   It builds data structures need for quick lookup of translations
   as well as a list of available languages.
   It returns a list of available languages.
   The list is not valid after a call to Translations_FreeData.
   The function must be called before any other function in this module.
   It can be called multiple times. This is to make debugging of translations
   easier by allowing re-loading translation file at runtime.
   */
bool Translations_FromData(const char* langs, const char* data, size_t data_len)
{
    assert(0); // TODO: implement me
    return false;
}

bool Translations_SetCurrentLanguage(const char* lang)
{
    for (int i=0; i < g_transLangsCount; i++) {
        if (str_eq(lang, g_transLangs[i])) {
            currLangIdx = i;
            return true;
        }
    }
    return false;
}

const char* Translatations_GetTranslation(const char* txt)
{
    // perf shortcut: don't bother translating if we use default lanuage
    if (0 == currLangIdx)
        return txt;
    for (int i=0; i < g_transTranslationsCount; i++) {
        // TODO: translations are sorted so can use binary search
        const char *tmp =  g_transTranslations[i];
        int cmp_res = strcmp(txt, tmp);
        if (0 == cmp_res) {
            tmp = g_transTranslations[(currLangIdx * g_transTranslationsCount) + i];
            if (NULL == tmp)
                return txt;
            return tmp;
        } else if (cmp_res < 0) {
            assert(0);  // bad - didn't find a translation
            return txt;
        }
    }
    assert(0); // bad - didn't find a translation
    return txt;
}

static WCHAR* lastTxtCached = NULL;

#define UTF8_END   -1
#define UTF8_ERROR -2

typedef struct json_utf8_decode
{
    int the_index;
    const char *the_input;
    int the_length;
    int the_char;
    int the_byte;
} json_utf8_decode;


void
utf8_decode_init(json_utf8_decode *utf8, const char *p, int length)
{
    utf8->the_index = 0;
    utf8->the_input = p;
    utf8->the_length = length;
    utf8->the_char = 0;
    utf8->the_byte = 0;
}

static int
get(json_utf8_decode *utf8)
{
    int c;
    if (utf8->the_index >= utf8->the_length) {
        return UTF8_END;
    }
    c = utf8->the_input[utf8->the_index] & 0xFF;
    utf8->the_index += 1;
    return c;
}

static int
cont(json_utf8_decode *utf8)
{
    int c = get(utf8);
    return ((c & 0xC0) == 0x80) ? (c & 0x3F) : UTF8_ERROR;
}

int
utf8_decode_next(json_utf8_decode *utf8)
{
    int c;  /* the first byte of the character */
    int r;  /* the result */

    if (utf8->the_index >= utf8->the_length) {
        return utf8->the_index == utf8->the_length ? UTF8_END : UTF8_ERROR;
    }
    utf8->the_byte = utf8->the_index;
    utf8->the_char += 1;
    c = get(utf8);
/*
    Zero continuation (0 to 127)
*/
    if ((c & 0x80) == 0) {
        return c;
    }
/*
    One contination (128 to 2047)
*/
    if ((c & 0xE0) == 0xC0) {
        int c1 = cont(utf8);
        if (c1 < 0) {
            return UTF8_ERROR;
        }
        r = ((c & 0x1F) << 6) | c1;
        return r >= 128 ? r : UTF8_ERROR;
    }
/*
    Two continuation (2048 to 55295 and 57344 to 65535)
*/
    if ((c & 0xF0) == 0xE0) {
        int c1 = cont(utf8);
        int c2 = cont(utf8);
        if (c1 < 0 || c2 < 0) {
            return UTF8_ERROR;
        }
        r = ((c & 0x0F) << 12) | (c1 << 6) | c2;
        return r >= 2048 && (r < 55296 || r > 57343) ? r : UTF8_ERROR;
    }
/*
    Three continuation (65536 to 1114111)
*/
    if ((c & 0xF1) == 0xF0) {
        int c1 = cont(utf8);
        int c2 = cont(utf8);
        int c3 = cont(utf8);
        if (c1 < 0 || c2 < 0 || c3 < 0) {
            return UTF8_ERROR;
        }
        r = ((c & 0x0F) << 18) | (c1 << 12) | (c2 << 6) | c3;
        return r >= 65536 && r <= 1114111 ? r : UTF8_ERROR;
    }
    return UTF8_ERROR;
}

static int json_utf8_to_utf16(unsigned short *w, const char *p, int length)
{
    int c;
    int the_index = 0;
    json_utf8_decode utf8;

    utf8_decode_init(&utf8, p, length);
    for (;;) {
        c = utf8_decode_next(&utf8);
        if (c < 0) {
            return UTF8_END ? the_index : UTF8_ERROR;
        }
        if (c < 0x10000) {
            w[the_index] = (unsigned short)c;
            the_index += 1;
        } else {
            c &= 0xFFFF;
            w[the_index] = (unsigned short)(0xD800 | (c >> 10));
            the_index += 1;
            w[the_index] = (unsigned short)(0xDC00 | (c & 0x3FF));
            the_index += 1;
        }
    }
}

WCHAR* utf8_to_utf16(const char *txt)
{
    // TODO: this is not correct, need real conversion
    size_t len = strlen(txt);
    WCHAR* wstr = (WCHAR*)zmalloc((len+1) * sizeof(WCHAR));
    if (!wstr) return NULL;
    int res = json_utf8_to_utf16((unsigned short*)wstr, txt, len);
    assert(UTF8_ERROR != res);
    return wstr;
}

// TODO: this is not thread-safe. lastTxtCached should be per-thread
const WCHAR* Translatations_GetTranslationW(const char* txt)
{
    txt = Translatations_GetTranslation(txt);
    if (!txt) return NULL;
    if (lastTxtCached)
        free(lastTxtCached);
    lastTxtCached = utf8_to_utf16(txt);
    return (const WCHAR*)lastTxtCached;
}

void Translations_FreeData()
{
    // TODO: will be more when we implement Translations_FromData
    free(lastTxtCached);
    lastTxtCached = NULL;
}

