/* $Id: keys.c,v 1.14 2005/09/15 15:03:17 cegger Exp $
******************************************************************************

   This LibGIC module is a very simple parser for KeyPress Events.

   Copyright (C) 1999 Andreas Beck	[becka@ggi-project.org]

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

******************************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <ggi/internal/gic.h>
#include <ggi/internal/gic_debug.h>

#include <ggi/gg.h>
#include <ggi/internal/gg_replace.h>	/* for snprintf() */


struct keypress {
	enum mode {
		LABEL,
		BUTTON,
		SYMBOL,
		NUM_MODES
	} mode;
	uint32_t value;
};

static char modemap[NUM_MODES] = "LBS";


static int key_check(gic_handle_t hand, gic_recognizer *ctrl,
		gii_event *event,gic_feature *feature,int recnum);

static int key_get_name(gic_handle_t hand, gic_recognizer *ctrl,
		char *string,size_t maxlen);

static int key_write_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
			char *string,int maxlen);

static int key_read_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
			const char *string);

static void key_free_pvtdata(gic_handle_t hand, gic_recognizer *ctrl);

static int key_train(gic_handle_t hand, gic_recognizer **ctrl,gii_event *event);

static int key_check_conflict(gic_handle_t hand, gic_recognizer *ctrl,
			gic_recognizer *ctrl2);

static int key_get_opposite(gic_handle_t hand, gic_recognizer *recognizer,
				gic_recognizer **opposite);


static gic_recognizerdriver mycontrols= {
	"Key",
	key_check,
	key_get_name,
	key_write_pvtdata,
	key_read_pvtdata,
	key_free_pvtdata,
	key_train,
	key_check_conflict,
	key_get_opposite,
};


static int chkkey(struct keypress *kp,gii_event *event)
{
	DPRINT_LIBS("Keys: keyEvent L%04x,B%04xS%04x [want %c,%04x].\n",
		event->key.label,event->key.button,event->key.sym,
		modemap[kp->mode],kp->value);

	switch(kp->mode) {
	case LABEL:
		return (event->key.label == (unsigned)kp->value);
	case BUTTON:
		return (event->key.button == (unsigned)kp->value);
	case SYMBOL:
		return (event->key.sym == (unsigned)kp->value);
	default:
		return 0;
	}
	return 0;
}

static int key_check(gic_handle_t hand, gic_recognizer *ctrl,
		gii_event *event,gic_feature *feature,int recnum)
{
	DPRINT_LIBS("Keys: Check with %p,%p.\n",ctrl,event);
	switch(event->any.type) {
	case evKeyPress:
	case evKeyRepeat:
		if (chkkey(ctrl->privdata,event)) {
			gicFeatureActivate(hand, feature, GIC_STATE_MAX, 0, recnum);
			return 1;
		} 
		break;
	case evKeyRelease:
		if (chkkey(ctrl->privdata,event)) {
			gicFeatureActivate(hand, feature, GIC_STATE_MIN, 0, recnum);
			return 1;
		} 
		break;
	}
	return 0;
}

static int keys_register(gic_handle_t hand, gic_recognizer **ctrl,struct keypress *kp,gic_state state)
{
	gic_recognizer *rl;
	struct keypress *mkp;
	
	for (rl=*ctrl; rl != NULL; rl=rl->next) {

		struct keypress *cur = (struct keypress *) rl->privdata;

		if ((cur->mode == kp->mode) && (cur->value == kp->value)) {
			/* Duplicate entry -- just update the confidence
			 * value.
			 */
			 if (state > rl->confidence) {
			 	rl->confidence = state;
				/* !!! MOVE UP */
			}
			return 1;
		}
	}

	rl = malloc(sizeof(gic_recognizer));
	if (rl == NULL) return GGI_ENOMEM;
	mkp = malloc(sizeof(struct keypress));
	if (mkp == NULL) {
		free(rl);
		return GGI_ENOMEM;
	}
	
	memcpy(mkp,kp,sizeof(struct keypress));
	rl->driver=&mycontrols;
	rl->confidence=state;
	rl->privdata=mkp;
	gicRecognizerTrainAdd(hand,ctrl,rl);
	return 1;
}

