#include <shinobi.h>
#include "kbd.h"

#define DEBUG 0
#define PDD_KBD_BUFSIZE 4
extern const char pdgKeyTbl_Normal[];
extern const char pdgKeyTbl_Shift[];
extern const char pdgKeyTbl_Alt[];
extern const char pdgKeyTbl_Gui[];

static const char* pdgKeyTbl[] = {
	pdgKeyTbl_Normal,			/* 通常状態 */
	pdgKeyTbl_Shift,			/* シフトキー押下状態 */
	pdgKeyTbl_Alt,				/* ALTキー押下状態 */
	pdgKeyTbl_Gui,				/* GUIキー押下状態 */
};

static PDS_KBDSTRUCT pdgKbdState[4];
static Uint8 pdgKbdBuf[4][PDD_KBD_BUFSIZE];




/******** 低レベルキーボード関数 *******************************************/
#if 0
const PDS_KEYBOARDINFO* pdGetKeyboardInfo(Uint32 port)
{
}
#endif


void pdKbdInit(void)
{
	memset(pdgKbdState, 0, sizeof(pdgKbdState));
	memset(pdgKbdBuf, 0, sizeof(pdgKbdBuf));
}


static const Sint32 pdgKbdPort2No[] = {
	0, -1, -1, -1, -1, -1,
	1, -1, -1, -1, -1, -1,
	2, -1, -1, -1, -1, -1,
	3, -1, -1, -1, -1, -1,
};

void pdKbdSetState(PD_KBD hKbd, Uint8 rate, Uint8 delay)
{
	hKbd->rate = rate;
	hKbd->delay = delay;
}

void pdKbdGetState(PD_KBD hKbd, Uint8* rate, Uint8* delay)
{
	*rate = hKbd->rate;
	*delay = hKbd->delay;
}

Sint32 pdKbdIsHit(PD_KBD hKbd)
{
	return (hKbd->getp == hKbd->putp) ? FALSE : TRUE;
}

void pdKbdExecServer(void)
{
	Sint32 i;
	PD_KBD kbd;

	kbd = pdgKbdState;
	for (i = 0; i < 4; i++, kbd++) {
		if (kbd->used) {
			pdKbdExecServerOne(kbd);
		}
	}
}

Sint32 kbd_comp(Uint8* dst, Uint8* src)
{
	Sint32 i;

	for(i = 0; i < 6; i++, dst++, src++) {
		if (*dst != *src) return FALSE;
	}

	return TRUE;
}

Sint32 search_predata(PD_KBD hKbd)
{
	Sint32 i;

	for (i = 0; i < 6; i++) {
		if (hKbd->predata == hKbd->old_data[i + 2]) return TRUE;
	}
	return FALSE;
}

void pdKbdExecServerOne(PD_KBD hKbd)
{
	const PDS_KEYBOARD* kbd;
	const char* keytbl;

	Uint8 data;
	Sint32 i;
	Sint32 input_flag;

	input_flag = FALSE;

	kbd = pdKbdGetData(hKbd->port);
	if (kbd == NULL) return;

	for (i = 0; i < 6; i++) {
		data = kbd->key[5 - i];
		switch (data) {
			case 0x00:
				break;
			case 0x01:
				break;
			default:
				if (((hKbd->putp + 1) & (hKbd->bufsize - 1)) == hKbd->getp) {
					/* リングバッファフル */
					return;
				}
				goto skip;
		}
	}

	hKbd->delay_count = 0;
	hKbd->repeat_flag = FALSE;
	hKbd->predata = 0;
	return;
skip:
#if DEBUG
	con_printf("skip:data=%02x,pre=%02x  ", data, hKbd->predata);
#endif
	if (data == hKbd->predata) {
		/* 同じだった */
#if DEBUG
		con_printf("SAME ");
#endif
		hKbd->delay_count++;
		if (hKbd->delay_count > hKbd->delay) {
			hKbd->repeat_flag = TRUE;
		}
	} else {
		/* 違った */
#if DEBUG
		con_printf("DIFF ");
#endif
		hKbd->delay_count = 0;
		hKbd->repeat_flag = FALSE;
		input_flag = TRUE;
	}


	if (hKbd->repeat_flag) {
		hKbd->rate_count++;
		if (hKbd->rate_count >= hKbd->rate) {
			input_flag = TRUE;
			hKbd->rate_count = 0;
		}
	} else {
		hKbd->rate_count = 0;
	}
#if DEBUG
	con_printf("rep=%3d,delay=%3d,rate=%3d,input=%3d\n", hKbd->repeat_flag, hKbd->delay_count, hKbd->rate_count, input_flag);
#endif
	if (input_flag) {
		long shift, ctrl, alt, gui;
		shift = kbd->ctrl & (PDD_KEY_CTRL_LSHIFT | PDD_KEY_CTRL_RSHIFT) ? 1 : 0;
		ctrl  = kbd->ctrl & (PDD_KEY_CTRL_LCTRL  | PDD_KEY_CTRL_RCTRL)  ? 1 : 0;
		alt   = kbd->ctrl & (PDD_KEY_CTRL_LALT   | PDD_KEY_CTRL_RALT)   ? 1 : 0;
		gui   = kbd->ctrl & (PDD_KEY_CTRL_LGUI   | PDD_KEY_CTRL_RGUI)   ? 1 : 0;

		if (ctrl || alt || gui) keytbl = pdgKeyTbl[2];
		else keytbl = pdgKeyTbl[shift];
		hKbd->buf[hKbd->putp++] = keytbl[data];
		hKbd->putp &= (hKbd->bufsize - 1);
		hKbd->predata = data;
	}
}

PD_KBD pdKbdCreate(Uint32 port)
{
	Sint32 no;
	PD_KBD kbd;
	if ((no = pdgKbdPort2No[port]) < 0) return NULL;

	kbd = &pdgKbdState[no];

	if (kbd->used) return NULL;

	kbd->used = TRUE;
	kbd->rate = 2;
	kbd->delay = 10;
	kbd->rate_count = 0;
	kbd->delay_count = 0;
	kbd->repeat_flag = 1;
	kbd->port = port;
	kbd->putp = 0;
	kbd->getp = 0;
	kbd->bufsize = PDD_KBD_BUFSIZE;
	kbd->buf = pdgKbdBuf[no];
	kbd->predata = 0xff;
	return kbd;
}

void pdKbdDestroy(PD_KBD kbd)
{
	kbd->used = FALSE;
}



char pdKbdGetChar(PD_KBD hKbd)
{
	Uint8 data;

	if (hKbd->getp == hKbd->putp) {
		/* リングバッファエンプティ */
		return 0;
	}

	data = hKbd->buf[hKbd->getp++];
	hKbd->getp &= hKbd->bufsize - 1;

	return data;
}

Sint32 pdKbdGetString(PD_KBD hKbd, char* buf, Sint32 size)
{
	hKbd->line = buf;

}
