/* This file is part of the YAZ toolkit.
 * Copyright (C) Index Data
 * See the file LICENSE for details.
 */
/**
 * \file charneg.c
 * \brief Implements Z39.50 Charset negotiation utilities
 *
 * Helper functions for Character Set and Language Negotiation - 3
 */
#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <yaz/otherinfo.h>
#include <yaz/z-charneg.h>
#include <yaz/charneg.h>
#include <yaz/yaz-util.h>
#include <yaz/oid_db.h>
#include <yaz/snprintf.h>

static Z_External* z_ext_record2(ODR o, const char *buf)
{
    Z_External *p;
    int len = strlen(buf);

    if (!(p = (Z_External *)odr_malloc(o, sizeof(*p))))
        return 0;
    p->descriptor = 0;
    p->indirect_reference = 0;
    p->direct_reference = odr_oiddup(o, yaz_oid_negot_charset_id);
    p->which = Z_External_octet;
    p->u.octet_aligned =
        odr_create_Odr_oct(o, buf, len);
    return p;
}

static int get_form(const char *charset)
{
    int form = -1;

    if (!yaz_matchstr(charset, "UCS-2"))
        form = 2;
    if (!yaz_matchstr(charset, "UCS-4"))
        form = 4;
    if (!yaz_matchstr(charset, "UTF-16"))
        form = 5;
    if (!yaz_matchstr(charset, "UTF-8"))
        form = 8;

    return form;
}

static char *set_form(Odr_oid *encoding)
{
    static char *charset = 0;
    if ( oid_oidlen(encoding) != 6)
        return 0;
    if (encoding[5] == 2)
        charset = "UCS-2";
    if (encoding[5] == 4)
        charset = "UCS-4";
    if (encoding[5] == 5)
        charset = "UTF-16";
    if (encoding[5] == 8)
        charset = "UTF-8";
    return charset;
}

static Z_OriginProposal_0 *z_get_OriginProposal_0(ODR o, const char *charset)
{
    int form = get_form(charset);
    Z_OriginProposal_0 *p0 =
        (Z_OriginProposal_0*)odr_malloc(o, sizeof(*p0));

    memset(p0, 0, sizeof(*p0));

    if (form > 0)
    {   /* ISO 10646 (UNICODE) */
        char oidname[28];

        Z_Iso10646 *is = (Z_Iso10646 *) odr_malloc(o, sizeof(*is));
        p0->which = Z_OriginProposal_0_iso10646;
        p0->u.iso10646 = is;
        is->collections = 0;
        yaz_snprintf(oidname, sizeof(oidname), "1.0.10646.1.0.%d", form);
        is->encodingLevel = odr_getoidbystr(o, oidname);
    }
    else
    {   /* private ones */
        Z_PrivateCharacterSet *pc =
            (Z_PrivateCharacterSet *)odr_malloc(o, sizeof(*pc));

        memset(pc, 0, sizeof(*pc));

        p0->which = Z_OriginProposal_0_private;
        p0->u.zprivate = pc;

        pc->which = Z_PrivateCharacterSet_externallySpecified;
        pc->u.externallySpecified = z_ext_record2(o, charset);
    }
    return p0;
}

static Z_OriginProposal *z_get_OriginProposal(
    ODR o, const char **charsets, int num_charsets,
    const char **langs, int num_langs, int selected)
{
    int i;
    Z_OriginProposal *p = (Z_OriginProposal *) odr_malloc(o, sizeof(*p));

    memset(p, 0, sizeof(*p));

    p->recordsInSelectedCharSets = (bool_t *)odr_malloc(o, sizeof(bool_t));
    *p->recordsInSelectedCharSets = (selected) ? 1 : 0;

    if (charsets && num_charsets)
    {
        p->num_proposedCharSets = num_charsets;
        p->proposedCharSets =
            (Z_OriginProposal_0**)
            odr_malloc(o, num_charsets*sizeof(Z_OriginProposal_0*));

        for (i = 0; i < num_charsets; i++)
            p->proposedCharSets[i] =
                z_get_OriginProposal_0(o, charsets[i]);
    }
    if (langs && num_langs)
    {
        p->num_proposedlanguages = num_langs;
        p->proposedlanguages =
            (char **) odr_malloc(o, num_langs*sizeof(char *));

        for (i = 0; i < num_langs; i++)
            p->proposedlanguages[i] = (char *)langs[i];
    }
    return p;
}

static Z_CharSetandLanguageNegotiation *z_get_CharSetandLanguageNegotiation(
    ODR o)
{
    Z_CharSetandLanguageNegotiation *p =
        (Z_CharSetandLanguageNegotiation *) odr_malloc(o, sizeof(*p));

    memset(p, 0, sizeof(*p));

    return p;
}

