/*************************************************************************
 *
 * ctrl.c
 *
 *************************************************************************
 *
 * controller and motor stuff
 *
 *************************************************************************/

/*
 * includes
 */

#include "ult_64.h"
#include <PR/sched.h>
#include "defs.h"
#include "main.h"
#include "ctrl.h"
#include "dma.h"

/*
 * defines
 */

#define BLOCK_SIZE		256
#define CTRL_SERIAL_DATA	1
#define SERIAL_QUEUE_SIZE	4
#define TIMER_QUEUE_SIZE	1

/*
 * typedefs
 */

/*
 * structures
 */

/*
 * globals
 */

int			ctrlDisableTime=0;
int			stopAllMotors=0;

// controller pak stuff
char			n64Chars[]=" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#'*+,-./:=?@";
ctrlPakDirEntry		ctrlPakDirEntries[4][MAX_CTRLPAK_FILES];

unsigned char		sramTestBuf[32];

int			eepromPresent=0;

u8    			validcontrollers = 0;
static OSContStatus	statusdata[MAXCONTROLLERS];
static OSContPad     	controllerdata[MAXCONTROLLERS];
static OSScMsg       	controllermsg;
static u16           	lastButArray[MAXCONTROLLERS];
ctrlData		controllers[4];

OSMesgQueue		serialMsgQ;
OSMesg			serialMsgBuf[SERIAL_QUEUE_SIZE];
OSMesgQueue		timerMsgQ;
OSMesg			timerMsgBuf[TIMER_QUEUE_SIZE];
OSTimer			eepromTimer;

OSPfs			GBTransfer[MAXCONTROLLERS];
OSPfs			motorPfs[MAXCONTROLLERS];
OSPfs			pakPfs[MAXCONTROLLERS];
int			ctrlReading;
u32			motorStat,motorValid,paksValid,dodgyPaks;
int			motorTime[MAXCONTROLLERS];

/*
 * function prototypes
 */

void CtrlScan(void);
void CtrlWaitReading(void);

/*
 * code
 */

/*************************************************************************
 *
 * int CtrlInit(void)
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlInit(void)
{

	ctrlDisableTime=0;

	/*
	 * initialise serial message queue for controller data
	 */

	osCreateMesgQueue(&serialMsgQ, serialMsgBuf, SERIAL_QUEUE_SIZE);
	osCreateMesgQueue(&timerMsgQ, timerMsgBuf, TIMER_QUEUE_SIZE);

	/*
	 * initialise controllers
	 */

	osSetEventMesg(OS_EVENT_SI, &serialMsgQ, (OSMesg)CTRL_SERIAL_DATA);
	if(osContInit(&serialMsgQ, &validcontrollers, &statusdata[0]) != 0)
	{
		return;
	}

	/*
	 * find out what's attached to the controllers
	 */

	CtrlScan();

	ctrlReading=0;

	return(0);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlDisable(int time)
{
	ctrlDisableTime=time;
}





