/*
Copyright (c) 2007-2013, John Ryland
*/
#include <oslayer.h>
#include <corelayer.h>
#include <keypad.h>
#include <event.h>


#if defined(ARCH_MacOSX64)

#define VMIN		16	/* !ICANON */
#define VTIME		17	/* !ICANON */
#define	NCCS		20
typedef unsigned long	tcflag_t;
typedef unsigned char	cc_t;
typedef unsigned long	speed_t;
struct termios {
	tcflag_t	c_iflag;	/* input flags */
	tcflag_t	c_oflag;	/* output flags */
	tcflag_t	c_cflag;	/* control flags */
	tcflag_t	c_lflag;	/* local flags */
	cc_t		c_cc[NCCS];	/* control chars */
	speed_t		c_ispeed;	/* input speed */
	speed_t		c_ospeed;	/* output speed */
};

// These are in hex
#define	ICANON		0x00000100	/* canonicalize input lines */
#define IGNBRK		0x0000001
#define BRKINT		0x0000002
#define IGNPAR		0x0000004
#define PARMRK		0x0000008
#define INPCK		0x0000010
#define ISTRIP		0x0000020
#define INLCR		0x0000040
#define IGNCR		0x0000080
#define ICRNL		0x0000100
#define IXON		0x0000200
#define IXANY		0x0000800
#define IXOFF		0x0000400
#define IMAXBEL		0x0002000
#define CSIZE		0x0000300
#define   CS5		0x0000000
#define   CS6		0x0000100
#define   CS7		0x0000200
#define   CS8		0x0000300
#define CSTOPB		0x0000400
#define CREAD		0x0000800

#define IOCTL_GET_FLAGS 0x40487413
#define IOCTL_SET_FLAGS 0x80487416

#else

// These are in octal - some are same, some are different though
#define IGNBRK		0000001
#define BRKINT		0000002
#define IGNPAR		0000004
#define PARMRK		0000010
#define INPCK		0000020
#define ISTRIP		0000040
#define INLCR		0000100
#define IGNCR		0000200
#define ICRNL		0000400
#define IXON		0002000
#define IXANY		0004000
#define IXOFF		0010000
#define IMAXBEL		0020000
#define CSIZE		0000060
#define   CS5		0000000
#define   CS6		0000020
#define   CS7		0000040
#define   CS8		0000060
#define CSTOPB		0000100
#define CREAD		0000200

#define IOCTL_GET_FLAGS	0x5405
#define IOCTL_SET_FLAGS	0x5406

#endif


const char *keyTab1[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Backspace", 0,
	"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", 0, 0,
	"Enter", "Fn", "A", "S", "D", "F", "G", "H", "J", "K", "L",
	0, 0, 0, 0, 0, "Z", "X", "C", "V", "B", "N", "M", ",", ".",
	0, "Shift", 0, 0, "Space", "Sym", 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, "Num", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, "Up", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, "Right", 0, "Select", 0, 0, 0, "Hold", "Down", 0,
	0, 0, 0, "Pwr", 0, 0, 0, 0, 0, 0, 0, 0
};

const char *keyTab2[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Info", 0, "Back",
	0, "Home", 0, 0, "Options", "Wireless", "Slide", 0, "Backward",
	0, 0, 0, 0, 0, "VolUp", "Play", 0, 0, 0, 0, 0, "VolDn", 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Up2", 0, 0, "Left2", 0,
	"Right2", 0, 0, "Down2", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Forward", 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Left", 0, 0, 0, 0, 0, 0
};