static struct trainingstate {
	int label,button,sym;
} trainingstate;

#define CONFIDENCE(percent)  \
	(GIC_STATE_MIN + (GIC_STATE_MAX-GIC_STATE_MIN)/100 * (percent))

static int key_train(gic_handle_t hand, gic_recognizer **ctrl,gii_event *event)
{
	struct keypress kp;
	int rc;

	DPRINT_LIBS("Keys: Training with %p,%p.\n",ctrl,event);

	rc=0;
	if (event) {
		DPRINT_LIBS("Keys: Analyzing event ...\n");
		switch(event->any.type) {
		case evKeyPress:	/* O.K. remember the press */
			trainingstate.label =event->key.label;
			trainingstate.button=event->key.button;
			trainingstate.sym   =event->key.sym;
			DPRINT_LIBS("Keys: Remembering last pressed key ...\n");
			return 0;
		case evKeyRelease:
			DPRINT_LIBS("Keys: Checking released key ...\n");
			if ((unsigned)trainingstate.label == event->key.label &&
			    trainingstate.label != GIIK_VOID)
			{
				kp.mode=LABEL;
				kp.value=event->key.label;
				rc+=keys_register(hand, ctrl,&kp,CONFIDENCE(100));
				DPRINT_LIBS("Keys: register label mode ...\n");
			}
			if ((unsigned)trainingstate.sym == event->key.sym &&
			    trainingstate.sym != GIIK_VOID)
			{
				kp.mode=SYMBOL;
				kp.value=event->key.sym;
				rc+=keys_register(hand, ctrl,&kp,CONFIDENCE(90));
				DPRINT_LIBS("Keys: register symbol mode ...\n");
			}
			if ((unsigned)trainingstate.button == event->key.button &&
			    trainingstate.button != GIIK_VOID)
			{
				kp.mode=BUTTON;
				kp.value=event->key.button;
				rc+=keys_register(hand, ctrl,&kp,CONFIDENCE(80));
				DPRINT_LIBS("Keys: register button mode ...\n");
			}
		}
	} else {
		trainingstate.sym=
		trainingstate.label=
		trainingstate.button=GIIK_VOID;
		DPRINT_LIBS("Keys: Initialized training state.\n");
	}
	return rc;
}

/*
 * Privatedata format is :
 * mode (0-2)  value (hex, 4 digits)
 * modes are : KeyLabel (0), Keycode (1), KeySymbol (2)
 * value is the appropriate value of the corresponding field.
 */
 
static int key_write_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
			char *string,int maxlen)
{
	struct keypress *keyp=ctrl->privdata;

	if (maxlen < 7) {
		*string='\0';
		return GGI_ENOSPACE;
	}
	sprintf(string,"%c %04x",modemap[keyp->mode],keyp->value);

	return 0;
}

static int key_read_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
			const char *string)
{
	struct keypress *keyp;
	char hlp;
	int x;

	keyp=ctrl->privdata=malloc(sizeof(struct keypress));
	sscanf(string,"%c %x",&hlp,&keyp->value);
	for(x=0;x<NUM_MODES;x++)
	{
		if (hlp==modemap[x]) {
			keyp->mode=x;
			break;
		}
	}

	return 0;
}

static void key_free_pvtdata(gic_handle_t hand, gic_recognizer *ctrl)
{
	struct keypress *keyp=ctrl->privdata;

	free(keyp);ctrl->privdata=NULL;
	
	return;
}

