#include <stdio.h>
#include <iconv.h>

#include "ime.h"
#include "ime_buffer.h"

#define PREFIX_COMPLETE     (0x20000)
#define PREFIX_VALID        (0x10000)
#define HEXCANDIDATES       (0x0FFFF)

//lower 16 bit return value mask '0..9A..F' possiblities.
//    When return valid, lower 16 bit contains value only when incoming hex-char
//         could make the hex-string valid character.
//    When non-valid or complete, lower 16 bit is of no use
typedef int (*VALIDATE_PREFIX_FUNC)(unsigned char*);

int validate_prefix_gb2312(unsigned char *prefix);
int validate_prefix_gbk(unsigned char *prefix);
int validate_prefix_gb18030(unsigned char *prefix);
int validate_prefix_big5(unsigned char *prefix);
int validate_prefix_big5hkscs(unsigned char *prefix);
int validate_prefix_euctw(unsigned char *prefix);

static unsigned char number_string[] = "0123456789ABCDEF";

VALIDATE_PREFIX_FUNC validators[] = {  // Please check the definition of ImeEncoding
    validate_prefix_gb2312,
    validate_prefix_gbk,
    validate_prefix_gb18030,
    validate_prefix_big5,
    validate_prefix_big5hkscs,
    validate_prefix_euctw
};

int validate_prefix(int encode, unsigned char *prefix)
{
    if (encode >= ENCODE_GB2312 && encode <= ENCODE_EUCTW)
        return validators[encode](prefix);
    return 0;
}

#define HEXVALUE(c)     ((c >= 'A')?(c-'A'+10):(c-'0'))
int commit_all(ImeBufferRec *ime_buffer)
{
    unsigned char *p = ime_buffer->preedit.preedit.text, *dst =ime_buffer->commit_buf;

    for(; *p; p+=2)
        *dst++ = (HEXVALUE(*p) << 4) + HEXVALUE(*(p+1));
    *dst = 0;

    ime_buffer->preedit.preedit.text[0] = '\0';
    ime_buffer->preedit.caret = 0;
    ime_buffer->candidates.count = 0;
    ime_buffer->return_status |= (IME_COMMIT | IME_PREEDIT_AREA | IME_LOOKUP_AREA);
}

