
/*
 *
 * UNICON - The Console Chinese & I18N
 * Copyright (c) 1999-2002
 *
 * This file is part of UNICON, a console Chinese & I18N
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * See the file COPYING directory of this archive
 * Author: see CREDITS
 */

#include <stdio.h>
#include <stdlib.h>
#include <xl_hzinput.h>
#include <string.h>
#include <xl_sysphrase.h>
#include <xl_phrase.h>
#include <xl_mfile.h>

/* Associate Phrase Support */
#define  IMM_MAX_ASSOCIATE_PHRASE          1024
#define  MAX_USHORT                        65535
#define  error                             printf 

void LoadPhrase (HzInputTable_T *pClient, int phrno, char *tt)
{
    TL_GetPhrase (pClient->pSysPhrase, phrno, tt);
}

long GetAssociatePhraseIndex (HzInputTable_T *pClient, 
                              int index, u_long *nPhrase)
{
    int n, i;
    ITEM *q;

    AssociatePhrase *p = pClient->cur_table->pAssociatePhrase;
    if (index < 0)
        return 0;
    n = index / IMM_MAX_ASSOCIATE_PHRASE;
    i = index % IMM_MAX_ASSOCIATE_PHRASE;
    if (p[n].total == 0 || i > p[n].total || i < 0)
        return 0;
    q = p[n].pPhrase[i];
    *nPhrase =  q->nPhrase;
    return 1;
}

static int CaculateAssociateIndex (u_char *p)
{
    int index = (int) p[0] * 256 + p[1];
    return index;
}

static void CaculatePhraseKeys (HzInputTable_T *pClient, u_char *szCode,
                                u_long *pKey1, u_long *pKey2)
{
    int len, i;
    u_long key1, key2;
    int k;

    len = strlen(szCode);
    if (len > pClient->cur_table->MaxPress)
         pClient->cur_table->MaxPress = len;

    key1 = key2 = 0;
    for (i = 0; i < len; i++)
    {
        if(i<5)
        {
            k = pClient->cur_table->KeyMap[szCode[i]];
            key1 |= k << (24-i*6);
        }
        else
        {
            k = pClient->cur_table->KeyMap[szCode[i]];
            key2 |= k << (24-(i-5)*6);
        }
    }
    *pKey1 = key1;
    *pKey2 = key2;
}

static void InitPhrase (HzInputTable_T *pClient, 
                        ITEM *p, long nPhrase, u_char *szCode)
{
    u_long key1, key2;
    CaculatePhraseKeys (pClient, szCode, &key1, &key2);
    memcpy(&p->key1,&key1,4);
    memcpy(&p->key2,&key2,4);
    p->nPhrase = nPhrase;
}

static int AddToAssociatePhrase (HzInputTable_T *pClient,
                                 u_char *szPhrase, ITEM *pItem)
{
    AssociatePhrase *p = pClient->cur_table->pAssociatePhrase;
    int index = CaculateAssociateIndex (szPhrase);

    p[index].total++;
    if (p[index].total == 1)
    {
        p[index].pPhrase = malloc (sizeof (ITEM));
    }
    else
    {
        p[index].pPhrase = realloc (p[index].pPhrase,
                           sizeof (ITEM) * p[index].total);
    }
    p[index].pPhrase[p[index].total-1] = pItem;
    return 1;
}

int IsThisPhraseExist (HzInputTable_T *p, char *szCode, char *szPhrase)
{
    u_long key1, key2;
    u_long start, end;
    char buf[256];
    u_long i;

    CaculatePhraseKeys (p, szCode, &key1, &key2);
    start = p->cur_table->KeyIndex[(int) szCode[0]];
    end   = p->cur_table->KeyIndex[(int) szCode[0]+1];

    for (i = 0; i < p->cur_table->TotalChar; i++)
    {
        if (p->cur_table->item[i].key1 == key1 && 
            p->cur_table->item[i].key2 == key2)
        {
            TL_GetPhrase (p->pSysPhrase, p->cur_table->item[i].nPhrase, buf);
            if (strcmp (buf, szPhrase) == 0)
                return 1;
        }
    }
    return 0;
}

int AppendPhrase (HzInputTable_T *p, char *szCode, char *szPhrase)
{
    long n = TL_AppendPhrase (p->pSysPhrase, szPhrase);
    if (IsThisPhraseExist (p, szCode, szPhrase) == 1)
        return 1;
    p->cur_table->TotalChar ++;
    p->cur_table->item = realloc 
            (p->cur_table->item, sizeof(ITEM) * p->cur_table->TotalChar);
    InitPhrase (p, &p->cur_table->item[p->cur_table->TotalChar-1], n, szCode);
    AddToAssociatePhrase 
            (p, szPhrase, &p->cur_table->item[p->cur_table->TotalChar-1]);
    ResortPhraseFreq (p);
    return 1;
}

int DeletePhrase (HzInputTable_T *p, char *szPhrase)
{
    return 1;
}