/* Create EXTERNAL for negotation proposal. Client side */
Z_External *yaz_set_proposal_charneg(ODR o,
                                     const char **charsets, int num_charsets,
                                     const char **langs, int num_langs,
                                     int selected)
{
    Z_External *p = (Z_External *)odr_malloc(o, sizeof(*p));

    p->descriptor = 0;
    p->indirect_reference = 0;

    p->direct_reference = odr_oiddup(o, yaz_oid_negot_charset_3);

    p->which = Z_External_charSetandLanguageNegotiation;
    p->u.charNeg3 = z_get_CharSetandLanguageNegotiation(o);
    p->u.charNeg3->which = Z_CharSetandLanguageNegotiation_proposal;
    p->u.charNeg3->u.proposal =
        z_get_OriginProposal(o, charsets, num_charsets,
                             langs, num_langs, selected);

    return p;
}

Z_External *yaz_set_proposal_charneg_list(ODR o,
                                          const char *delim,
                                          const char *charset_list,
                                          const char *lang_list,
                                          int selected)
{
    char **charsets_addresses = 0;
    char **langs_addresses = 0;
    int charsets_count = 0;
    int langs_count = 0;

    if (charset_list)
        nmem_strsplit(odr_getmem(o), delim, charset_list,
                      &charsets_addresses, &charsets_count);
    if (lang_list)
        nmem_strsplit(odr_getmem(o), delim, lang_list,
                      &langs_addresses, &langs_count);
    return yaz_set_proposal_charneg(o,
                                    (const char **) charsets_addresses,
                                    charsets_count,
                                    (const char **) langs_addresses,
                                    langs_count,
                                    selected);
}


/* used by yaz_set_response_charneg */
static Z_TargetResponse *z_get_TargetResponse(ODR o, const char *charset,
                                              const char *lang, int selected)
{
    Z_TargetResponse *p = (Z_TargetResponse *) odr_malloc(o, sizeof(*p));
    int form = get_form(charset);

    memset(p, 0, sizeof(*p));
    if (form > 0)
    {
        char oidname[28];

        Z_Iso10646 *is = (Z_Iso10646 *) odr_malloc (o, sizeof(*is));
        p->which = Z_TargetResponse_iso10646;
        p->u.iso10646 = is;
        is->collections = 0;
        yaz_snprintf(oidname, sizeof oidname, "1.0.10646.1.0.%d", form);
        is->encodingLevel = odr_getoidbystr (o, oidname);
    }
    else
    {
        Z_PrivateCharacterSet *pc =
            (Z_PrivateCharacterSet *)odr_malloc(o, sizeof(*pc));

        memset(pc, 0, sizeof(*pc));

        p->which = Z_TargetResponse_private;
        p->u.zprivate = pc;

        pc->which = Z_PrivateCharacterSet_externallySpecified;
        pc->u.externallySpecified =
            z_ext_record2(o, charset);
    }
    p->recordsInSelectedCharSets = (bool_t *)odr_malloc(o, sizeof(bool_t));
    *p->recordsInSelectedCharSets = (selected) ? 1 : 0;

    p->selectedLanguage = lang ? (char *) odr_strdup(o, lang) : 0;
    return p;
}

/* Create charset response. Server side */
Z_External *yaz_set_response_charneg(ODR o, const char *charset,
                                     const char *lang, int selected)
{
    Z_External *p = (Z_External *)odr_malloc(o, sizeof(*p));

    p->descriptor = 0;
    p->indirect_reference = 0;

    p->direct_reference = odr_oiddup(o, yaz_oid_negot_charset_3);

    p->which = Z_External_charSetandLanguageNegotiation;
    p->u.charNeg3 = z_get_CharSetandLanguageNegotiation(o);
    p->u.charNeg3->which = Z_CharSetandLanguageNegotiation_response;
    p->u.charNeg3->u.response = z_get_TargetResponse(o, charset, lang, selected);

    return p;
}

/* Get negotiation from OtherInformation. Client&Server side */
Z_CharSetandLanguageNegotiation *yaz_get_charneg_record(Z_OtherInformation *p)
{
    int i;

    if (!p)
        return 0;

    for (i = 0; i < p->num_elements; i++)
    {
        Z_External *pext;
        if ((p->list[i]->which == Z_OtherInfo_externallyDefinedInfo) &&
            (pext = p->list[i]->information.externallyDefinedInfo))
        {
            if (!oid_oidcmp(pext->direct_reference, yaz_oid_negot_charset_3)
                && pext->which == Z_External_charSetandLanguageNegotiation)
            {
                return pext->u.charNeg3;
            }
        }
    }
    return 0;
}

