/*  PADwin
 *  Copyright (C) 2002-2004  PADwin Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <string.h>
#include <stdarg.h>

#include "PAD.h"

char *libraryName      = "PADwinKeyb Driver";

const unsigned char version  = PS2E_PAD_VERSION;
const unsigned char revision = 1;
const unsigned char build    = 0;    // increase that with each version


u32 pads=0;
u8 stdpar[2][20] = { {0xff, 0x5a, 0xff, 0xff, 0x80, 0x80, 0x80, 0x80,
				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
					 0x00, 0x00, 0x00, 0x00},
					 {0xff, 0x5a, 0xff, 0xff, 0x80, 0x80, 0x80, 0x80,
				     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
					 0x00, 0x00, 0x00, 0x00}};
u8 cmd40[2][8]    = { {0xff, 0x5a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5a},
					 {0xff, 0x5a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5a}};
u8 cmd41[2][8]    = { {0xff, 0x5a, 0xff, 0xff, 0x03, 0x00, 0x00, 0x5a},
					 {0xff, 0x5a, 0xff, 0xff, 0x03, 0x00, 0x00, 0x5a}};
u8 unk46[2][8]    = { {0xFF, 0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A},
					 {0xFF, 0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}};
u8 unk47[2][8]    = { {0xff, 0x5a, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00},
					 {0xff, 0x5a, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}};
u8 unk4c[2][8]    = { {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
					 {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
u8 unk4d[2][8]    = { {0xff, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
					 {0xff, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
u8 cmd4f[2][8]    = { {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a},
					 {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a}};
u8 stdcfg[2][8]   = { {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
					 {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // 2 & 3 = 0
u8 stdmode[2][8]  = { {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
					 {0xff, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
u8 stdmodel[2][8] = { {0xff, 0x5a, 
					  0x03, // 03 - dualshock2, 01 - dualshock
					  0x02, // number of modes
					  0x01, // current mode: 01 - analog, 00 - digital
					  0x02,
					  0x01,
					  0x00},
					  {0xff, 0x5a, 
					  0x03, // 03 - dualshock2, 01 - dualshock
					  0x02, // number of modes
					  0x01, // current mode: 01 - analog, 00 - digital
					  0x02,
					  0x01,
					  0x00}};

u8 *buf;
int padID[2];
int padMode[2];
int curPad;
int curByte;
int curCmd;
int cmdLen;
int ds2mode = 0; // DS Mode at start
FILE *padLog = NULL;

int POV(u32 direction, u32 angle){
	if ((direction==0) && (angle>=    0) && (angle< 4500))	return 1;//forward
	if ((direction==2) && (angle>= 4500) && (angle<13500))	return 1;//right
	if ((direction==1) && (angle>=13500) && (angle<22500))	return 1;//backward
	if ((direction==3) && (angle>=22500) && (angle<31500))	return 1;//left
	if ((direction==0) && (angle>=31500) && (angle<36000))	return 1;//forward
	return 0;
}

void _KeyPressNE(u32 key) {
	int i;

	if (pads & 0x1) {
		for (i=0; i<16; i++) {
			if (key == conf.keys[0][i]) {
				status[0]&=~(1<<i);
				return;
			}
		}
	}
	if (pads & 0x2) {
		for (i=0; i<16; i++) {
			if (key == conf.keys[1][i]) {
				status[1]&=~(1<<i);
				return;
			}
		}
	}
}

void _KeyPress(u32 key) {
	_KeyPressNE(key);
	event.event = KEYPRESS;
	event.key = key;
}

void _KeyReleaseNE(u32 key) {
	int i;

	if (pads & 0x1) {
		for (i=0; i<16; i++) {
			if (key == conf.keys[0][i]) {
				status[0]|= (1<<i);
				return;
			}
		}
	}
	if (pads & 0x2) {
		for (i=0; i<16; i++) {
			if (key == conf.keys[1][i]) {
				status[1]|= (1<<i);
				return;
			}
		}
	}
	event.event = KEYRELEASE;
	event.key = key;
}

void _KeyRelease(u32 key) {
	_KeyReleaseNE(key);
	event.event = KEYRELEASE;
	event.key = key;
}

u32 CALLBACK PS2EgetLibType() {
	return PS2E_LT_PAD;
}

char* CALLBACK PS2EgetLibName() {
	return libraryName;
}

u32 CALLBACK PS2EgetLibVersion2(u32 type) {
	return (version<<16)|(revision<<8)|build;
}

void __Log(char *fmt, ...) {
	va_list list;

    if (!conf.log || padLog == NULL) return;
	va_start(list, fmt);
	vfprintf(padLog, fmt, list);
	va_end(list);
}


s32 CALLBACK PADinit(u32 flags) {
#ifdef PAD_LOG
	if (padLog == NULL) {
		padLog = fopen("logs/padLog.txt", "w");
		if (padLog) setvbuf(padLog, NULL,  _IONBF, 0);
	}
	PAD_LOG("PADinit\n");
#endif

	pads|= flags;
	status[0] = 0xffff;
	status[1] = 0xffff;

	LoadConfig();

	PADsetMode(0, 0);
	PADsetMode(1, 0);

	pressure = 5; // basic
	ranalog.x = 0x80;
	ranalog.y = 0x80;
	ranalog.button = 0;
	lanalog.x = 0x80;
	lanalog.y = 0x80;
	lanalog.button = 0;

	return 0;
}

void CALLBACK PADshutdown() {
#ifdef PAD_LOG
    if( padLog != NULL ) {
        fclose(padLog);
        padLog = NULL;
    }
#endif
}

s32 CALLBACK PADopen(void *pDsp) {
	memset(&event, 0, sizeof(event));

	return _PADopen(pDsp);
}

void CALLBACK PADclose() {
	_PADclose();
}

u32 CALLBACK PADquery() {
	return 3; // both
}

void PADsetMode(int pad, int mode) {
	padMode[pad] = mode;
	switch(ds2mode) {
		case 0: // dualshock
			switch (mode) {
				case 0: // digital
					padID[pad] = 0x41;
					break;

				case 1: // analog
					padID[pad] = 0x73;
					break;
			}
			break;
		case 1: // dualshock2
			switch (mode) {
				case 0: // digital
					padID[pad] = 0x41;
					break;

				case 1: // analog
					padID[pad] = 0x79;
					break;
			}
			break;
	}
}

u8   CALLBACK PADstartPoll(int pad) {
#ifdef PAD_LOG
	PAD_LOG("PADstartPoll: %d\n", pad);
#endif
	curPad = pad-1;
	curByte = 0;

	return 0xff;
}

/*
u8   PADpollDigital(u8 value) {
	u8 ret;

	if (curByte == 0) {
		curByte++;
#ifdef PAD_LOG
		PAD_LOG("PADpoll: cmd: %x\n", value);
#endif

		curCmd = value;
		stdpar[2] = status[curPad-1] >> 8;
		stdpar[3] = status[curPad-1] & 0xff;
		cmdLen = 4;
		buf = stdpar;
		return 0x41;
	}

	if (curByte >= cmdLen) return 0;
	return buf[curByte++];
}*/