int neima_filter(int encoding, unsigned char key, ImeBufferRec *ime_buffer)
{
    int  i, ret, preedit_len;
    unsigned char *p, *dst;

    ime_buffer->return_status = 0;
    preedit_len = strlen(ime_buffer->preedit.preedit.text);

    DEBUG_printf("    ====>neima: filter key(0x%x)\n", key);
    if ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F')) {
        DEBUG_printf("        ====>neima:ime_buffer->preedit.preedit.text: %s (len=%d) key=%c(0x%x)\n", ime_buffer->preedit.preedit.text, preedit_len, key, key);

        ime_buffer->preedit.preedit.text[preedit_len] = toupper(key);
        ime_buffer->preedit.preedit.text[++preedit_len] = 0;

        ret = validate_prefix(encoding, ime_buffer->preedit.preedit.text);
        DEBUG_printf("        ====>Validating Key...result 0x%x...", ret);
        if (!(ret & PREFIX_VALID)) {
            DEBUG_printf("Invalid\n", key);
            ime_buffer->preedit.preedit.text[--preedit_len] = 0;
            return IME_INVALID_KEY;
        }
        DEBUG_printf("valid Key\n", key);
        DEBUG_printf("        ====>neima:ime_buffer->preedit.preedit.text: %s (len=%d) key=%c(0x%x)\n", ime_buffer->preedit.preedit.text, preedit_len, key, key);

        ime_buffer->preedit.caret = preedit_len;
        ime_buffer->return_status |= IME_PREEDIT_AREA;

        if (ret & PREFIX_COMPLETE) {
            printf("  [Neima IME] Commit all...\n");
            commit_all(ime_buffer);
        } else if (ret & PREFIX_VALID) {
            ime_buffer->candidates.count = 0;
            ime_buffer->candidates.page_state = ImeCandidatesFirstPage | ImeCandidatesLastPage;
            for (i=0; i < 16; ++i) {
                if (ret & 1) {
                    dst = ime_buffer->candidates.candidates[ime_buffer->candidates.count].text;
                    for(p=ime_buffer->preedit.preedit.text; *p && *(p+1); p+=2)
                        *dst++ = (HEXVALUE(*p) << 4) + HEXVALUE(*(p+1));
                    *dst++ = (HEXVALUE(*p) << 4) + i;
                    *dst = 0;
                    ime_buffer->candidates.numbers[ime_buffer->candidates.count] = number_string[i];
                    ime_buffer->candidates.numbers[ime_buffer->candidates.count+1] = 0;
                    ++ime_buffer->candidates.count;
                }
                ret >>= 1;
            }
            printf("    ====%d Candidates\n",  ime_buffer->candidates.count);
            if (ime_buffer->candidates.count) {
                ime_buffer->return_status |= IME_LOOKUP_AREA;

                #ifdef DEBUG
                for (i=0; i < ime_buffer->candidates.count; ++i) {
                    printf("          %c %s\n", 
                          ime_buffer->candidates.numbers[i], 
                          ime_buffer->candidates.candidates[i].text);
                }
                #endif
            }
        }
        return(IME_OK);
    }

    if(key == IME_FILTERED_KEY_ESCAPE && preedit_len > 0) {
        clear_ime_buffer(ime_buffer);
        ime_buffer->return_status |= (IME_PREEDIT_AREA | IME_LOOKUP_AREA);
        return (IME_OK);
    }

    if ((key == IME_FILTERED_KEY_BACKSPACE || key == IME_FILTERED_KEY_DELETE) && (preedit_len > 0)) {
        ime_buffer->preedit.preedit.text[--preedit_len] = '\0';
        ime_buffer->return_status = IME_PREEDIT_AREA;
        ime_buffer->candidates.count = 0;
        ime_buffer->return_status |= IME_LOOKUP_AREA;
        return(IME_OK);
    }

    return (preedit_len == 0)?(IME_UNUSED_KEY):(IME_INVALID_KEY);
}



int validate_prefix_gb2312(unsigned char *prefix)
{
    int idx, ret;

    for (idx = 0; *prefix; ++prefix, ++idx) {
        switch (idx) {
        case 0:
            if (*prefix < 'A' || *prefix > 'F')
                return 0;
            break;
        case 1:
            if ((*(prefix-1) == 'A' && *prefix == '0') || (*(prefix-1) == 'F' && *prefix > '7'))
                return 0;
            break;
        case 2:
            if (*prefix < 'A' || *prefix > 'F')
                return 0;
            break;
        case 3:
            if ((*(prefix-1) == 'A' && *prefix == '0') || (*(prefix-1) == 'F' && *prefix == 'F'))
                return 0;
            break;
        default:
            return 0;
        }
    }
    ret = PREFIX_VALID;
    if (idx == 3) {
        ret |= HEXCANDIDATES;
        if (*(prefix-1) == 'F') ret ^= 0x8000;
        if (*(prefix-1) == 'A') ret ^= 0x0001;
    } else if (idx == 4) {
        ret |= PREFIX_COMPLETE;
    }
    return ret;
}

int validate_prefix_gbk(unsigned char *prefix)
{
    int idx, ret;

    for (idx = 0; *prefix; ++prefix, ++idx) {
        switch (idx) {
        case 0:
            if (*prefix < '8' || *prefix > 'F')
                return 0;
            break;
        case 1:
            if ((*(prefix-1) == '8' && *prefix == '0') || (*(prefix-1) == 'F' && *prefix >= 'F'))
                return 0;
            break;
        case 2:
            if (*prefix < '4' || *prefix > 'F')
                return 0;
            break;
        case 3:
            if ((*(prefix-1) == '7' || *(prefix-1) == 'F') && *prefix >= 'F')
                return 0;
            break;
        default:
            return 0;
        }
    }
    ret = PREFIX_VALID;
    if (idx == 3) {
        ret |= HEXCANDIDATES;
        if (*(prefix-1) == 'F' || *(prefix-1) == '7')
            ret ^= 0x8000;      //F could not be used
    } else if (idx == 4) {
        ret |= PREFIX_COMPLETE;
    }
    return ret;
}

