/* atie.c -- Ascii To Input Event */

/* The contents of this file are copyright 1990, Mikael Karlsson.
 * You may use these routines freely in non-commerical programs,
 * provided that some notice of it's use is given in the program
 * and/or documentation.
*/

struct HalfMap {
    UBYTE *KeyMapTypes;
    ULONG *KeyMap;
    UBYTE *Capsable;
    UBYTE *Repeatable;
};

struct DeadSpec {
    UBYTE ds_Prev1DownCode;
    UBYTE ds_Prev1DownQual;
    UBYTE ds_Prev2DownCode;
    UBYTE ds_Prev2DownQual;
};

#define CONTROLBITS ((1 << 5) | (1 << 6))
#define KEYMAPSIZE  64

#define S IEQUALIFIER_LSHIFT
#define A IEQUALIFIER_LALT
#define C IEQUALIFIER_CONTROL

/* These tables can be found in RKM Libraries & Devices 1.3 */

WORD deadQuals[8][10] = {
    { KC_NOQUAL                     , 1, 0                                },
    {                     KCF_SHIFT , 2, 0, S                             },
    {             KCF_ALT           , 2, 0, A                             },
    {             KCF_ALT+KCF_SHIFT , 4, 0, S, A, S+A                     },
    { KCF_CONTROL                   , 2, 0, C                             },
    { KCF_CONTROL+        KCF_SHIFT , 4, 0, S, C, S+C                     },
    { KCF_CONTROL+KCF_ALT           , 4, 0, A, C, A+C                     },
    { KCF_CONTROL+KCF_ALT+KCF_SHIFT , 8, 0, S, A, S+A, C, C+S, C+A, C+S+A }
};

WORD normalQuals[8][6] = {
    { KC_NOQUAL                     , 1,   0, 0, 0, 0 },
    {                     KCF_SHIFT , 2,   0, 0, S, 0 },
    {             KCF_ALT           , 2,   0, 0, A, 0 },
    {             KCF_ALT+KCF_SHIFT , 4, S+A, A, S, 0 },
    { KCF_CONTROL                   , 2,   0, 0, C, 0 },
    { KCF_CONTROL+        KCF_SHIFT , 4, C+S ,C, S, 0 },
    { KCF_CONTROL+KCF_ALT           , 4, C+A, C, A, 0 },
    { KCF_CONTROL+KCF_ALT+KCF_SHIFT , 4, S+A, A, S, 0 }
};

#define keytype 0
#define combos 1

#undef S
#undef A
#undef C

STATIC WORD checkNormalQual(four_bytesp, val, type, qualp)
LONG four_bytesp;
UBYTE val;
UWORD type;
UWORD *qualp;
{
    register UBYTE *p = (UBYTE *)four_bytesp; /* codes held in long word  */
    register WORD position;
    register WORD i = normalQuals[type][combos];

    position = 4;

    while (i--) {
        --position;
        if (p[position] == val) {
            *qualp = normalQuals[type][position + 2];
            return 1;
        }
    }
    return 0;
}

WORD checkNormal(p, val, type, qualp)
UBYTE *p;
UBYTE val;
WORD type;
UWORD *qualp;
{
    /* only one way to match a vanilla control key  */
    if (type == KC_VANILLA && (val & CONTROLBITS) == NULL) { /* control vanilla */
        if (checkNormalQual(p, val | CONTROLBITS, type, qualp)) {
            *qualp |= IEQUALIFIER_CONTROL;
            return (1);
        } else {
            return (0);
        }
    } else { /* not a control */
        return (checkNormalQual(p, val, type, qualp));
    }
}


STATIC WORD checkDead(keybase, val, type, qualp, indexp)
UBYTE *keybase;                      /* note: byte pointer   */
UBYTE val;
WORD type;
UWORD *qualp;
ULONG *indexp;
{
    WORD i;
    WORD j;
    register UBYTE *p = keybase;  /* need to remember keybase for offsets */
    UBYTE *deadp;

    /* walk through two-byte entries, one for each qual. combo.       */
    for (i = 0; i < deadQuals[type][combos]; ++i, p += 2) {
        switch (p[0]) {
            case DPF_DEAD:       /* dead keys do not themselves map to anything */
                break;
            case DPF_MOD:        /* dead key modifiable  */
                deadp = keybase + p[1];
                /* look down the string indexed by dead-key     index */
                for (j = 0; j < 6; ++j) {
                    if (deadp[j] == val) {
                        *qualp = deadQuals[type][i + 2];
                        *indexp = j;
                        return 1;
                    }
                }
                break;
            case 0:                      /* normal stroke for this key   */
                if (p[1] == val) {
                    *qualp = deadQuals[type][i + 2];
                    return 1;
            }
        }
    }
    return (0);
}

/*
 * Calculates Code+Qual of previous dead key (should be keys)
 * and puts them in DeapSpec.
 * returns success (1) or failure (0).
 */
STATIC WORD BuildDeadSpec(inx, hm, hms, ds)
ULONG inx;
struct HalfMap *hm;
WORD hms;
struct DeadSpec *ds;
{
    /* find keystroke which generates index */

    register WORD code = 0;
    register UBYTE *deadthing;
    register WORD i;