void DeterminePakStatus(int pakno)
{
int		rc,rc2;
int  pakmask = (1<<pakno);

		rc=osPfsInitPak(&serialMsgQ,&pakPfs[pakno],pakno);

		switch(rc)
		{
			case 0:
				paksValid |= pakmask;
				break;

			case PFS_ERR_NOPACK:
				break;

			case PFS_ERR_CONTRFAIL:
				break;

			case PFS_ERR_DEVICE:
				// hmm... test for a rumble pak?
				rc2=osMotorInit(&serialMsgQ,&motorPfs[pakno],pakno);
				if(!rc2)
				{
					osMotorStop(&motorPfs[pakno]);
					motorValid |= pakmask;
				}
				else 
				{
					rc2 = osGbpakInit(&serialMsgQ,&GBTransfer[pakno],pakno);	// case PFS_ERR_NOPACK:_NO_PAK, PFS_ERR_DEVICE:, FAIL
					if(rc2)		// still have error
					{
						dodgyPaks |= pakmask;	// physically damaged or corrupted controller pak goes thru here
					}
				}
				break;

			case PFS_ERR_ID_FATAL:
				// hmm... test for a rumble pak?
				rc2=osMotorInit(&serialMsgQ,&motorPfs[pakno],pakno);
				if(!rc2)
				{
					osMotorStop(&motorPfs[pakno]);
					motorValid |= pakmask;
				}
				else
				{
					dodgyPaks |= pakmask;	// do repair id
				}
				break;
		}

}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlScanSingle(int pak)
{
int		i;	//,j,k,rc,rc2;
OSMesg		msg;
int		pakMask;

	pakMask=(1<<pak);

	/*
	 * ensure the SI interface is free
	 */

	if(ctrlReading) CtrlWaitReading();

	/*
	 * query # of controllers
	 */

	osContStartQuery(&serialMsgQ);
       	osRecvMesg(&serialMsgQ, (OSMesg *)&msg,OS_MESG_BLOCK);
	osContGetQuery(&statusdata[0]);
	validcontrollers=0;
	for(i=0;i<MAXCONTROLLERS;i++)
	{
		if(statusdata[i].errno) continue;
		validcontrollers|=(1<<i);
	}
	if(!(validcontrollers & pakMask)) return;

	/*
	 * check for controller paks
	 */


	paksValid &= ~pakMask;
	motorValid &= ~pakMask;
	dodgyPaks &= ~pakMask ;

	DeterminePakStatus(pak);

}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakRepair(int id)
{
	int		i;

	CtrlWaitReading();

	osPfsInitPak(&serialMsgQ,&pakPfs[id],id);
	i=osPfsRepairId(&pakPfs[id]);
	osPfsInitPak(&serialMsgQ,&pakPfs[id],id);
	return(i);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlScan(void)
{
int		i;	//,j,k,rc,rc2;
OSMesg		msg;

	/*
	 * ensure the SI interface is free
	 */

	if(ctrlReading) CtrlWaitReading();

	/*
	 * query # of controllers
	 */

	osContStartQuery(&serialMsgQ);
       	osRecvMesg(&serialMsgQ, (OSMesg *)&msg,OS_MESG_BLOCK);
	osContGetQuery(&statusdata[0]);
	validcontrollers=0;
	for(i=0;i<MAXCONTROLLERS;i++)
	{
		if(statusdata[i].errno) continue;
		validcontrollers|=(1<<i);
	}

	/*
	 * check for controller paks
	 */

	motorStat=0;

	paksValid=0;
	motorValid=0;
	dodgyPaks=0;

	for(i=0;i<MAXCONTROLLERS;i++)
	{
		motorTime[i]=0;

		if(!(validcontrollers & (1<<i)))
		{
			continue;
		}

		DeterminePakStatus(i);
	}

	/*
	 * check for EEPROM - hey... why not?
	 */

	eepromPresent=osEepromProbe(&serialMsgQ);

	/*
	 * try getting some SRAM data...
	 */

//	memset(sramTestBuf,123,32);
//	i=DmaStart(sramHandle,OS_READ,0x08000000,(unsigned long)sramTestBuf,32);
//	while(DmaStatus(i)==DMA_ACTIVE);
}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlScanQuick(unsigned char *r)
{
	int		i;
	OSMesg		msg;

	CtrlWaitReading();

	validcontrollers=0;
	for(i=0;i<MAXCONTROLLERS;i++)
	{
		if(statusdata[i].errno) continue;
		validcontrollers|=(1<<i);
	}

	i=osPfsIsPlug(&serialMsgQ,r);
	return(i);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlStartReading(void)
{
	if(ctrlReading) return;

	osContStartReadData(&serialMsgQ);
	ctrlReading=1;
}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlEepromWriteBuffer(int offset,unsigned char *buf,int len)
{
	int	i,j;
	OSMesg	msg;

	// ensure no controller read going on...
	CtrlWaitReading();

	// write back data
	osWritebackDCache(buf,len*8);

	// write in 8K blocks...
	for(i=0;i<len;i++)
	{
		osEepromWrite(&serialMsgQ,offset+i,&buf[i*8]);
	}
 	osSetTimer(&eepromTimer,7500000,0,&timerMsgQ,(OSMesg)1);
 	osRecvMesg(&timerMsgQ,(OSMesg *)&msg,OS_MESG_BLOCK);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlEepromReadBuffer(int offset,unsigned char *buf,int len)
{
	unsigned long	i,j;
	OSMesg		msg;
	int		validBlocks;
	int		blankSig=0x55;

	// ensure no controller read going on...
	CtrlWaitReading();

	// init valid block count
	validBlocks=0;

	// initialise with blank
	memset(buf,blankSig,len*8);

	while(validBlocks < len)
	{
		validBlocks=0;

		for(i=0;i<len;i++)
		{
			for(j=0;j<8;j++) if(buf[i*8+j]!=blankSig) break;
			if(j!=8)
			{
				validBlocks++;
				continue;
			}
			memset(&buf[i*8],blankSig+1,8);
			osWritebackDCache(&buf[i*8],8);
			osEepromRead(&serialMsgQ,offset+i,&buf[i*8]);
			osInvalDCache(&buf[i*8],8);
		}
		blankSig++;
	}

	// invalidate cache
	osInvalDCache(buf,len*8);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlWaitReading(void)
{
	OSMesg	msg;
	int	i;

	if(!ctrlReading) return;
       	osRecvMesg(&serialMsgQ, (OSMesg *)&msg, OS_MESG_BLOCK);

	// flush..
	for(i=0;i<8;i++) osRecvMesg(&serialMsgQ, (OSMesg *)&msg, OS_MESG_NOBLOCK);

	// free to mess with motors, ctrl paks, etc... at this point
	for(i=0;i<4;i++)
	{
		if(stopAllMotors)
		{
			osMotorInit(&serialMsgQ,&motorPfs[i],i);
			osMotorStop(&motorPfs[i]);
			motorStat &= ~(1<<i);
			motorTime[i]=0;
		}

		if(!(motorValid & (1<<i))) continue;

		if((motorStat & (1<<i)) && !motorTime[i])
		{
			osMotorStop(&motorPfs[i]);
			motorStat &= ~(1<<i);
			motorTime[i]=0;
		}
		else if(!(motorStat & (1<<i)) && motorTime[i])
		{
			osMotorStart(&motorPfs[i]);
			motorStat |= (1<<i);
		}

		if(motorTime[i]) motorTime[i]--;
	}

	stopAllMotors=0;
	ctrlReading=0;
}

/*************************************************************************
 *
 * void CtrlUpdate(void)
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlUpdate(void)
{
	OSContPad   *pad;
	u16         i, newbutton;
	u16	    ctrlCount;

	if(ctrlReading) return;

	if(ctrlDisableTime) ctrlDisableTime--;

	ctrlCount=0;
	osContGetReadData(controllerdata);

	for( i = 0; i < MAXCONTROLLERS; i++)
	{
		if((validcontrollers >> i) & 1)
		{
			/*
			 * we got a valid controller
			 */

			pad = &controllerdata[i];

			/*
			 * ignore controllers with errors
			 */

			if (pad->errno) continue;

			/*
			 * get bits that have changed, ignore button ups
			 */

			newbutton = pad->button ^ lastButArray[i];
			newbutton &= pad->button;
			lastButArray[i] = pad->button;

			if(!ctrlDisableTime)
			{
				controllers[i].buttons=pad->button;
				controllers[i].x=pad->stick_x;
				controllers[i].y=pad->stick_y;
			}
			else
			{
				controllers[i].buttons=0;
				controllers[i].x=0;
				controllers[i].y=0;
			}

			ctrlCount++;
			if(ctrlCount==4) break;
		}
	}
}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlMotorOn(int which,int time)
{
	if(!(motorValid & (1<<which))) return;
	motorTime[which]=time+1;
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakQuerySpace(int pak)
{
	s32	rc,space;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	rc=osPfsFreeBlocks(&pakPfs[pak],&space);
	if(rc!=0) return(-2);

	return(space);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlPakNCODEToASCII(char *s,int len)
{
	int i;

	for(i=0;i<len;i++)
	{
		if(s[i]>=0x0F && s[i]<=0x41) s[i]=n64Chars[s[i]-0x0F];
		else if(s[i]==0) s[i]=0;
		else s[i]='*';
	}
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void CtrlPakASCIIToNCODE(char *s,int len)
{
	int i,j;

	for(i=0;i<len;i++)
	{
		for(j=0;n64Chars[j];j++)
		{
			if(s[i]==n64Chars[j]) break;
		}
		if(!n64Chars[j])
		{
			if(s[i]==0) s[i]=0;
			else s[i]=0x0F;
		}
		else
		{
			s[i]=j+0x0F;
		}
	}
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakReadDirectory(int pak)
{
	int	i,j,k;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	for(i=0;i<16;i++) ctrlPakDirEntries[pak][i].fileId=-1;

	k=0;
	for(i=0;i<16;i++)
	{
		j=osPfsFileState(&pakPfs[pak],i,(OSPfsState *)&ctrlPakDirEntries[pak][k]);
		if(j==PFS_ERR_INVALID)
		{
			memset(&ctrlPakDirEntries[pak][k],0,sizeof(ctrlPakDirEntry));
			ctrlPakDirEntries[pak][k].fileId=-1;
		}
		else if(!j)
		{
			ctrlPakDirEntries[pak][k].fileId=i;
			CtrlPakNCODEToASCII(ctrlPakDirEntries[pak][k].gameName,16);
			CtrlPakNCODEToASCII(ctrlPakDirEntries[pak][k].extension,4);
			k++;
		}
		else
		{
			return(-2);
		}
	}

	return(0);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakCreateFile(int pak,char *n,char *e,int compCode,int gameCode,int size)
{
	int	i,j,k;
	char	name[16];
	char	ext[4];
	s32	handle;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	memset(ext,0,4);
	strcpy(ext,e);
	memset(name,0,16);
	strcpy(name,n);

	CtrlPakASCIIToNCODE(name,16);
	CtrlPakASCIIToNCODE(ext,4);

	i=osPfsAllocateFile(&pakPfs[pak],
	                    compCode,
			    gameCode,
			    name,
			    ext,
			    size,
			    &handle);
	if(i) return(-i);

	return(handle);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakDeleteFile(int pak,int fileId)
{
	int		i,j,k;
	char		name[16];
	char		ext[4];
	int		dirEntry;
	OSPfsState	filedata;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	j=osPfsFileState(&pakPfs[pak],fileId,&filedata);
	if(j) return(100-j);

	i=osPfsDeleteFile(&pakPfs[pak],
			  filedata.company_code,
			  filedata.game_code,
			  filedata.game_name,
			  filedata.ext_name);

	if(i) return(-i);

	return(0);
}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakReadFile(int pak,int fh,int offset,int len,char *data)
{
	int rc;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	rc=osPfsReadWriteFile(&pakPfs[pak],
			      fh,
			      PFS_READ,
			      offset,
			      len,
			      data);

	if(rc) return(-rc);

	return(0);
}


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

int CtrlPakWriteFile(int pak,int fh,int offset,int len,char *data)
{
	int rc;

	if(!(paksValid & (1<<pak))) return(-1);
	CtrlWaitReading();

	rc=osPfsReadWriteFile(&pakPfs[pak],
			      fh,
			      PFS_WRITE,
			      offset,
			      len,
			      data);

	if(rc) return(-rc);

	return(0);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/


/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/


