/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "swis.h"

#include "Unicode/iso10646.h"
#include "unicdata.h"

char keypad_codes[]={
          0x23, 0x24, 0x25,
    0x37, 0x38, 0x39, 0x3A,
    0x48, 0x49, 0x4A, 0x4B,
    0x5A, 0x5B, 0x5C,
    0x65,       0x66, 0x67,
    0
};

int default_keypad[17][2] = {
    0x2F, 0x2F,
    0x2A, 0x2A,
    0x23, 0x23,
    0x1E, 0x37,
    0x8F, 0x38,
    0x9F, 0x39,
    0x2D, 0x2D,
    0x8C, 0x34,
    -1,   0x35,
    0x8D, 0x36,
    0x2B, 0x2B,
    0x8B, 0x31,
    0x8E, 0x32,
    0x9E, 0x33,
    0xCD, 0x30,
    0x7F, 0x2E,
    0x0D, 0x0D
};

int our_keypad[17][2];

int is_keypad_key(int key)
{
    return key != 0 && strchr(keypad_codes, key) != NULL;
}

const char *dead_latin1[] =
{
    "GRAVE ACCENT",
    "ACUTE ACCENT",
    "CIRCUMFLEX ACCENT",
    "TILDE",
    "DIAERESIS",
    "RING ABOVE",
    "CEDILLA"
};

const char *dead_latin2[] =
{
    "OGONEK",
    "STROKE",
    "CARON",
    "ACUTE ACCENT",
    "CEDILLA",
    "DOT ABOVE",
    "CIRCUMFLEX ACCENT",
    "BREVE",
    "DIAERESIS",
    "DOUBLE ACUTE"
};

const char *dead_latin3[] =
{
    "STROKE",
    "CIRCUMFLEX ACCENT",
    "DOT ABOVE",
    "CEDILLA",
    "BREVE",
    "GRAVE ACCENT",
    "ACUTE ACCENT",
    "DIAERESIS",
    "TILDE"
};

const char *dead_latin4[] =
{
    "OGONEK",
    "CEDILLA",
    "TILDE",
    "CARON",
    "MACRON",
    "STROKE",
    "DIAERESIS",
    "RING ABOVE",
    "ACUTE ACCENT",
    "DOT ABOVE",
    "CIRCUMFLEX ACCENT"
};

const char *dead_welsh[] =
{
    "GRAVE ACCENT",
    "ACUTE ACCENT",
    "CIRCUMFLEX ACCENT",
    "TILDE",
    "DIAERESIS",
    "RING ABOVE",
    "CEDILLA"
};

const char *dead_greek[] =
{
    "GREEK TONOS",
    "GREEK DIALYTIKA",
    "GREEK DIALYTIKA TONOS"
};

const int dead_alphabets[] =
{
    101, 102, 103, 104, 107, 110, 0
};

const char **dead_tables[] =
{
    dead_latin1, dead_latin2, dead_latin3, dead_latin4, dead_greek, dead_welsh
};

void error(const char *p)
{
    fprintf(stderr, "%s\n", p);
    exit(1);
}

void *xmalloc(size_t s)
{
    void *ret = malloc(s);
    if (!ret) error("Out of memory");
    return ret;
}

char *read_string(FILE *f)
{
    int type = fgetc(f);
    int len;
    char *ret;

    if (type != 0) error("Type mismatch");
    len = fgetc(f);

    ret = xmalloc(len+1);
    ret[len] = 0;
    while (len > 0)
        ret[--len] = fgetc(f);

    return ret;
}

int read_int(FILE *f)
{
    int type = fgetc(f);
    int ret;

    if (type != 0x40) error("Type mismatch");
    ret = fgetc(f);
    ret = (ret << 8) + fgetc(f);
    ret = (ret << 8) + fgetc(f);
    ret = (ret << 8) + fgetc(f);

    return ret;
}

