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

   This LibGIC module is a very simple parser for Valuator 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 valuator
{ 
  uint32_t device;
  uint32_t axisnum;
  int32_t min,max;
};


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

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

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

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

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

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

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

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



static gic_recognizerdriver mycontrols= {
	"Valuator",
	valuator_check,
	valuator_get_name,
	valuator_write_pvtdata,
	valuator_read_pvtdata,
	valuator_free_pvtdata,
	valuator_train,
	valuator_check_conflict,
	valuator_get_opposite
};




#define sign(x) ( x > 0 ? 1 : -1 )
#define abs(x)  ( x > 0 ? x : -x )

enum rangeret {
	DISJUNCT,
	INTERSECT,
	SUPERSET,
	SUBSET,
	EQUAL
};

static enum rangeret rangecomp(int32_t mi1,int32_t ma1,int32_t mi2,int32_t ma2)
{
	int32_t hlp;

	/* first reorder all the stuff */
	if (ma1<mi1) { hlp=mi1;mi1=ma1;ma1=hlp; }
	if (ma2<mi2) { hlp=mi2;mi2=ma2;ma2=hlp; }

	if ( ma1<mi2 || ma2<mi1 ) return DISJUNCT;	/* no common parts */
	if ( mi1==mi2 && ma2==ma1 ) return EQUAL;	/* both ranges are equal   */
	if ( mi1<=mi2 && ma2<=ma1 ) return SUPERSET;	/* 1 includes 2 completely */
	if ( mi2<=mi1 && ma1<=ma2 ) return SUBSET;	/* 2 includes 1 completely */

	return INTERSECT;
}

static int getvaluator(gic_handle_t hand, struct valuator *kp,gii_event *event,
			gic_feature *feature,int recnum)
{
	int32_t myval;
	gic_state state;

	DPRINT_LIBS("Valuator: valuatorEvent dev=%08x,first=%08x,count=%08x",
		event->any.origin,event->val.first,event->val.count);

	if ( kp->device != event->any.origin ||
	     kp->axisnum < event->val.first ||
	     kp->axisnum >= event->val.first+event->val.count )
	     	return 0;
	     	
	myval=event->val.value[kp->axisnum-event->val.first];

	/* Check, if the value is within limits. DO NOT make assumptions
	 * about ordering of min/max here !
	 */
	if ( (myval<kp->min && myval<kp->max) || 
	     (myval>kp->min && myval>kp->max) )
		return 0;
	
	state=((double)myval-kp->min)/((double)kp->max-kp->min)*
		((double)GIC_STATE_MAX-GIC_STATE_MIN)+GIC_STATE_MIN;
		
	gicFeatureActivate( hand, feature, state,
		(gic_flag)((event->any.type == evValRelative) ? GIC_FLAG_PULSE : 0),
		recnum);

	return 1;
}

static int valuator_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);

	/* FIXME ! Check ->origin ! */
	if (event->any.type==evValRelative ||
	    event->any.type==evValAbsolute)
	{
		return getvaluator(hand, ctrl->privdata,event, feature, recnum);
	}

	return 0;
}

static int valuator_register(gic_handle_t hand, gic_recognizer **ctrl,gii_event *event)
{
	gic_recognizer *rl;
	struct valuator *mkp;
	unsigned int axisnum;
	int rc;

	rc = 0;
	mkp = NULL;
	for(axisnum = event->val.first;
		axisnum < event->val.first+event->val.count;
		axisnum++)
	{
		for(rl=*ctrl;rl!=NULL;rl=rl->next) {
			if (rl->driver==&mycontrols) {
				mkp=rl->privdata;
				if ( mkp->axisnum == axisnum &&
				     mkp->device  == event->any.origin )
					break;
			}
		}
	
		if (!rl) {	/* Nothing found - attach a new one. */
			rl = malloc(sizeof(gic_recognizer));
			if (rl == NULL) return GGI_ENOMEM;
			mkp = malloc(sizeof(struct valuator));
			if (mkp == NULL) {
				free(rl);
				return GGI_ENOMEM;
			}
	
			rl->driver=&mycontrols;
			rl->confidence=GIC_STATE_MIDDLE;	/* FIXME ! */
			rl->privdata=mkp;
			mkp->min=mkp->max=event->val.value[axisnum-event->val.first];	/* Fixme */
			gicRecognizerTrainAdd(hand, ctrl, rl);
		}

		mkp->device=event->any.origin;
		mkp->axisnum=axisnum;
		/* FIXME - derive order from movement ! */
		if (abs(event->val.value[axisnum-event->val.first]-mkp->min) 
		    > abs(mkp->max-mkp->min) ) {
			mkp->max=event->val.value[axisnum-event->val.first];
		}
		rl->confidence=GIC_STATE_MIDDLE+abs(mkp->max-mkp->min);
		gicRecognizerTrainMove(hand, ctrl, rl);
		rc++;
	}
	return 1;
}