static const char *name_of_key(uint32_t value, int is_label, int want_short)
{
	static char namebuf[40];

	if (GII_KTYP(value) == GII_KT_LATIN1) {

		int c = GII_KVAL(value);

		switch(value) {
			case GIIUC_BackSpace: return "<BS>";
			case GIIUC_Tab:       return "<Tab>";
			case GIIUC_Linefeed:  return "<LF>";
			case GIIUC_Return:    return "<CR>";
			case GIIUC_Escape:    return "<Esc>";
			case GIIUC_Delete:    return "<Del>";
			case GIIUC_Space:     return "<Space>";
			case GIIUC_NoBreakSpace: return "<NBSP>";
		}

		if (c < 32) {
			snprintf(namebuf, 40, " ^%c", c+64);
			return namebuf;
		}

		if (((0x21 <= c) && (c <= 0x7e)) ||
		    ((0xA1 <= c) && (c <= 0xff)))
		{
			snprintf(namebuf, 40, " %c", c);
			return namebuf;
		}

	} else if (GII_KTYP(value) == GII_KT_FN) {
		snprintf(namebuf, 40, "<F%d>", GII_KVAL(value));
		return namebuf;
	
	} else if (GII_KTYP(value) == GII_KT_MOD) {

		if (is_label) switch (value) {
			case GIIK_ShiftL: return "<ShiftLeft>";
			case GIIK_ShiftR: return "<ShiftRight>";
			case GIIK_CtrlL:  return "<CtrlLeft>";
			case GIIK_CtrlR:  return "<CtrlRight>";
			case GIIK_AltL:   return "<AltLeft>";
			case GIIK_AltR:   return "<AltRight>";
			case GIIK_MetaL:  return "<MetaLeft>";
			case GIIK_MetaR:  return "<MetaRight>";
			case GIIK_SuperL: return "<SuperLeft>";
			case GIIK_SuperR: return "<SuperRight>";
			case GIIK_HyperL: return "<HyperLeft>";
			case GIIK_HyperR: return "<HyperRight>";
			default: break;

		} else switch (value) {
			case GIIK_Shift:  return "<Shift>";
			case GIIK_Ctrl:   return "<Ctrl>";
			case GIIK_Alt:    return "<Alt>";
			case GIIK_Meta:   return "<Meta>";
			case GIIK_Super:  return "<Super>";
			case GIIK_Hyper:  return "<Hyper>";
			case GIIK_AltGr:  return "<AltGr>";
			case GIIK_Caps:   return "<Caps>";
			case GIIK_Num:    return "<Num>";
			case GIIK_Scroll: return "<Scroll>";
			default: break;
		}

	} else switch(value) {

		case GIIUC_BackSpace: return "<BS>";

		case GIIK_ShiftLock:  return "<ShiftLock>";
		case GIIK_CtrlLock:   return "<CtrlLock>";
		case GIIK_AltLock:    return "<AltLock>";
		case GIIK_MetaLock:   return "<MetaLock>";
		case GIIK_SuperLock:  return "<SuperLock>";
		case GIIK_HyperLock:  return "<HyperLock>";
		case GIIK_AltGrLock:  return "<AltGrLock>";
		case GIIK_CapsLock:   return "<CapsLock>";
		case GIIK_NumLock:    return "<NumLock>";
		case GIIK_ScrollLock: return "<ScrLock>";

		case GIIK_ScrollForw: return "<ScrForward>";
		case GIIK_ScrollBack: return "<ScrBack>";

		case GIIK_Undo:       return "<Undo>";
		case GIIK_Redo:       return "<Redo>";
		case GIIK_Menu:       return "<Menu>";
		case GIIK_Cancel:     return "<Cancel>";
		case GIIK_PrintScreen: return "<PrtScr>";
		case GIIK_Execute:    return "<Exec>";
		case GIIK_Find:       return "<Find>";
		case GIIK_Begin:      return "<Begin>";
		case GIIK_Clear:      return "<Clear>";
		case GIIK_Insert:     return "<Insert>";
		case GIIK_Select:     return "<Select>";
		case GIIK_Macro:      return "<Macro>";
		case GIIK_Help:       return "<Help>";
		case GIIK_Do:         return "<Do>";
		case GIIK_Pause:      return "<Stop>";
		case GIIK_ModeSwitch: return "<ModeSwch>";

		case GIIK_Up:         return "<Up>";
		case GIIK_Down:       return "<Down>";
		case GIIK_Left:       return "<Left>";
		case GIIK_Right:      return "<Right>";
		case GIIK_PageUp:     return "<PageUp>";
		case GIIK_PageDown:   return "<PageDown>";
		case GIIK_Home:       return "<Home>";
		case GIIK_End:        return "<End>";

		case GIIK_P0:         return "<KP0>";
		case GIIK_P1:         return "<KP1>";
		case GIIK_P2:         return "<KP2>";
		case GIIK_P3:         return "<KP3>";
		case GIIK_P4:         return "<KP4>";
		case GIIK_P5:         return "<KP5>";
		case GIIK_P6:         return "<KP6>";
		case GIIK_P7:         return "<KP7>";
		case GIIK_P8:         return "<KP8>";
		case GIIK_P9:         return "<KP9>";
		case GIIK_PA:         return "<KPA>";
		case GIIK_PB:         return "<KPB>";
		case GIIK_PC:         return "<KPC>";
		case GIIK_PD:         return "<KPD>";
		case GIIK_PE:         return "<KPE>";
		case GIIK_PF:         return "<KPF>";

		case GIIK_PPlus:      return "<KP+>";
		case GIIK_PMinus:     return "<KP->";
		case GIIK_PStar:      return "<KP*>";
		case GIIK_PSlash:     return "<KP/>";
		case GIIK_PEnter:     return "<KP Enter>";
		case GIIK_PPlusMinus: return "<KP+/->";
		case GIIK_PParenLeft: return "<KP(>";
		case GIIK_PParenRight: return "<KP)>";
		case GIIK_PSpace:     return "<KP Space>";
		case GIIK_PTab:       return "<KP Tab>";
		case GIIK_PBegin:     return "<KP Begin>";
		case GIIK_PEqual:     return "<KP=>";
		case GIIK_PDecimal:   return "<KP Dec>";
		case GIIK_PSeparator: return "<KP Sep>";

		case GIIK_PF1:        return "<KPF1>";
		case GIIK_PF2:        return "<KPF2>";
		case GIIK_PF3:        return "<KPF3>";
		case GIIK_PF4:        return "<KPF4>";
		case GIIK_PF5:        return "<KPF6>";
		case GIIK_PF6:        return "<KPF6>";
		case GIIK_PF7:        return "<KPF7>";
		case GIIK_PF8:        return "<KPF8>";
		case GIIK_PF9:        return "<KPF9>";

		default: break;
	}

	/* Last resort, use hex value... */

	snprintf(namebuf, 40, "_%4x", value);

	return namebuf;
}

