
/*
 *
 * 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)
{
	//fprintf ( stderr, "Here, from xl_phrase.c : 42 LoadPhrase()\n" );
    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)
        {
			fprintf ( stderr, "Here, from xl_phrase.c : 144 IsThisPhraseExist()\n" );
            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 short) * TotalKeyNum);
    for (i = 0; i < index; i++)
    {
        int kk = p->item[i].key1 >> 24 & 0x3f;
        if (!CharDef[kk])
        {
            p->KeyIndex[kk]= (u_short) 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;
	unsigned long long mem_alocd = 0;

    cur_table = malloc (sizeof (hz_input_table));
	mem_alocd += (sizeof (hz_input_table));
    if (cur_table == NULL)
	{
        error ("Out of memory in LoadInputMethod");
		return NULL;
	}

    fd = fopen (filename, "rb");
    if (fd == NULL)
    {
        printf ("Cannot open input method %s", filename);
        free (cur_table);
        return NULL;
    }

    nread = fread (cur_table, sizeof (hz_input_table), 1, fd);
    if (nread != 1)
    {
        printf ("Cannot read file header %s", filename);
        free (cur_table);
		fclose (fd);
        return NULL;
    }

    if (strcmp (MAGIC_NUMBER, cur_table->magic_number))
    {
        printf ("is not a valid tab file\n\n");
        free (cur_table);
		fclose (fd);
        return NULL;
    }

    cur_table->item = (ITEM *)malloc (sizeof (ITEM) * cur_table->TotalChar);
	mem_alocd += (sizeof (ITEM) * cur_table->TotalChar);
    if (cur_table->item == NULL)
    {
        perror ("TL_hzinput.so >> LoadInputMethod ()");
        free (cur_table);
		fclose (fd);
        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);
	mem_alocd += (sizeof (AssociatePhrase) * MAX_USHORT);
    if (cur_table->pAssociatePhrase == NULL)
    {
        printf ("Gosh, cannot malloc enough memory");
		free (cur_table->item);
		free (cur_table);
		fclose (fd);
        return NULL;
    }
    memset (cur_table->pAssociatePhrase, 0, 
            sizeof (AssociatePhrase) * MAX_USHORT);

    offset = ftell (fd);
    fseek (fd, 0L, SEEK_END);
    end = ftell (fd);
	
    handle = openMemFile (fd, offset, end - offset);
    for (i = 0; i < cur_table->TotalAssociatePhrase; i++) 
    {
        u_short n;
        long  nPhrase[1024];

        readMemFile (handle, sizeof (u_short), (void *) &n);

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

        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);
		mem_alocd += sizeof (sizeof (ITEM*) * cur_table->pAssociatePhrase[n].total);

		if (cur_table->pAssociatePhrase[n].pPhrase == NULL)
		{
			// We can't use 'n' to tranverse the array, as 'n' is always +ve.
			unsigned short cnt;

			printf ("Warning: LoadInputModule (). Err in malloc for Assoc Phr");
			// Free all pPhrase from last created record to the first
			for (cnt = 0; cnt < MAX_USHORT; cnt ++)
			{
				// Free only if it is a pointer.
				if (cur_table->pAssociatePhrase[n].pPhrase)
					free (cur_table->pAssociatePhrase[n].pPhrase);
			}
			free (cur_table->pAssociatePhrase);
			free (cur_table->item);
			free (cur_table);
			closeMemFile (handle);
			fclose (fd);
			return NULL;
		}

        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);

//	printf ("-- LoadInputMethod () ---------------------------------\n");
//	printf ("Total requested memory : %lld\n", mem_alocd);
//	printf ("TotalChar is %d\n", cur_table->TotalChar);
//	printf ("TotalAssociatePhrase is %d\n", cur_table->TotalAssociatePhrase);
//	printf ("-------------------------------------------------------\n");

//	for (i = 0; i < cur_table->TotalChar; i++)
//		printf ("#%05d, key1: 0x%04lX key2: 0x%04lX nPhrase: %ld\n", 
//				i, cur_table->item[i].key1, cur_table->item[i].key2,
//				cur_table->item[i].nPhrase);
	
	return cur_table;
}

void UnloadInputMethod (hz_input_table *p)
{
	int i;

//	printf ("UnloadLoadInputMethod ()...\n");
	if (p == NULL)
		return;

//	printf ("-- UnloadInputMethod ()  -----------------------------\n");
//	printf (" TotalChar is %d\n", p->TotalChar);
//	printf (" TotalAssociatePhrase is %d\n", p->TotalAssociatePhrase);
//	printf ("------------------------------------------------------\n");

	for (i = 0; i < p->TotalAssociatePhrase; i++)
		if (p->pAssociatePhrase[i].pPhrase != NULL)
			free (p->pAssociatePhrase[i].pPhrase);
	free (p->pAssociatePhrase);
	free (p->item);
	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 file %s", filename);
		fclose(fd);
		return 0;
	}

	if (strcmp (MAGIC_NUMBER, cur_table->magic_number))
	{
		printf("is not a valid tab file\n\n");
		return 0;
	}

	nread = fwrite (cur_table, sizeof (hz_input_table), 1, fd);
	if (nread != 1)
	{
		error ("Cannot write file header %s", filename);
		return 0;
	}

    nread = fwrite (cur_table->item, sizeof (ITEM) , cur_table->TotalChar, fd);
	if (nread != cur_table->TotalChar)
	{
		printf ("Cannot write file header %s", filename);
		return 0;
	}

	// For 0 ~ (2^16 - 1)
	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];
		fprintf ( stderr, "Here, from xl_phrase.c : 406 DumpLoadInputMethod()\n" );
        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;
// This compare to element which is type ITEM.
// This function must be static and different from the one in xl_hzinput.c
static int qcmp (const void *t1, const 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);
}