char *function_name(int code, int key, int pos)
{
    char buffer[256];
    const char *ctrlshift;
    static const char *ctrlshifttab[] = { "", "SHIFT-", "CTRL-", "CTRL-SHIFT-" };

    ctrlshift = ctrlshifttab[pos & 3];

    if (key==0x00 && code == 27)
        sprintf(buffer, "FUNCTION KEY %sESCAPE", ctrlshift);
    else if (key==0x1E && code == 8)
        sprintf(buffer, "FUNCTION KEY %sBACKSPACE", ctrlshift);
    else if ((key==0x20 || key == 0x37) && code == 30)
        sprintf(buffer, "FUNCTION KEY %sHOME", ctrlshift);
    else if ((key==0x47 || key == 0x67) && code == 13)
        sprintf(buffer, "FUNCTION KEY %sENTER", ctrlshift);
    else if (code < 32)
        sprintf(buffer, "FUNCTION KEY CTRL-%c", "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"[code]);
    else if (code == 0x7F)
        sprintf(buffer, "FUNCTION KEY DELETE");
    else
    {
        int shift = code & 0x10;
        int ctrl = code & 0x20;
        const char *s;
        static const char *func[32] =
        {
            "PRINT", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
            "F8", "F9", "BREAK", "END", "LEFT", "RIGHT", "DOWN", "UP",
            "ACORN", "MENU", "WIDTH", "KANJI", "EISUU", "NO CONVERT", "CONVERT", "KANA",
            "ALL CANDIDATES", "KANJI NUMBER", "F10", "F11", "F12", "INSERT", "&1CE", "&1CF"
        };
        code &= ~0x30;
        code -= 0x180;
        if (code >= 0x40) code -= 0x30;
        s = func[code];
        if ((key == 0x21 || key == 0x39) && code == 0x0F ||
            (key == 0x36 || key == 0x5C) && code == 0x0E)
        {
            shift ^= 0x10;
            s = code == 0x0E ? "PAGE DOWN" : "PAGE UP";
        }
        sprintf(buffer, "FUNCTION KEY %s%s%s", ctrl ? "CTRL-" : "",
                                              shift ? "SHIFT-" : "",
                                              s);
    }
    return buffer;
}

