/*  Tilem, TI-Linux Emulator
 *  Copyright (C) 2001 Solignac Julien <x1cygnus@xcalc.org>
 *  Portions copyright (C) 2004 Benjamin Moody <benjamin@ecg.mit.edu>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "../../config.h"

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<gdk/gdk.h>
#include<gdk/gdkkeysyms.h>

#include "core/Z80.h"
#include "core/dep/inter.h"

#include "tools.h"
#include "keypad.h"

struct keymap {
	guint k;
	byte v;
	short seqlen;
	byte *seq;
};


struct keymap *x3kmap;
struct keymap *x7kmap;
struct keymap *x6kmap;

char *x3sknames[]={"Down","Left","Right","Up","sk05","sk06","sk07","sk08",
		   "Enter","Add","Sub","Mul","Div","Power","Clear","sk10",
		   "Chs","3","6","9","RParen","Tan","Vars","sk18",
		   "DecPnt","2","5","8","LParen","Cos","Prgm","Stat",
		   "0","1","4","7","Comma","Sin","Matrix","Graphvar",
		   "On","Store","Ln","Log","Square","Recip","Math","Alpha",
		   "Graph","Trace","Zoom","Window","YEqu","2nd","Mode","Del",
		   NULL};

char *x6sknames[]={"Down","Left","Right","Up","sk05","sk06","sk07","sk08",
		   "Enter","Add","Sub","Mul","Div","Power","Clear","sk10",
		   "Chs","3","6","9","RParen","Tan","Custom","sk18",
		   "DecPnt","2","5","8","LParen","Cos","Prgm","Del",
		   "0","1","4","7","EE","Sin","Table","Graphvar",
		   "On","Store","Comma","Square","Ln","Log","Graph","Alpha",
		   "F5","F4","F3","F2","F1","2nd","Exit","More",
		   NULL};

char *x7sknames[]={"Down","Left","Right","Up","sk05","sk06","sk07","sk08",
		   "Enter","Add","Sub","Mul","Div","Const","Clear","sk10",
		   "Chs","3","6","9","RParen","MixSimp","AppsMenu","sk18",
		   "DecPnt","2","5","8","LParen","FracDec","Prgm","StatEd",
		   "0","1","4","7","Percent","FracSlash","Expon","Draw",
		   "On","Store","Comma","VarX","Simp","Unit","Square","Math",
		   "Graph","Trace","Zoom","Window","YEqu","2nd","Mode","Del",
		   NULL};

byte lookupkey(char *skname, char *mapname, char *sknames[], int n)
{
	int i;

	for (i = 0; sknames[i]; i++)
		if (0==strncasecmp(sknames[i], skname, n))
			return i+1;

	fprintf(stderr,
		"Unknown calculator key '%s' in %s\n",
		skname, mapname);

	return 0xFF;
}

void loadkeys(struct keymap **map, char *mapname, char *sknames[])
{
	FILE *mapfile;
	char *name;
	char line[256];
	char keyname[256], skname[256];
	int nkeys, i, j;
	char *p, *q;

	name = ashare(mapname);
	if ((mapfile = fopen(name, "r"))) {

		nkeys=0;
		while (fgets(line, 256, mapfile))
			nkeys++;

		rewind(mapfile);
		*map = malloc((nkeys + 1) * sizeof(struct keymap));

		for (i = 0; i < nkeys; i++) {
			fgets(line, 256, mapfile);
			sscanf(line,"%s%s",keyname,skname);

			if (((*map)[i].k =
			     gdk_keyval_from_name(keyname))
			    == GDK_VoidSymbol) {
				fprintf(stderr,"Unknown GDK key '%s' in %s\n",
					keyname, mapname);
			}

			(*map)[i].seqlen = 0;
			p = skname-1;
			while ((p = strchr(p+1, ',')))
				(*map)[i].seqlen++;

			if ((*map)[i].seqlen) {
				(*map)[i].seqlen++;

				(*map)[i].seq = malloc((*map)[i].seqlen *
						       sizeof(byte));

				p = skname;
				for (j = 0; j < (*map)[i].seqlen; j++) {
					q = strchr(p, ',');

					(*map)[i].seq[j] = 
						lookupkey(p, mapname,
							  sknames,
							  q?q-p:256);

					p = q+1;
				}

				(*map)[i].v = 0xff;
			}
			else {
				(*map)[i].v = lookupkey(skname, mapname,
							sknames, 256);
			}

		}
		
		(*map)[nkeys].k = 0;
		(*map)[nkeys].v = 0;
	}

	free(name);
}

void load_keymaps()
{

#ifdef __WIN32__
# define DATADIR "..\\data\\"
#else
# define DATADIR "../data/"
#endif

	loadkeys(&x3kmap, DATADIR "x3keys", x3sknames);
	loadkeys(&x6kmap, DATADIR "x6keys", x6sknames);
	loadkeys(&x7kmap, DATADIR "x7keys", x7sknames);
}


unsigned int onkey;

#define KEYTIME 16

int keytmr=0;
byte *keyseq=NULL;
int kspos=0;
int kslen=0;

void updatekb()
{
	if (kslen && keytmr==0) {
		keytmr = KEYTIME;
		xkeyup(0, keyseq[kspos]-1);
		kspos++;
		if (kspos<kslen) {
			xkeyin(0, keyseq[kspos]-1);
		}
		else {
			kslen = 0;
		}
	}

	if (keytmr)
		keytmr--;
}


int xkeyin(guint keyv, byte i)
{
	int j;
	struct keymap *kmap;
	extern struct hardware *hw;

	if (hw->model == '6' || hw->model == '5') kmap = x6kmap;
	else if (hw->model == '7') kmap = x7kmap;
	else kmap = x3kmap;

	if (i == 0xFF) {
		for (j=0; kmap[j].v; j++) {
			if (keyv == kmap[j].k) {
				i = kmap[j].v - 1;

				if (kmap[j].seqlen) {
					if (kslen)
						/* cancel an existing
						   sequence if one is
						   being run */
						xkeyup(0, keyseq[kspos]-1);

					kslen = kmap[j].seqlen;
					keytmr = KEYTIME;
					keyseq = kmap[j].seq;
					kspos = 0;
					i = keyseq[0] - 1;
				}

				break;
			}
		}
	}

	if (i == 40) {
		onkey = -1;
		return 1;
	}

	if (i < 0x40) {
		scode[i >> 3] &= ~(1 << (i & 0x07));
		return 1;
	}

	return 0;
}

int xkeyup(guint keyv, byte i)
{
	int j;
	struct keymap *kmap;
	extern struct hardware *hw;

	if (hw->model == '6') kmap = x6kmap;
	else if (hw->model == '7') kmap = x7kmap;
	else kmap = x3kmap;

	if (i == 0xFF) {
		for (j=0; kmap[j].v; j++) {
			if (keyv == kmap[j].k) {
				i = kmap[j].v - 1;
				break;
			}
		}
	}

	if (i == 40) {
		onkey = 0;
		return 1;
	}

	if (i < 0x40) {
		scode[i >> 3] |= 1 << (i & 0x07);
		return 1;
	}

	return 0;
}