    do {
        /* check each deadkey in the table      */

        if (hm->KeyMapTypes[code] & KCF_DEAD) {
            register WORD type = hm->KeyMapTypes[code] & 7;

            /* keymap entry is pointer to prefix:byte pairs   */

            deadthing = (UBYTE *)hm->KeyMap[code];
            for (i = 0; i < deadQuals[type][combos]; ++i, deadthing += 2) {
                  /* check for index prefix and correct index     */
                if (deadthing[0] == DPF_DEAD &&
                  (deadthing[1] & DP_2DINDEXMASK) == inx) {
                    ds->ds_Prev1DownCode = code;
                    ds->ds_Prev1DownQual = deadQuals[type][i + 2];
                    ds->ds_Prev2DownCode = 0;
                    ds->ds_Prev2DownQual = 0;
                    return 1;
                }
            }
        }
    } while (++code < hms);

    return 0;   /* Not found */
}

STATIC WORD checkString(keybase, val, type, qualp)
UBYTE *keybase;                      /* note: byte pointer   */
UBYTE val;
WORD type;
UWORD *qualp;
{
    WORD i;
    register UBYTE *p = keybase;  /* need to remember keybase for offsets */

    /* walk through two-byte entries, one for each qual. combo.       */
    for (i = 0; i < deadQuals[type][combos]; ++i, p += 2) {
        if (p[0] == 1) {         /* One char in string */
            if (keybase[p[1]] == val) {   /* Our char? */
                *qualp = deadQuals[type][i + 2];
                return 1;
            }
        }
    }
    return (0);
}

/* BuildEvent tries to generate an input event.
 * returns success (1) or failure (0).
 */
STATIC UWORD BuildEvent(value, hm, hms, ie, km)
register UBYTE  value;
struct HalfMap *hm;
WORD hms;
struct InputEvent *ie;
struct KeyMap *km;      /* We need this to find dead prefix */
{
    register UWORD code = 0;
    register WORD type;
    register LONG *p;             /* points to four-byte lokeymap entry   */
    UWORD *qualp = &ie->ie_Qualifier;
    WORD found_it = 0;
    ULONG index = 0;

    p = (LONG *) hm->KeyMap;

    do  {
        type = hm->KeyMapTypes[code];
        /* determine type of key        */
        if (type & KCF_STRING) {
            found_it = checkString((UBYTE *)*p, value, type & 7, qualp);
        } else if (type & KCF_DEAD) {
            found_it = checkDead((UBYTE *)*p, value, type & 7, qualp, &index);
        } else if (!(type & KCF_NOP)) {
            found_it = checkNormal((LONG)p, value, type & 7, qualp);
        }
        ++p;
    } while (!found_it && ++code < hms);

    if (found_it) {
        ie->ie_Code = code;
          /* Successful BuildEvent. Check for dead key. */
        if (index) {
            struct HalfMap *dhm;
            struct DeadSpec *ds = (struct DeadSpec *)&ie->ie_EventAddress;

            dhm = (struct HalfMap *)&km->km_LoKeyMapTypes;
            if (BuildDeadSpec(index, dhm, 64, ds)) {
                return 1;
            }
            dhm = (struct HalfMap *)&km->km_HiKeyMapTypes;
            if (BuildDeadSpec(index, dhm, 56, ds)) {
                return 1;
            }
            return 0;   /* Couldn't find index generating dead key */
        } else {
            return 1;
        }
    } else {
        return 0;
    }
}

/*
 * NAME
 *      AsciiToInputEvent -- build input event to generate ascii
 *
 * SYNOPSIS
 *      success = AsciiToInputEvent(ascii, inputevent, keymap)
 *      ULONG AsciiToInputEvent(ULONG, struct InputEvent *, struct KeyMap *)
 *
 * FUNCTION
 *      This function tries to generate an input event that, when sent
 *      to the input handler, will generate the specified ascii code.
 *
 * INPUTS
 *      ascii - the ascii code to be generated by the constructed
 *              input event.
 *
 *      inputevent - the input event that will be filled will the
 *              appropriate values to generate the given ascii code.
 *
 *      keymap - the keymap that the input event will be reverse-
 *              engineered from.
 *
 *
 * RESULT
 *      success - a boolean value indicating whether the reverse-
 *              engineering was successful.
 *
 * NOTE
 *      The ascii code must be available in the given keymap to be
 *      generatable. Ascii values between 128 and 160 are usually
 *      not available with normal keymaps.
 *
 * SEE ALSO
 *      RawKeyConvert(), V36 keymap.library/MapANSI().
 */

ULONG AsciiToInputEvent(ascii, ie, km)
ULONG ascii;
register struct InputEvent *ie;
struct KeyMap *km;
{
    struct HalfMap *hm;

    ie->ie_Class = IECLASS_RAWKEY;
    ie->ie_Qualifier = 0;
    ie->ie_EventAddress = 0;

    /* Some ascii values has to be treated separately
       since some programs know the difference between
       for example CTRL-M and RETURN.  */
    {
        WORD code = 0;

        switch(ascii)
        {
            case 0x08:              /* backspace  */
                code = 0x41;
                break;
            case '\t':              /* tab        */
                code = 0x42;
                break;
            case 0x0D:              /* return     */
                code = 0x44;
                break;
            case 0x1B:              /* esc        */
                code = 0x45;
                break;
            case 0x7F:              /* del        */
                code = 0x46;
                break;
        }

        if (code) {
            ie->ie_Code = code;
            return 1;
        }
    }

    hm = (struct HalfMap *)&km->km_LoKeyMapTypes;
    if (BuildEvent((UBYTE)ascii, hm, 64, ie, km)) {
        return 1;   /* Key found in LoKeyMap */
    }
    hm = (struct HalfMap *)&km->km_HiKeyMapTypes;
    if (BuildEvent((UBYTE)ascii, hm, 56, ie, km)) {
        ie->ie_Code += 64;  /* Don't forget that we're in the hi keymap */
        return 1;   /* Key found in HiKeyMap */
    }
    return 0;     /* No luck */
}