const char *ttyKeyTab[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Select", 0, 0, "Enter", 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Esc", 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, "Insert", "Delete", 0, "PgUp", "PgDn", 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, "Up", "Down", "Right", "Left", 0, "End", 0, "Home", 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

extern int keyFd;
int keyFd;
int haveOriginalFlags = 0;

#if defined(ARCH_MacOSX64)
unsigned long savedFlags[9];
#else
unsigned short savedFlags[12];
#endif

static void preserveFlags()
{
	if ( !haveOriginalFlags ) {
		haveOriginalFlags = 1;
		sysIoctl(keyFd, IOCTL_GET_FLAGS, savedFlags);	// Get flags
	}
}

static void restoreFlags()
{
	if ( haveOriginalFlags ) {
		sysIoctl(keyFd, IOCTL_SET_FLAGS, savedFlags);	// Set flags
	}
/*
#if defined(ARCH_MacOSX64)
        unsigned long flags[9];
#else
	unsigned short flags[12];
#endif
	sysIoctl(keyFd, IOCTL_GET_FLAGS, flags);	// Get flags
	flags[3] = (flags[3] | 0110) & ~03;		// Modify flags
	sysIoctl(keyFd, IOCTL_SET_FLAGS, flags);	// Set flags
*/
}

static void uncook()
{
#if defined(ARCH_MacOSX64)
	preserveFlags();
        struct termios tflags;
	sysIoctl(keyFd, IOCTL_GET_FLAGS, &tflags);
        tflags.c_lflag &= ~(ICANON);
        tflags.c_cc[VMIN] = 1;
	tflags.c_cc[VTIME] = 0;

	//tflags.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
	tflags.c_oflag = 0;
	//tflags.c_cflag = CREAD | CS8;
	tflags.c_lflag = 0;

	sysIoctl(keyFd, IOCTL_SET_FLAGS, &tflags);
#else
	unsigned short flags[12];
	preserveFlags();

	//sysIoctl(keyFd, IOCTL_GET_FLAGS, flags);	// Get flags

	memcpy(flags, savedFlags, 12*sizeof(short));

	flags[0] = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
	flags[1] = 0;
	flags[2] = CREAD | CS8;
	flags[3] = 0;

//	flags[0] &= ~0002753;
//	flags[1] &= ~0000001;
//	flags[2] = (flags[2] & ~0000460) | 0000060;
//	flags[3] &= ~0100113;

	sysIoctl(keyFd, IOCTL_SET_FLAGS, flags);	// Set flags

//	sysIoctl(0, 0x4B45, 0);
#endif
}

void openKeypad()
{
#if defined(ARCH_ARM)
	keyFd = fsOpen("/dev/vc/0", O_RDWR | O_NONBLOCK);
#else
	keyFd = fsOpen("/dev/tty", O_RDWR | O_NONBLOCK);
	uncook();
#endif
}

void closeKeypad()
{
	restoreFlags();
	fsClose(keyFd);
}

void readKeyData()
{
	unsigned int ch = 0;
	while (fsRead(keyFd, &ch, 1) != -1) {
		const char *key = 0;
#if defined(ARCH_ARM)
		if ((ch & 127) != 96) {
			key = keyTab1[ch & 127];
		} else {
			fsRead(keyFd, &ch, 1);
			key = keyTab2[ch & 127];
		}
#else
//		strPrintf("Got key: %i  (%c)\n", ch, ch);
		if ( ch == 3 ) {
			strPrintf("Got CTRL-C, exiting\n");
			restoreFlags();
			sysExit(0);
		}
		char keyBuf[2];
		keyBuf[0] = ch;
		keyBuf[1] = '\0';
		key = ttyKeyTab[ch & 127];
		if ( !key )
			key = keyBuf;
		if (ch == 27) {
			key = 0;
			fsRead(keyFd, &ch, 1);
			fsRead(keyFd, &ch, 1);
			key = ttyKeyTab[ch & 127];
			if ( !key )
			{
				strPrintf("Got key: %x  (%c)\n", ch, ch);
				break;
			}
		}
#endif
		if ( key ) {
			Event keyEv;
			keyEv.type = KeyEventType;
			keyEv.event.keyEvent.desc = key;
			keyEv.event.keyEvent.code = ch; // XXX
			keyEv.event.keyEvent.pressed = (ch & 128) ? 0 : 1;
			eventQueue_AppendEvent(keyEv);
			break;
		}
	}
}