static int key_get_name(gic_handle_t hand, gic_recognizer *ctrl,char *string,size_t maxlen)
{
	struct keypress *keyp=ctrl->privdata;
	char hlpstr[30];
	int shrt=(maxlen < 10);

	switch(keyp->mode) {

	    case LABEL:  
		snprintf(hlpstr, 30, "L%s", name_of_key(keyp->value, 1, shrt));
		break;

	    case SYMBOL: 
		*hlpstr='S'; 
		snprintf(hlpstr, 30, "S%s", name_of_key(keyp->value, 0, shrt));
		break;

	    case BUTTON:
		if (shrt) {
			snprintf(hlpstr, 30, "B%d", keyp->value);
		} else {
			snprintf(hlpstr, 30, "Button%d", keyp->value);
		}
		break;

	    default:
	    	return GGI_ENOMATCH;
	}

	ggstrlcpy(string, hlpstr, maxlen);
	return 0;
}

static int key_check_conflict(gic_handle_t hand, gic_recognizer *ctrl,
			gic_recognizer *ctrl2)
{
	struct keypress *keyp =ctrl ->privdata;
	struct keypress *keyp2=ctrl2->privdata;

	if (ctrl==ctrl2) return GIC_C_ISSAME;
	if (ctrl->driver!=ctrl2->driver) return GIC_C_NOCONFLICT;

	if ( keyp->mode==keyp2->mode && keyp->value==keyp2->value )
		return GIC_C_ISSAME;

	return GIC_C_NOCONFLICT;
}

static int key_get_opposite(gic_handle_t hand, gic_recognizer *recognizer,
				gic_recognizer **opposite)
{
	return GGI_ENOMATCH;	/* FIXME */
}


EXPORTFUNC gic_recognizerdriver *GICdl_keys(void);

gic_recognizerdriver *GICdl_keys(void)
{
	return &mycontrols;
}