int validate_prefix_gb18030(unsigned char *prefix)
{
    int idx, ret;

    ret = validate_prefix_gbk(prefix);
    if (ret & PREFIX_VALID)
        return ret;
    for (idx = 0; *prefix; ++prefix, ++idx) {
        switch (idx) {
        case 0:
        case 4:
            if (*prefix < '8' || *prefix > 'F')
                return 0;
            break;
        case 1:
        case 5:
            if ((*(prefix-1) == '8' && *prefix == '0') || (*(prefix-1) == 'F' && *prefix >= 'F'))
                return 0;
            break;
        case 2:
        case 6:
            if (*prefix !='3')
                return 0;
            break;
        case 3:
        case 7:
            if (*prefix < '0' || *prefix > '9')
                return 0;
            break;
        default:
            return 0;
        }
    }
    ret = PREFIX_VALID;
    if (idx == 7) {
        ret |= 0x3FF;  //0~9
    } else if (idx == 8) {
        ret |= PREFIX_COMPLETE;
    }
    return ret;
}

int validate_prefix_big5(unsigned char *prefix)
{
    int idx, ret;

    for (idx = 0; *prefix; ++prefix, ++idx) {
        switch (idx) {
        case 0:
            if (*prefix < 'A' || *prefix > 'F')
                return 0;
            break;
        case 1:
            if ((*(prefix-1) == 'A' && *prefix == '0') || (*(prefix-1) == 'F' && *prefix > 'E'))
                return 0;
            break;
        case 2:
            if (*prefix < '4' || *prefix > 'F' || *prefix == '8' || *prefix == '9')
                return 0;
            break;
        case 3:
            if ((*(prefix-1) == '7' && *prefix == 'F') 
                     || (*(prefix-1) == 'A' && *prefix == '0') 
                     || (*(prefix-1) == 'F' && *prefix == 'F'))
                return 0;
            break;
        default:
            return 0;
        }
    }
    ret = PREFIX_VALID;
    if (idx == 3) {
        ret |= HEXCANDIDATES;
        if (*(prefix-1) == 'F' || *(prefix-1)=='7') ret ^= 0x8000;
        if (*(prefix-1) == 'A') ret ^= 0x0001;
    } else if (idx == 4) {
        ret |= PREFIX_COMPLETE;
    }
    return ret;
}

int validate_prefix_big5hkscs(unsigned char *prefix)
{
    int idx, ret;

    for (idx = 0; *prefix; ++prefix, ++idx) {
        switch (idx) {
        case 0:
            if (*prefix < '8' || *prefix > 'F')
                return 0;
            break;
        case 1:
            if ((*(prefix-1) == '8' && *prefix == '0') 
                     || (*(prefix-1) == 'F' && *prefix > 'E'))
                return 0;
            break;
        case 2:
            if (*prefix < '4' || *prefix > 'F')
                return 0;
            break;
        case 3:
            if ((*(prefix-1) == 'F' && *prefix == 'F'))
                return 0;
            break;
        default:
            return 0;
        }
    }
    ret = PREFIX_VALID;
    if (idx == 3) {
        ret |= HEXCANDIDATES;
        if (*(prefix-1) == 'F') ret ^= 0x8000;
    } else if (idx == 4) {
        ret |= PREFIX_COMPLETE;
    }
    return ret;
}

int validate_prefix_euctw(unsigned char *prefix)
{
    // we do not support this now
    return 0;
}