/* Delete negotiation from OtherInformation. Client&Server side */
int yaz_del_charneg_record(Z_OtherInformation **p)
{
    int i;

    if (!*p)
        return 0;

    for (i = 0; i < (*p)->num_elements; i++)
    {
        Z_External *pext;
        if (((*p)->list[i]->which == Z_OtherInfo_externallyDefinedInfo) &&
            (pext = (*p)->list[i]->information.externallyDefinedInfo))
        {
            if (!oid_oidcmp(pext->direct_reference, yaz_oid_negot_charset_3)
                && pext->which == Z_External_charSetandLanguageNegotiation)
            {
                if ((*p)->num_elements <= 1)
                    *p = 0;
                else
                {
                    --((*p)->num_elements);
                    for (; i < (*p)->num_elements; i++)
                        (*p)->list[i] = (*p)->list[i+1];
                }
                return 1;
            }
        }
    }
    return 0;
}


/* Get charsets, langs, selected from negotiation.. Server side */
void yaz_get_proposal_charneg(NMEM mem, Z_CharSetandLanguageNegotiation *p,
                              char ***charsets, int *num_charsets,
                              char ***langs, int *num_langs, int *selected)
{
    int i;
    Z_OriginProposal *pro = p->u.proposal;

    if (num_charsets && charsets)
    {
        if (pro->num_proposedCharSets)
        {
            *num_charsets = pro->num_proposedCharSets;

            (*charsets) = (char **)
                nmem_malloc(mem, pro->num_proposedCharSets * sizeof(char *));

            for (i = 0; i < pro->num_proposedCharSets; i++)
            {
                (*charsets)[i] = 0;

                if (pro->proposedCharSets[i]->which ==
                    Z_OriginProposal_0_private &&
                    pro->proposedCharSets[i]->u.zprivate->which ==
                    Z_PrivateCharacterSet_externallySpecified)
                {
                    Z_External *pext =
                        pro->proposedCharSets[i]->u.zprivate->u.externallySpecified;

                    if (pext->which == Z_External_octet)
                    {
                        (*charsets)[i] = (char *)
                            nmem_malloc(mem, (1+pext->u.octet_aligned->len) *
                                        sizeof(char));

                        memcpy((*charsets)[i], pext->u.octet_aligned->buf,
                               pext->u.octet_aligned->len);
                        (*charsets)[i][pext->u.octet_aligned->len] = 0;
                    }
                }
                else if (pro->proposedCharSets[i]->which ==
                         Z_OriginProposal_0_iso10646)
                    (*charsets)[i] = set_form(
                        pro->proposedCharSets[i]->u.iso10646->encodingLevel);
            }
        }
        else
            *num_charsets = 0;
    }

    if (langs && num_langs)
    {
        if (pro->num_proposedlanguages)
        {
            *num_langs = pro->num_proposedlanguages;

            (*langs) = (char **)
                nmem_malloc(mem, pro->num_proposedlanguages * sizeof(char *));

            for (i = 0; i < pro->num_proposedlanguages; i++)
                (*langs)[i] = nmem_strdup(mem, pro->proposedlanguages[i]);
        }
        else
            *num_langs = 0;
    }

    if (pro->recordsInSelectedCharSets && selected)
        *selected = *pro->recordsInSelectedCharSets;
}

/* Return charset, lang, selected from negotiation.. Client side */
void yaz_get_response_charneg(NMEM mem, Z_CharSetandLanguageNegotiation *p,
                              char **charset, char **lang, int *selected)
{
    Z_TargetResponse *res = p->u.response;

    if (charset && res->which == Z_TargetResponse_private &&
        res->u.zprivate->which == Z_PrivateCharacterSet_externallySpecified)
    {
        Z_External *pext = res->u.zprivate->u.externallySpecified;

        if (pext->which == Z_External_octet)
        {
            *charset = (char *)
                nmem_malloc(mem, (1+pext->u.octet_aligned->len)*sizeof(char));
            memcpy(*charset, pext->u.octet_aligned->buf,
                   pext->u.octet_aligned->len);
            (*charset)[pext->u.octet_aligned->len] = 0;
        }
    }
    if (charset && res->which == Z_TargetResponse_iso10646)
        *charset = set_form(res->u.iso10646->encodingLevel);
    if (lang && res->selectedLanguage)
        *lang = nmem_strdup(mem, res->selectedLanguage);

    if (selected && res->recordsInSelectedCharSets)
        *selected = *res->recordsInSelectedCharSets;
}
/*
 * Local variables:
 * c-basic-offset: 4
 * c-file-style: "Stroustrup"
 * indent-tabs-mode: nil
 * End:
 * vim: shiftwidth=4 tabstop=8 expandtab
 */