u8  _PADpoll(u8 value) {
	u8 button_check = 0, button_check2 = 0;

	if (curByte == 0) {
		curByte++;
#ifdef PAD_LOG
		PAD_LOG("PADpoll: cmd: %x\n", value);
#endif

		curCmd = value;
		switch (value) {
			case 0x40: // DUALSHOCK2 ENABLER 
				cmdLen = 8;
				buf = cmd40[curPad];
				return 0xf3;

			case 0x41: // QUERY_DS2_ANALOG_MODE
				cmdLen = 8;
				buf = cmd41[curPad];
				return 0xf3;

			case 0x42: // READ_DATA
				if(lanalog.button) status[curPad] &= ~(1<<9);
				else status[curPad] |= (1<<9);
				if(ranalog.button) status[curPad] &= ~(1<<10);
				else status[curPad] |= (1<<10);

				lanalog.button = 0; // reset them :p (l3)
				ranalog.button = 0; // reset them :P (r3)

				stdpar[curPad][2] = status[curPad] >> 8;
				stdpar[curPad][3] = status[curPad] & 0xff;
				stdpar[curPad][4] = ranalog.x;
				stdpar[curPad][5] = ranalog.y;
				stdpar[curPad][6] = lanalog.x;
				stdpar[curPad][7] = lanalog.y;
				if (padMode[curPad] == 1) cmdLen = 20;
				else cmdLen = 4;
				button_check2 = stdpar[curPad][2] >> 4;
				switch(stdpar[curPad][3])
				{
				case 0xBF: // X
					stdpar[curPad][14] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][16]));
					break;
				case 0xDF: // Circle
					stdpar[curPad][13] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][17]));
					break;
				case 0xEF: // Triangle
					stdpar[curPad][12] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][19]));
					break;
				case 0x7F: // Square
					stdpar[curPad][15] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][18]));
					break;
				case 0xFB: // L1
					stdpar[curPad][16] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][26]));
					break;
				case 0xF7: // R1
					stdpar[curPad][17] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][28]));
					break;
				case 0xFE: // L2
					stdpar[curPad][18] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][27]));
					break;
				case 0xFD: // R2
					stdpar[curPad][19] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][29]));
					break;
				default:
					stdpar[curPad][14] = 0x00; // Not pressed
					stdpar[curPad][13] = 0x00; // Not pressed
					stdpar[curPad][12] = 0x00; // Not pressed
					stdpar[curPad][15] = 0x00; // Not pressed
					stdpar[curPad][16] = 0x00; // Not pressed
					stdpar[curPad][17] = 0x00; // Not pressed
					stdpar[curPad][18] = 0x00; // Not pressed
					stdpar[curPad][19] = 0x00; // Not pressed
					break;
				}
				switch(button_check2)
				{
				case 0xE: // UP
					stdpar[curPad][10] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][21]));
					break;
				case 0xB: // DOWN
					stdpar[curPad][11] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][22]));
					break;
				case 0x7: // LEFT
					stdpar[curPad][9] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][23]));
					break;
				case 0xD: // RIGHT
					stdpar[curPad][8] = (pressure*255)/100; //0xff/(100/(100-conf.keys[curPad][24]));
					break;
				default:
					stdpar[curPad][8] = 0x00; // Not pressed
					stdpar[curPad][9] = 0x00; // Not pressed
					stdpar[curPad][10] = 0x00; // Not pressed
					stdpar[curPad][11] = 0x00; // Not pressed
					break;
				}
				buf = stdpar[curPad];
				return padID[curPad];

			case 0x43: // CONFIG_MODE
				cmdLen = 8;
				buf = stdcfg[curPad];
				if (stdcfg[curPad][3] == 0xff) return 0xf3;
				else return padID[curPad];

			case 0x44: // SET_MODE_AND_LOCK
				cmdLen = 8;
				buf = stdmode[curPad];
				return 0xf3;

			case 0x45: // QUERY_MODEL_AND_MODE
				cmdLen = 8;
				buf = stdmodel[curPad];
				buf[4] = padMode[curPad];
				return 0xf3;

			case 0x46: // ??
				cmdLen = 8;
				buf = unk46[curPad];
				return 0xf3;

			case 0x47: // ??
				cmdLen = 8;
				buf = unk47[curPad];
				return 0xf3;

			case 0x4c: // QUERY_MODE ??
				cmdLen = 8;
				buf = unk4c[curPad];
				return 0xf3;

			case 0x4d:
				cmdLen = 8;
				buf = unk4d[curPad];
				return 0xf3;

			case 0x4f: // SET_DS2_NATIVE_MODE
				cmdLen = 8;
				padID[curPad] = 0x79; // setting ds2 mode
				ds2mode = 1; // Set DS2 Mode
				buf = cmd4f[curPad];
				return 0xf3;

			default:
#ifdef PAD_LOG
				PAD_LOG("*PADpoll*: unknown cmd %x\n", value);
#endif
				break;
		}
	}

	switch (curCmd) {
		case 0x43:
			if(curByte == 2)
			{
				switch(value){
				case 0:
					buf[2] = 0;
					buf[3] = 0;
					break;
				case 1:
					buf[2] = 0xff;
					buf[3] = 0xff;
					break;
				}
			}
			break;

		case 0x44:
			if (curByte == 2) {
				PADsetMode(curPad, value);
			}
			break;

		case 0x46:
			if(curByte == 2) {
				switch(value) {
				case 0: // default
					buf[5] = 0x2;
					buf[6] = 0x0;
					buf[7] = 0xA;
					break;
				case 1: // Param std conf change
					buf[5] = 0x1;
					buf[6] = 0x1;
					buf[7] = 0x14;
					break;
				}
			}
			break;

		case 0x4c:
			if (curByte == 2) {
				switch (value) {
					case 0: // mode 0 - digital mode
						buf[5] = 0x4;
						break;

					case 1: // mode 1 - analog mode
						buf[5] = 0x7;
						break;
				}
			}
			break;
	}

	if (curByte >= cmdLen) return 0;
	return buf[curByte++];
}

u8   CALLBACK PADpoll(u8 value) {
	u8 ret;
/*	if (conf.analog) {
		ret = PADpollAnalog(value);
	} else {
		ret = PADpollDigital(value);
	}*/
	ret = _PADpoll(value);
#ifdef PAD_LOG
	PAD_LOG("PADpoll: %x (%d: %x)\n", value, curByte, ret);
#endif
	return ret;
}
/*
u32  CALLBACK PADgetType() {
	if (conf.analog) return PAD_TYPE_ANALOG;
	else return PAD_TYPE_DIGITAL;
}*/

keyEvent ev;

// PADkeyEvent is called every vsync (return NULL if no event)
keyEvent* CALLBACK PADkeyEvent() {

#ifdef __LINUX__
	_PADupdate();
#endif

	if (event.event != 0) {
		memcpy(&ev, &event, sizeof(ev));
		event.event = 0;
		return &ev;
	}

	return NULL;
}


