#include "fliconv.h"
#include <errno.h>
#include "std.h"
#include "lpc_incl.h"
#include "comm.h"
#ifdef USE_ICONV
struct translation *head;
static struct translation *find_translator(const char *encoding){
    struct translation *cur = head;
    while(cur){
        if(!strcmp(cur->name, encoding))
            break;
        cur = cur->next;
    }
    return cur;
}
struct translation *get_translator(const char *encoding){
    struct translation *ret = find_translator(encoding);
    if(ret)
        return ret;
    ret = (struct translation *)MALLOC(sizeof(struct translation));
    char *name = (char *)MALLOC(strlen(encoding)+18+1);
    strcpy(name, encoding);
#ifdef linux
    strcat(name, "//TRANSLIT//IGNORE");
#endif
    ret->name = name;
    ret->incoming = iconv_open("UTF-8", encoding);
    ret->outgoing = iconv_open(name, "UTF-8");
    ret->next = 0;
    if(ret->incoming == (iconv_t)-1 || ret->outgoing == (iconv_t)-1){
        FREE(name);
        FREE(ret);
        return 0;
    }
    name[strlen(encoding)] = 0;
    if(!head)
        head = ret;
    else {
        struct translation *cur = head;
        while(cur->next)
            cur = cur->next;
        cur->next = ret;
    }
    return ret;
}
char *translate(iconv_t tr, const char *mes, int inlen, int *outlen){
    size_t len = inlen;
    size_t len2;
    unsigned char *tmp = (unsigned char *)mes;
    static char *res = 0;
    static size_t reslen = 0;
    char *tmp2;
    if(!res){
        res = (char *)MALLOC(1);
        reslen = 1;
    }
    tmp2 = res;
    len2 = reslen;
    while(len){
        iconv(tr, (char **)&tmp, &len, &tmp2, &len2);
#ifdef PACKAGE_DWLIB
        if(len > 1 && tmp[0] == 0xff && tmp[1] == 0xf9){
            len -=2;
            tmp +=2;
#else
            if(0){
#endif
            } else {
                if(E2BIG == errno){
                    errno = 0;
                    tmp = (unsigned char *)mes;
                    len = strlen(mes)+1;
                    FREE(res);
                    reslen *= 2;
                    res = (char *)MALLOC(reslen);
                    tmp2 = res;
                    len2 = reslen;
                    continue;
                }
                tmp2[0] = 0;
                *outlen = reslen - len2;
                return res;
            }
        }
        *outlen = reslen - len2;
        return res;
    }
#else
    char *translate(iconv_t tr, const char *mes, int inlen, int *outlen){
        *outlen = inlen;
        return (char *)mes;
    }
#endif
    char *translate_easy(iconv_t tr, char *mes){
        int dummy;
        char *res = translate(tr, mes, strlen(mes)+1, &dummy);
        return res;
    }
#ifdef F_SET_ENCODING
    void f_set_encoding(){
        if(current_object->interactive){
            struct translation *newt = get_translator((char *)sp->u.string);
            if(newt){
                current_object->interactive->trans = newt;
                return;
            }
        }
        pop_stack();
        push_number(0);
    }
#endif
#ifdef F_TO_UTF8
    void f_to_utf8(){
        struct translation *newt = get_translator((char *)sp->u.string);
        pop_stack();
        if(!newt)
            error("unknown encoding");
        char *text = (char *)sp->u.string;
        char *translated = translate_easy(newt->incoming, text);
        pop_stack();
        copy_and_push_string(translated);
    }
#endif
#ifdef F_UTF8_TO
    void f_utf8_to(){
        struct translation *newt = get_translator((char *)sp->u.string);
        pop_stack();
        if(!newt)
            error("unknown encoding");
        char *text = (char *)sp->u.string;
        char *translated = translate_easy(newt->outgoing, text);
        pop_stack();
        copy_and_push_string(translated);
    }
#endif
#ifdef F_STR_TO_ARR
    void f_str_to_arr(){
        static struct translation *newt = 0;
        if(!newt){
            newt = get_translator("UTF-32");
            translate_easy(newt->outgoing, " ");
        }
        int len;
        int *trans = (int *)translate(newt->outgoing, sp->u.string, SVALUE_STRLEN(sp)+1, &len);
        len/=4;
        array_t *arr = allocate_array(len);
        while(len--)
            arr->item[len].u.number = trans[len];
        free_svalue(sp, "str_to_arr");
        put_array(arr);
    }
#endif
#ifdef F_ARR_TO_STR
    void f_arr_to_str(){
        static struct translation *newt = 0;
        if(!newt){
            newt = get_translator("UTF-32");
        }
        int len = sp->u.arr->size;
        int *in = (int *)MALLOC(sizeof(int)*(len+1));
        char *trans;
        in[len] = 0;
        while(len--)
            in[len] = sp->u.arr->item[len].u.number;
        trans = translate(newt->incoming, (char *)in, (sp->u.arr->size+1)*4, &len);
        FREE(in);
        pop_stack();
        copy_and_push_string(trans);
    }
#endif
#ifdef F_STRWIDTH
    void f_strwidth(){
        int len = SVALUE_STRLEN(sp);
        int width = 0;
        int i;
        for(i=0; i<len; i++)
            width += !(((sp->u.string[i]) & 0xc0) == 0x80);
        pop_stack();
        push_number(width);
    }
#endif