static int valuator_train(gic_handle_t hand, gic_recognizer **ctrl,gii_event *event)
{
	int rc;
	DPRINT_LIBS("Keys: Training with %p,%p.\n",ctrl,event);

	rc=0;
	if (event) {
		DPRINT_LIBS("Keys: Analyzing event ...\n");
		if (event->any.type==evValRelative||
		    event->any.type==evValAbsolute ) {	
			/* O.K. match. Remember the stats */
			DPRINT_LIBS("Keys: Remembering last valuator ...\n");
			rc+=valuator_register(hand, ctrl, event);
			return rc;
		}
	} else {
		DPRINT_LIBS("Keys: Initialized training state.\n");
	}
	return rc;
}

/*
 * Privatedata format is :
 * axisnumber min max (hex, 8 digits) device (string)
 */
 
static int valuator_write_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
				char *string,int maxlen)
{
	struct valuator *valuatorp=ctrl->privdata;
	gii_cmddata_getdevinfo devinfo;
	gii_cmddata_getvalinfo valinfo;


	if (!hand->input || giiQueryDeviceInfo(hand->input, valuatorp->device, &devinfo)) {
		devinfo.shortname[0]='\0';
	}
	if (!hand->input || giiQueryValInfo   (hand->input, valuatorp->device, 
			valuatorp->axisnum, &valinfo)) {
		valinfo.shortname[0]='\0';
	}

	if (maxlen < 40) {
		*string='\0';
		return GGI_ENOSPACE;
	}
	sprintf(string,"%08x %08x (%s) (%s) %08x %08x",
		valuatorp->min,
		valuatorp->max,
		devinfo.shortname,
		valinfo.shortname,
		valuatorp->device,
		valuatorp->axisnum
		);

	return 0;
}

static int valuator_read_pvtdata(gic_handle_t hand, gic_recognizer *ctrl,
				const char *string)
{
	struct valuator *valuatorp;
	char _devname[20],axisname[20];
	uint32_t devfallback,axisfallback;
	uint32_t x,y,device;

	valuatorp=ctrl->privdata=malloc(sizeof(struct valuator));
	sscanf(string,"%x %x (%[^)]) (%[^)]) %x %x",
		(uint32_t *)&valuatorp->min,
		(uint32_t *)&valuatorp->max,
		_devname,
		axisname,
		&devfallback,
		&axisfallback);
	valuatorp->device =devfallback;		/* Hope for good defaults ... */
	valuatorp->axisnum=axisfallback;

        if (hand->input) {
		for(x=0;x<0xffffffff;x++) {
			/* O.K. - ask for all device descriptors ... */
			gii_cmddata_getdevinfo devinfo;
			gii_cmddata_getvalinfo valinfo;

			if (giiQueryDeviceInfoByNumber(hand->input, x, &device, &devinfo )) 
				break;
			
			DPRINT_LIBS("devinfo: %s (%s) %x look for %s\n",
				devinfo.longname,devinfo.shortname,
				device,_devname);
			if (strcmp(devinfo.shortname,_devname)) continue;
			DPRINT_LIBS("devinfo for %x found.\n",device);
			for(y=0;y<0xffffffff;y++) {
				if (giiQueryValInfo(hand->input, device, y, &valinfo))
					break;
				DPRINT_LIBS("valinfo: %s (%s) %d look for %s\n",
					valinfo.longname,valinfo.shortname,
					y,axisname);
				if (strcmp(valinfo.shortname,axisname)) continue;
				DPRINT_LIBS("valinfo found.\n");
				valuatorp->device =device;
				valuatorp->axisnum=y;
				break;
			}
			break;
		}
	}

	return 0;
}

static void valuator_free_pvtdata(gic_handle_t hand, gic_recognizer *ctrl)
{
	struct valuator *valuatorp=ctrl->privdata;

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

static int valuator_get_name(gic_handle_t hand, gic_recognizer *ctrl,
			char *string,size_t maxlen)
{
	struct valuator *valuatorp=ctrl->privdata;
	char hlpstr[50];
	gii_cmddata_getvalinfo valinfo;

	if (hand->input) {
		giiQueryValInfo(hand->input, valuatorp->device, 
			valuatorp->axisnum, &valinfo);

		snprintf(hlpstr, 50, "%s%c", 
			valinfo.shortname, 
			valuatorp->min < valuatorp->max ? '+' : '-' );
	} else {
		snprintf(hlpstr, 50, "V%x_%d_%d_%d", 
			valuatorp->device, valuatorp->axisnum, 
			valuatorp->min,valuatorp->max);
	}

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

static int valuator_check_conflict(gic_handle_t hand, gic_recognizer *ctrl,gic_recognizer *ctrl2)
{
	struct valuator *valuatorp =ctrl ->privdata;
	struct valuator *valuatorp2=ctrl2->privdata;

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

	if ( valuatorp->device  == valuatorp2->device &&
	     valuatorp->axisnum == valuatorp2->axisnum )
	{
		switch(rangecomp(valuatorp->min,valuatorp->max,
				valuatorp2->min,valuatorp2->max)) {
			case DISJUNCT : return GIC_C_NOTATSAMETIME;
			case SUPERSET :
			case SUBSET   : return GIC_C_ALWAYSATSAMETIME;
			case EQUAL    : return GIC_C_ISSAME;
			case INTERSECT: return GIC_C_MAYBECONFLICT;
		}
	}
	return GIC_C_NOCONFLICT;
}

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


EXPORTFUNC gic_recognizerdriver *GICdl_valuator(void);

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