int AdjustPhraseOrder (HzInputTable_T *pClient, long nPhrase)
{
    int n = TL_AdjustPhraseOrder (pClient->pSysPhrase, nPhrase);
/*
    int ResortPhraseFreq (HzInputTable_T *pClient);

    ResortPhraseFreq (pClient);
*/
    return n;
}

int ResortPhraseFreq (HzInputTable_T *pClient)
{
    hz_input_table *p = pClient->cur_table;
    u_short CharDef[64];
    int i, index = p->TotalChar;
    int TotalKeyNum = p->TotalKey;

    SortPhraseItem (pClient->pSysPhrase, pClient->cur_table);
    bzero (CharDef, sizeof(CharDef));
    bzero (p->KeyIndex, sizeof (unsigned int) * TotalKeyNum);
    for (i = 0; i < index; i++)
    {
        int kk = p->item[i].key1 >> 24 & 0x3f;
        if (!CharDef[kk])
        {
            p->KeyIndex[kk]= (unsigned int) i;
            CharDef[kk]=1;
        }
    }

    p->KeyIndex[TotalKeyNum] = index;
    for(i=TotalKeyNum-1; i>0; i--)
    {
        if (!CharDef[i])
            p->KeyIndex[i] = p->KeyIndex[i+1];
    }
    return 1;
}

/* public Associate Phrase Interface */
int FindAssociateKey (HzInputTable_T *pClient, u_char *pStr)
{
    AssociatePhrase *p = pClient->cur_table->pAssociatePhrase;
    int index;

    index = CaculateAssociateIndex (pStr);
    if (p[index].total == 0)
    {
       pClient->StartKey = pClient->EndKey = 0;
       return 0;  /* no match */
    }
    pClient->StartKey = IMM_MAX_ASSOCIATE_PHRASE * index;
    pClient->EndKey = IMM_MAX_ASSOCIATE_PHRASE * index + p[index].total;
    return 1;
}

/***************************************************************************
 *          The File format of Turbo Linux Input Method
 *
 *    symbol       len                              meaning
 *    cur_table    sizeof (hz_input_table)          the same as CCE
 *    n            sizeof (long)                    total phrase
 *    pItem        n * sizeof (ITEM)                read Item
 *
 **************************************************************************/
#include <assert.h>
hz_input_table* LoadInputMethod(char *filename)
{
    int  nread, i, j;
    FILE *fd;
    hz_input_table *cur_table;
    long offset, end, handle;

    cur_table = malloc (sizeof(hz_input_table));
    if (cur_table == NULL)
        error("Out of memory in LoadInputMethod");
    fd = fopen(filename, "rb");
    if (fd == NULL)
    {
        error("Cannot open input method %s", filename);
        free(cur_table);
        return NULL;
    }
    nread = fread(cur_table, sizeof(hz_input_table), 1, fd);
    if (nread != 1)
    {
        error("Cannot read file header %s", filename);
        return NULL;
    }
    if( strcmp(MAGIC_NUMBER, cur_table->magic_number) )
    {
        printf("is not a valid tab file\n\n");
        return NULL;
    }
    cur_table->item = (ITEM *)malloc(sizeof(ITEM ) * cur_table->TotalChar);
    //warn("Totalchar=%d\n",cur_table->TotalChar);
    if ( cur_table->item == NULL )
    {
        error("Gosh, cannot malloc enough memory");
        return NULL;
    }
    assert (fread(cur_table->item, sizeof(ITEM) , cur_table->TotalChar, fd)
                   == cur_table->TotalChar);

    cur_table->pAssociatePhrase = 
           (AssociatePhrase *)malloc(sizeof(AssociatePhrase) * MAX_USHORT);
    if (cur_table->pAssociatePhrase == NULL)
    {
        error("Gosh, cannot malloc enough memory");
        return NULL;
    }
    memset (cur_table->pAssociatePhrase, 0, 
            sizeof(AssociatePhrase) * MAX_USHORT);

    offset = ftell (fd);
    fseek (fd, 0L, SEEK_END);
    end = ftell (fd);
    // assert (0);
    handle = openMemFile (fd, offset, end - offset);
    for (i = 0; i < cur_table->TotalAssociatePhrase; i++) 
    {
        u_short n;
        long  nPhrase[1024];
        // fread (&n, sizeof (u_short), 1, fd);
        readMemFile (handle, sizeof (u_short), (void *) &n);

        //fread (&cur_table->pAssociatePhrase[n], 
        //       sizeof (AssociatePhrase), 1, fd);
        readMemFile (handle, 
                     sizeof (AssociatePhrase), 
                     (void *) &cur_table->pAssociatePhrase[n]);

        //fread (nPhrase, sizeof (long), 
        //       cur_table->pAssociatePhrase[n].total, fd);
        readMemFile (handle, 
                     sizeof (long) * cur_table->pAssociatePhrase[n].total,
                     (void *) nPhrase);

        cur_table->pAssociatePhrase[n].pPhrase = (ITEM **) malloc 
                   (sizeof (ITEM *) * cur_table->pAssociatePhrase[n].total);
        for (j = 0; j < cur_table->pAssociatePhrase[n].total; j++)
            cur_table->pAssociatePhrase[n].pPhrase[j] = 
                                            &cur_table->item[nPhrase[j]];
    }
    closeMemFile (handle);
    fclose (fd);
    // SortPhraseItem (cur_table);
    return cur_table;
}

