/* 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( 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( 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(
			( LONG *)p,
			val | CONTROLBITS,
			type, qualp))
		{
			*qualp |= IEQUALIFIER_CONTROL;
			return ( 1);
		}
		else
		{
			return ( 0);
		}
	}
	else
	{			/* not a control */
		return ( checkNormalQual( (LONG *)p, val, type, qualp));
	}
}


STATIC WORD checkDead( UBYTE *keybase, 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( 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( UBYTE *keybase, 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( 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( ( UBYTE *)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(  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 */
}
