#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "memtype.h"
#include "6502.h"
#include "atari.h"


/* Atari Keyboard emulation
 *   by Frank Barrus
 */

#define KType_Mask		0x38
#define KType_Press		0x40
#define KType_Release		0x80
#define KType_Key		0x08
#define KType_Console		0x10	/* key = console key to set */
#define KType_Special		0x18	/* key = special key selection */
#define KType_Stick		0x20	/* low 2 bits select stick #, key = direction */
#define KType_Paddle		0x28	/* low 3 bits select paddle #, key = signed adjustment */

#define SKey_Warm		0x01
#define SKey_Cold		0x02
#define SKey_Halt		0x03
#define SKey_Shift		0x04
#define SKey_Ctrl		0x05
#define SKey_Break		0x06
#define SKey_NextStick		0x10
#define SKey_PrevStick		0x11


typedef struct keynode {
	char	ch;
	byte	type;
	byte	scan;
	struct keynode	*next;
	struct keynode	*down;
} keynode;


static keynode *ktree;
static byte scanbuf[40];
static int sibuf = 0;
static int sobuf = 0;
static int empty = True;


static void addkey(int type, int scan, char *str)
{
keynode **kp;
keynode *tp = 0;
char ch;
	kp = &ktree;
	while(ch = *str++, (ch != '\t' && ch != ' ')) {
		if(ch == '\\') {
			ch = *str++ - '0';
			ch = (ch*8) + *str++ - '0';
			ch = (ch*8) + *str++ - '0';
		}
		/* printf("ch=%02x\n", ch); */
		while(*kp) {
			if((*kp)->ch == ch) 
				goto found;
			kp = &((*kp)->next);
		}
		tp = (keynode*)malloc(sizeof(keynode));
		tp->ch = ch;
		tp->type = 0;
		tp->down = NULL;
		tp->next = *kp;
		*kp = tp;
	found:
		tp = *kp;
		kp = &tp->down;
	}
	if(!tp)
		return;
	if(tp->type)
		printf("Keytable error: repeat for %02x/%02x\n", scan, type);
	tp->scan = scan;
	tp->type = type;
}

void print_tree(keynode *kp)
{
	if(!kp)
		return;
	printf("( ");
	while(kp) {
		if(kp->type)
			printf("%02x:%02x/%02x ", (byte)(kp->ch),
					(byte)(kp->type), (byte)(kp->scan));
		else
			printf("%02x ", (byte)(kp->ch));
		print_tree(kp->down);
		kp = kp->next;		
	}
	printf(") ");
}


void keyboard_init(char *keyfile)
{
char *tmp;
FILE *f;
char str[100];
int code = 0;
	ktree = NULL;
	if((tmp = getenv("ACE_KEYMAP")))
		keyfile = tmp;
	f = fopen(keyfile, "r");
	while(fgets(str, sizeof(str), f)) {
		if(str[0] == ':')
			code = strtol(&str[1], NULL, 16);
		else if(str[0] >= '0' && str[0] <= '9' && str[3] != '\t')
			addkey(code, strtol(str, NULL, 16), &str[3]);
	}
	/* print_tree(ktree); 
	printf("\n"); */
	fclose(f);
}




static void key_release(int p);

static void key_press(int key)
{
	empty = False;
	sys.keycode = sys.keylast = key;
	sys.sk_status |= 0x04;
	SET_IRQ(0x40);
	cpu_schedule(SCHED_KB, MSEC(2), key_release, 0);
}

static void key_release(int p)
{
	sys.keycode = 0xff;
	sys.sk_status &= ~0x04;
	if(sibuf != sobuf) {
		cpu_schedule(SCHED_KB, MSEC(4), key_press, scanbuf[sobuf]);
		sobuf = (sobuf+1)%sizeof(scanbuf);
	} else
		empty = True;
}


static void console_release(int p)
{
	sys.console |= p;
}

static void trig_release(int t)
{
}

static void ptrig_release(int t)
{
}



void keyboard_press_ascii(char ch)
{
static int shiftstate = 0;
static keynode *kp = NULL;
static int stick = 0;
int n;
	if(ch == '\0' && kp)
		goto found;
	if(!kp)
		kp = ktree;
	else
		kp = kp->down;
	while(kp) {
		if(kp->ch == ch)
			if(kp->down)
				return;
			else
				goto found;
		kp = kp->next;
	}
	printf("\007");
	fflush(stdout);
	return;
found:
	switch(kp->type & KType_Mask) {
	case KType_Key:
		if(empty)
			key_press(kp->scan|shiftstate);
		else {
			scanbuf[sibuf] = kp->scan|shiftstate;
			sibuf = (sibuf+1)%sizeof(scanbuf);
		}
		shiftstate = 0;
		break;
	case KType_Stick: 
		n = (kp->type+stick) & 3;
		if(kp->type & KType_Press) {
			sys.strig[n] = True;
			if(kp->type & KType_Release)
				cpu_schedule(SCHED_KB2, MSEC(20),
					trig_release, n);
		} else if(kp->type & KType_Release)
			sys.strig[n] = False;
		else
			sys.stick[n] = kp->scan;
		break;
	case KType_Paddle: 
		n = (kp->type+stick) & 7;
		if(kp->type & KType_Press) {
			sys.stick[n/2] &= ~(8>>(n&1));
			if(kp->type & KType_Release)
				cpu_schedule(SCHED_KB2, MSEC(20),
					ptrig_release, n);
		} else if(kp->type & KType_Release)
			sys.stick[n/2] |= (8>>(n&1));
		else 
			sys.paddle[n] += (signed char)kp->scan;
		break;
	case KType_Console:
		sys.console &= ~kp->scan;
		if(kp->type & KType_Release)
			cpu_schedule(SCHED_KB2, MSEC(20),
				console_release, kp->scan);
		break;
	case KType_Special:
		switch(kp->scan) {
		case SKey_Warm:		atari_warmstart(); break;
		case SKey_Cold:		atari_coldstart(); break;
		case SKey_Halt:		cpu.halt++;	break;
		case SKey_Break:	SET_IRQ(0x80);	break;
		case SKey_Shift:	shiftstate |= 0x40;
		case SKey_Ctrl:		shiftstate |= 0x80;
		case SKey_NextStick:	stick++;	break;
		case SKey_PrevStick:	stick--;	break;
		}
	}
	kp = NULL;
}