void UnloadInputMethod (hz_input_table *p)
{
    int i;
    // printf ("Calling UnloadInputMethod \n");
    if (p == NULL)
        return;
    free (p->item);
    for (i = 0; i < MAX_USHORT; i++)
        if (p->pAssociatePhrase[i].pPhrase != NULL)
            free (p->pAssociatePhrase[i].pPhrase);
   free (p->pAssociatePhrase);
   free (p);
}

/***************************************************************************
 *          The File format of Turbo Linux Input Method
 *
 *    symbol       len                              meaning
 *    cur_table    sizeof (hz_input_table)          the same as CCE
 *    n            sizeof (long)                    total phrase
 *    pItem        n * sizeof (ITEM)                read Item
 *
 **************************************************************************/
int SaveLoadInputMethod (hz_input_table *cur_table, char *filename)
{
    int  nread, i, j;
    FILE *fd;

    fd = fopen(filename, "wb");
    if (fd == NULL)
    {
        error("Cannot open input method %s", filename);
        fclose(fd);
        return 0;
    }
    nread = fwrite (cur_table, sizeof(hz_input_table), 1, fd);
    if (nread != 1)
    {
        error("Cannot read file header %s", filename);
        return 0;
    }
    if (strcmp(MAGIC_NUMBER, cur_table->magic_number))
    {
        printf("is not a valid tab file\n\n");
        return 0;
    }
    fwrite (cur_table->item, sizeof(ITEM) , cur_table->TotalChar, fd);

    for (i = 0; i < MAX_USHORT; i++)
    {
        u_short m = i;
        if (cur_table->pAssociatePhrase[i].total == 0)
            continue;
        fwrite (&m, sizeof (u_short), 1, fd);
        fwrite (&cur_table->pAssociatePhrase[i],
                             sizeof (AssociatePhrase), 1, fd);
        for (j = 0; j < cur_table->pAssociatePhrase[i].total; j++)
            fwrite (&cur_table->pAssociatePhrase[i].pPhrase[j]->nPhrase, 
                    sizeof (long), 1, fd);
    }
    fclose (fd);
    return 1;
}

int DumpLoadInputMethod (HzInputTable_T *p, char *filename)
{
    int  i;
    FILE *fp;
    TL_SysPhrase_T *q = p->pSysPhrase; 
    hz_input_table *cur_table = p->cur_table;

    fp = fopen(filename, "wt");
    if (fp == NULL)
    {
        error("Cannot open input method %s", filename);
        fclose(fp);
        return 0;
    }

    fprintf (fp, "Total = %ld\n", (u_long) cur_table->TotalChar);
    for (i = 0; i < (int) cur_table->TotalChar; i++)
    {
        char buf[256];
        TL_GetPhrase (q, cur_table->item[i].nPhrase, buf);
        fprintf (fp, "%s::0x%lx, 0x%lx \n",  buf,
                 cur_table->item[i].key1, cur_table->item[i].key2);
    }

    fclose (fp);
    return 1;
}

static TL_SysPhrase_T *pDefaultSysPhrase = NULL;
static int qcmp (void *t1, void *t2)
{
    u_long c1, c2;
    ITEM *a = (ITEM *) t1, 
         *b = (ITEM*) t2;

    if (a->key1 > b->key1) 
        return 1;
    if (a->key1 < b->key1) 
        return -1;
    if (a->key2 > b->key2) 
        return 1;
    if (a->key2 < b->key2) 
        return -1;
/*
{
    static char p1[256], p2[256];
    int len1, len2;
    TL_GetPhrase (a->nPhrase, p1);
    TL_GetPhrase (b->nPhrase, p2);
    len1 = strlen (p1);
    len2 = strlen (p2);
    if (len1 > len2)
       return 1;
    else if (len1 < len2)
       return -1;
}
*/
    TL_GetPhraseFreq (pDefaultSysPhrase, a->nPhrase, &c1);
    TL_GetPhraseFreq (pDefaultSysPhrase, b->nPhrase, &c2);

    if (c1 > c2)
       return 1;
    else if (c1 < c2)
       return -1;
    return 0;
}

void SortPhraseItem (TL_SysPhrase_T *q, hz_input_table *cur_table)
{
    ITEM *p = cur_table->item;
    int total = cur_table->TotalChar;
    pDefaultSysPhrase = q;
    qsort (p, total,  sizeof(ITEM), qcmp);
}

