/*----------------------------------------------------------------------
 * Module name:  my_iconv
 * Author name:  Arkadiusz Firus
 * Create date:  07 Sep 08
 * Purpose:    iconv handles
 *----------------------------------------------------------------------
 Changes:
 * 04 Jan 10, daved@physiol.usyd.edu.au: use path specfied with -P to
 *    load charmap file(s)
 * 07 Oct 11, jf@dockes.org, major changes to unicode translations
 *--------------------------------------------------------------------*/

#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <errno.h>

#include "malloc.h"
#include "my_iconv.h"
#include "util.h"
#include "unicode.h"
#include "path.h"

extern int verbose_mode;

/* Convert from charmap file entry to charmap table one.
   1st byte in table entry is code length
*/
static char *
get_code_str(FILE *f, iconv_t desc)
{
    char *icp, *ocp0, *ocp;
    size_t ibytes, obytes;

    char *utf8 = get_unicode_utf8(f);
    if (utf8 == NULL || *utf8 == 0)
    {
        my_free(utf8);
        return NULL;
    }

    obytes = 10;
    ibytes = strlen(utf8);
    char *  obuf = static_cast<char*>(malloc(obytes));
    if (obuf == NULL)
    {
        my_free(utf8);
        return NULL;
    }
    icp = utf8;
    ocp0 = ocp = obuf + 1;
    if (iconv(desc, &icp, &ibytes, &ocp, &obytes) == -1)
    {
        /*        fprintf(stderr, "unrtf: my_iconv: iconv error\n");*/
        my_free(utf8);
        return NULL;
    }

    // Set 1st byte to length
    obuf[0] = ocp - ocp0;
    my_free(utf8);
    return obuf;
}

my_iconv_t
my_iconv_open(const char *tocode, const char *fromcode)
{
    FILE *f;
    my_iconv_t cd = MY_ICONV_T_CLEAR;
    int c, i;
    /*      fprintf(stderr, "my_iconv_open: from %s to %s\n", fromcode, tocode);*/
    if ((cd.desc = iconv_open(tocode, fromcode)) == (iconv_t) - 1)
    {
        char *path = search_in_path(fromcode, "charmap");
        if (path == NULL)
        {
            return cd;
        }
        if ((f = fopen(path, "r")) == NULL && verbose_mode)
        {
            fprintf(stderr, "failed to open charmap file %s\n", path);
        }

        if (f != NULL)
        {
            /* Open iconv utf8->tocode conversion */
            iconv_t desc;
            if ((desc = iconv_open(tocode, "UTF-8")) == (iconv_t) - 1)
            {
                fclose(f);
                return cd;
            }
            cd.char_table = (char **)my_malloc(char_table_size *
                                               sizeof(char *));
            c = fgetc(f);

            for (i = 0; i < char_table_size && c != EOF; i++)
            {
                if (c == '<')
                {
                    cd.char_table[i] = get_code_str(f, desc);
                }
                leave_line(f);//read up to including \n or eof
                c = fgetc(f);
            }
            iconv_close(desc);
            fclose(f);
        }

        my_free(path);
    }

    return cd;
}

size_t
my_iconv(my_iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
{
    int c, i;
    size_t result = 0;
    **outbuf = 0;
    if (cd.desc == (iconv_t) - 1)
    {
        if (cd.char_table != NULL)
        {
            while (*inbytesleft > 0 && *outbytesleft > 0)
            {
                c = **(unsigned char **)inbuf;
                if (cd.char_table[c] != NULL)
                {
                    for (i = 0; i < cd.char_table[c][0] && *outbytesleft > 0; i++)
                    {
                        **outbuf = cd.char_table[c][i + 1];
                        (*outbytesleft)--;
                        (*outbuf)++;
                    }
                }
                else
                {
                    /*                    fprintf(stderr, "my_iconv: no conversion for 0x%x\n",
                              (unsigned)c);*/
                    errno = EILSEQ;
                    return (size_t) - 1;
                }

                (*inbuf)++;
                (*inbytesleft)--;
                result++;
            }

            if (*outbytesleft == 0 && *inbytesleft > 0)
            {
                errno = E2BIG;
                result = (size_t) - 1;
            }
        }
    }
    else
    {
        result = iconv(cd.desc, inbuf, inbytesleft, outbuf, outbytesleft);
    }

    return result;
}

my_iconv_t
my_iconv_close(my_iconv_t cd)
{
    int i;

    if (cd.char_table != NULL)
    {
        for (i = 0; i < char_table_size; i++)
        {
            if (cd.char_table[i] != NULL)
            {
                my_free(cd.char_table[i]);
            }
        }

        my_free(reinterpret_cast<char*>(cd.char_table));
        cd.char_table = NULL;
    }

    if (cd.desc != (iconv_t) - 1)
    {
        iconv_close(cd.desc);
        cd.desc = (iconv_t) - 1;
    }

    return cd;
}

int
my_iconv_is_valid(my_iconv_t cd)
{
    if (cd.desc != (iconv_t) - 1 || cd.char_table != NULL)
    {
        return 1;
    }

    return 0;
}

void
my_iconv_t_make_invalid(my_iconv_t *cd)
{
    cd->desc = (iconv_t) - 1;
    cd->char_table = NULL;
}