int main(int argc, char **argv)
{
    FILE *in, *out;
    char *cno, *alphabet;
    int lastkey, flags, i, j, n, alphabet_no, notclaimed, same;
    const UCS4 *alphabet_table;
    const char **dead_table;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: keyconvert <in-file> <out-file>\n");
        return 1;
    }

    load_unidata("UniData215");

    in = fopen(argv[1], "r");
    if (!in)
    {
        perror(argv[1]);
        exit(1);
    }

    out = fopen(argv[2], "w");
    if (!out)
    {
        perror(argv[2]);
        exit(1);
    }

    cno = read_string(in);
    alphabet = read_string(in);
    lastkey = read_int(in);
    flags = read_int(in);

    _swix(OS_ServiceCall, _INR(1,3)|_OUT(1)|_OUT(4),
                          0x43, 1, alphabet,
                          &notclaimed, &alphabet_no);
    if (notclaimed) error("Unknown alphabet");
    _swix(OS_ServiceCall, _INR(1,3)|_OUT(1)|_OUT(4),
                          0x43, 8, alphabet_no,
                          &notclaimed, &alphabet_table);
    if (notclaimed || !alphabet_table) error("Can't find table for alphabet");

    dead_table = NULL;

    for (i=0; dead_alphabets[i]; i++)
        if (dead_alphabets[i] == alphabet_no)
        {
            dead_table = dead_tables[i];
            break;
        }

    fprintf(out, "# Layout converted by keyconvert\n#\n");
    fprintf(out, "# Default alphabet: %s\n", alphabet);
    fprintf(out, "# Extra key 1D %sfitted\n", flags & 2 ? "" : "not ");
    fprintf(out, "# Extra key 4D %sfitted\n", flags & 1 ? "" : "not ");
    fprintf(out, "# Extra key 6E %sfitted\n", flags & 8 ? "" : "not ");
    fprintf(out, "# Enter key in %s position\n", flags & 4 ? "USA" : "UK");

    fprintf(out, "\n$Country %s\n", cno);

    fprintf(out, "\n$Layer 0\n\n");

    for (i=0; i<=lastkey; i++)
    {
        int null = 1;
        for (j=0; j<8; j++)
        {
            int code = read_int(in);
            if (code != -1 || (i==0x01&&j==0x06) || (i==0x02&&j==0x06) || (i==0x0C&&j==0x06) || i==0x34)
                null = 0;
        }
        if (null) continue;
        if (i >= 0x6B) continue; /* Hard coded hack to strip extra keys from layouts */
        fseek(in, -8*5L, SEEK_CUR);
        fprintf(out, "%02X\n", i);
        for (j=0; j<8; j++)
        {
            int code = read_int(in);
            const char *caps="";
            if ((code &~ 0xFF) == 0x200)
            {
                static const char brack[8][2] = { "[", "]", "<", ">", "{", "}", "(", ")" };
                code -= 0x200;
                caps = brack[j];
            }

            if (i==0x01 && j == 6)
            {
                fprintf(out, "SPECIAL KEY DEFAULT KEYBOARD\n");
                continue;
            }
            if (i==0x02 && j == 6)
            {
                fprintf(out, "SPECIAL KEY CONFIGURED KEYBOARD\n");
                continue;
            }
            if (i==0x0C && j ==6)
            {
                fprintf(out, "SPECIAL KEY DIAL KEYBOARD\n");
                continue;
            }
            if (i==0x34 && j < 4)
            {
                fprintf(out, "SPECIAL KEY DELETE\n");
                continue;
            }

            if (code == -1)
                fprintf(out, "-\n");
            else if (code < 32)
                fprintf(out, "%s\n", function_name(code, i, j));
            else if (code == 127)
                fprintf(out, "FUNCTION KEY CTRL-?\n");
            else if (code < 0x100)
                fprintf(out, "%s%s\n", caps, name_from_UCS(alphabet_table[code]));
            else if (code < 0x180)
                error("I don't grok codes from &100 to &17F");
            else if (code < 0x200)
                fprintf(out, "%s\n", function_name(code, i, j));
            else if (code >= 0x300 && code <= 0x3FF)
            {
                if (!dead_table) error("I don't know the dead keys for this alphabet");
                fprintf(out, "DEAD KEY %s\n", dead_table[code-0x301]);
            }
            else
                error("Unknown code");
        }
    }

    fprintf(out, "\n$EndLayer\n");

    for (n=0,i=0; i<=lastkey; i++)
    {
        if (!is_keypad_key(i))
        {
            read_int(in); read_int(in);
            continue;
        }

        our_keypad[n][0] = read_int(in);
        our_keypad[n][1] = read_int(in);
        n++;
    }

    same = 1;
    for (n=0; n<17; n++)
    {
        for (j=0; j<2; j++)
        {
            if (our_keypad[n][j] != default_keypad[n][j])
                same = 0;
        }
    }

    if (!same)
    {
        int c0, c1;
        const char *s;
        fprintf(out, "\n$Keypad\n");
        for (n = 0; n<17; n++)
        {
            int count;
            c0 = our_keypad[n][1];
            c1 = our_keypad[n][0];
            if (c0 >= 0x80 && c0 <= 0xFF) c0 += 0x100;
            if (c1 >= 0x80 && c1 <= 0xFF) c1 += 0x100;
            if (c0 == -1)
                s = "-";
            else if (c0 < 32 || c0 == 0x7F || c0 >= 0x180)
                s = function_name(c0, keypad_codes[n], 0);
            else
                s = name_from_UCS(c0);

            fprintf(out, "%s%n", s, &count);
            if (c0 == c1)
            {
                fprintf(out, "\n");
            }
            else
            {
                if (c1 == -1)
                   s = "-";
                else if (c1 < 32 || c1 == 0x7F || c1 >= 0x180)
                    s = function_name(c1, keypad_codes[n], 0);
                else
                    s = name_from_UCS(c1);

                fputc(';', out);
                while (count < 19)
                {
                    fputc(' ', out);
                    count++;
                }

                fprintf(out, "%s\n", s);
            }
        }
        fprintf(out, "$EndKeypad\n");
    }

    return 0;


}