/****************************************************************************
*
* PSXINPUT.C
*
* PROGRAMMER : Chris
*    VERSION : 00.01
*    CREATED : 26/03/98
*   MODIFIED : 26/03/98
*       TABS : 3
*
* DESCRIPTION
*	Controller stuff
*
*****************************************************************************/

/*---------------------------------------------------------------------------
 * Import Headers
\*--------------------------------------------------------------------------*/

#include <sys\types.h>
#include <libpad.h>
#include <stdio.h>
#include <string.h>
#include "typedefs.h"
#include "types.h"
#include "pad.h"
#include "malloc.h"
#include "memory.h"
#include "file.h"
#include "specific.h"
#include "..\game\objects.h"
#include "..\game\items.h"
#include "..\game\room.h"
#include "..\game\lara.h"
#include "..\game\input.h"
#include "..\game\control.h"
#include "..\game\gameflow.h"
#include "..\game\savegame.h"
#include "..\game\laramisc.h"
#include "..\game\demo.h"

extern int level_complete;
extern int demo_quit_flag;

//#define IPROFILE
//#define GAMEDEBUG

#ifdef GAMEDEBUG
#include "..\game\camera.h"
extern int GnGameMode;
extern uint32 CurrentLevel;
#endif

/*---------------------------------------------------------------------------
 * Constants
\*--------------------------------------------------------------------------*/

//#define RECORD_DEMOS
//#define AUTO_RECORD

#define LOOKY_FRAC	-83	// (44/96) * ONE_DEGREE
#define LOOKU_FRAC	-42	// (22/96) * ONE_DEGREE
#define LOOKD_FRAC	-66	// (35/96) * ONE_DEGREE

#define FAST_VAL    96

#define CHEAT (PAD_L1|PAD_R2|PAD_RL|PAD_RD)

/*---------------------------------------------------------------------------
 * Type Definitions
\*--------------------------------------------------------------------------*/

typedef struct pad_configs {
	ULONG pad_L2;
	ULONG pad_R2;
	ULONG pad_L1;
	ULONG pad_R1;
	ULONG pad_triangle;
	ULONG pad_square;
	ULONG pad_circle;
	ULONG pad_cross;
}PAD_CONFIG;

/*---------------------------------------------------------------------------
 * Externals
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Globals
\*--------------------------------------------------------------------------*/

LONG input;
int PadConnected=1;		// Indicates Pad is Connected
UBYTE DualShock=0;

int psx_status;		// ## what is this shit ?

sint32 LaraLookY;
sint32 LaraLookX;

UBYTE Motors[2];

volatile ControllerPacket GPad1,GPad2;

#ifdef GAMEDEBUG
int InfoFlag=0;
#endif

/*---------------------------------------------------------------------------
 * Locals
\*--------------------------------------------------------------------------*/

static unsigned char align[6]={0,1,0xFF,0xFF,0xFF,0xFF};

static PAD_CONFIG pad_cons[] = {
	{
		IN_DUCK,	IN_DASH,
		IN_LOOK,	IN_SLOW,
		IN_DRAW,
		IN_JUMP,	IN_ROLL,
		IN_ACTION
	},{
		IN_LOOK,	IN_SLOW,
		IN_DUCK,	IN_DASH,
		IN_ROLL,
		IN_DRAW,	IN_ACTION,
		IN_JUMP
	},{
		IN_DASH,	IN_DUCK,
		IN_SLOW,	IN_LOOK,
		IN_JUMP,
		IN_ROLL,	IN_ACTION,
		IN_DRAW
	},{
		IN_DUCK,	IN_DASH,
		IN_DRAW,	IN_SLOW,
		IN_LOOK,
		IN_ACTION,IN_JUMP,
		IN_ROLL
	},{
		IN_ROLL,	IN_DRAW,
		IN_JUMP,	IN_ACTION,
		IN_DASH,
		IN_LOOK,	IN_DUCK,
		IN_SLOW
	}
};

#ifdef GAMEDEBUG
static int record_demo=0;
#endif

/*---------------------------------------------------------------------------
 * Macros
\*--------------------------------------------------------------------------*/

#if !defined(PSX_SONY_DEMO_SHELL) && !defined(TEST_SONY_DEMO_SHELL)
	#define SELECT_BUT	PAD_SEL
	#define START_BUT	PAD_START
#else
	#define SELECT_BUT	PAD_START
	#define START_BUT	PAD_SEL
#endif

#if LANGUAGE == 3
	#define SELECT_BUTTON	PAD_RR
	#define DESELECT_BUTTON	PAD_RD
#else
	#define SELECT_BUTTON	PAD_RD
	#define DESELECT_BUTTON	PAD_RU
#endif

/*---------------------------------------------------------------------------
 * Local Functions
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Public Functions
\*--------------------------------------------------------------------------*/

void S_UpdateInput()
{
	UBYTE type;
	ULONG in = 0;
	static ULONG Edge;
	static ULONG padd;
	static ULONG Edge2;
	static ULONG Pad2;
	static int reset_count=0;
	static int pause_db=0;
	static int option_db=0;

/* -------- read pad */

	type = PadInfoMode(0, InfoModeCurID, 0);
//	type = GetType(&GPad1);

	Edge = padd;

	if ((!GoodData(&GPad1))
	||  ((type != CTL_PAD) && (type != CTL_ANALOGC)))
	{
		padd = 0;			// error reading pad...
		PadConnected = 0;
	}
	else
	{
		padd = (~GPad1.data.pad)&0xffff;					// grab the pad data...
		PadConnected = 1;
	}


	Edge = ((Edge ^ padd) & padd);

/* -------- select/start stuff  */

	{
		if (Edge & PAD_SEL)
			option_db = 1;

		if (padd & PAD_SEL)
		{
		/* -------- do inventory shortcuts */

			if (padd & PAD_R1 && lara.skidoo == NO_ITEM)
			{
				in |= IN_F;
				padd &= ~PAD_R2;
				option_db = 0;
			}

		/* -------- exit to title */

			if (padd & PAD_START)
			{
				if ((++reset_count) > (24*2))
					reset_flag = 1;

				padd = 0;
			}
			else
				reset_count = 0;
		}

		else
		{
			if (option_db)
				in |= IN_OPTION;

			reset_count = 0;
			option_db = 0;

			if (Edge & PAD_START)
				in |= IN_PAUSE;
		}
	}

/* -------- menu buttons */

	if (Edge & SELECT_BUTTON)
		in |= IN_SELECT;

	if (Edge & DESELECT_BUTTON)
		in |= IN_DESELECT;

/* -------- configurable buttons */

	if (padd & PAD_RL)
		in |= (pad_cons[iconfig].pad_square);

	if (padd & PAD_RR)
		in |= (pad_cons[iconfig].pad_circle);

	if (padd & PAD_RU)
		in |= (pad_cons[iconfig].pad_triangle);

	if (padd & PAD_RD)
		in |= (pad_cons[iconfig].pad_cross);

	if (padd & PAD_L1)
		in |= (pad_cons[iconfig].pad_L1);

	if (padd & PAD_L2)
		in |= (pad_cons[iconfig].pad_L2);

	if (padd & PAD_R1)
		in |= (pad_cons[iconfig].pad_R1);

	if (padd & PAD_R2)
		in |= (pad_cons[iconfig].pad_R2);

/* -------- handle analogue controller */

	if (type == CTL_ANALOGC)
	{
		BYTE pos;
		BYTE walking = 0;

	/* -------- extra buttons */

		if (padd & 2)
			in |= (pad_cons[iconfig].pad_square);

		if (padd & 4)
			in |= (pad_cons[iconfig].pad_cross);

	/* -------- run on left analogue controller */

		walking = (in & IN_SLOW);

		pos = 127 - GPad1.data.analog.left_x;

		if (pos > 32)
		{
			in |= IN_LEFT;

			if ((pos < FAST_VAL) && (!(in & IN_STEPSHIFT)))
				in |= IN_SLOW;
		}

		else if (pos < -32)
		{
			in |= IN_RIGHT;

			if ((pos > -FAST_VAL) && (!(in & IN_STEPSHIFT)))
				in |= IN_SLOW;
		}

		pos = 127 - GPad1.data.analog.left_y;

		if (pos > 32)
		{
			in |= IN_FORWARD;

			if ((pos < FAST_VAL) && (!(in & IN_STEPSHIFT)))
				in |= IN_SLOW;
			else if (!walking)
				in &= ~IN_SLOW;

		}
		else if (pos < -32)
		{
			in |= IN_BACK;

			if ((pos > -FAST_VAL) && (!(in & IN_STEPSHIFT)))
				in |= IN_SLOW;
			else if (!walking)
				in &= ~IN_SLOW;
		}

	/* -------- handle step */

#if 0
		if (in & IN_STEPSHIFT)
		{
			if (in & IN_LEFT)
			{
				in = (in & ~IN_LEFT) | IN_STEPL;

				if (in & IN_FORWARD)
					in = (in & ~(IN_FORWARD)) | IN_RIGHT;

				else if (in & IN_BACK)
					in = (in & ~(IN_BACK)) | IN_LEFT;
			}

			else if (in & IN_RIGHT)
			{
				in = (in & ~IN_RIGHT) | IN_STEPR;

				if (in & IN_FORWARD)
					in = (in & ~(IN_FORWARD)) | IN_LEFT;

				else if (in & IN_BACK)
					in = (in & ~(IN_BACK)) | IN_RIGHT;
			}

			else if (in & (IN_FORWARD|IN_BACK))
				in |= IN_SLOW;
		}
#else
		if (in & IN_SLOW)
		{
			if (!(in & (IN_FORWARD|IN_BACK)))
			{
				if (in & IN_LEFT)
					in = (in & ~IN_LEFT) | IN_STEPL;

				else if (in & IN_RIGHT)
					in = (in & ~IN_RIGHT) | IN_STEPR;
			}
		}
#endif

	/* -------- look on right analogue controller */

		pos = 127 - GPad1.data.analog.right_y;

		if (pos > 32)
		{
			LaraLookX = ((pos-32) * LOOKD_FRAC);
			in |= IN_LOOKFORWARD|IN_LOOK;
		}
		else if (pos < -32)
		{
			LaraLookX = ((pos+32) * LOOKU_FRAC);
			in |= IN_LOOKBACK|IN_LOOK;
		}
		else
			LaraLookX = 0;


		pos = 127 - GPad1.data.analog.right_x;

		if (pos > 32)
		{
			LaraLookY = ((pos-32) * LOOKY_FRAC);
			in |= IN_LOOKRIGHT|IN_LOOK;
		}
		else if (pos < -32)
		{
			LaraLookY = ((pos+32) * LOOKY_FRAC);
			in |= IN_LOOKLEFT|IN_LOOK;
		}
		else
			LaraLookY = 0;

	}

/* -------- determine walk or strafe */

	if (!(in & (IN_FORWARD|IN_BACK|IN_LEFT|IN_RIGHT)))
//	else
	{
		if (padd & PAD_LU)
			in |= IN_FORWARD;

		else if (padd & PAD_LD)
			in |= IN_BACK;

		if (padd & PAD_LL)
			in |= IN_LEFT;

		else if (padd & PAD_LR)
			in |= IN_RIGHT;

		if (in & IN_SLOW)
		{
			if (!(in & (IN_FORWARD|IN_BACK)))
			{
				if (in & IN_LEFT)
					in = (in & ~IN_LEFT) | IN_STEPL;

				else if (in & IN_RIGHT)
					in = (in & ~IN_RIGHT) | IN_STEPR;
			}
		}
	}

/* -------- update DualShock motors */

//	if ((type == CTL_ANALOGC)
//	&&  (PadInfoMode(0, InfoModeCurExID, 0)))
	if (PadInfoMode(0, InfoModeCurExID, 0))
	{
		int state;
		static int send=0;

		DualShock = 1;
		state = PadGetState(0);

		if (state == PadStateFindPad)
			send = 0;

		if (send == 0)
		{
			PadSetAct(0, &Motors[0], 2);

			if (state == PadStateFindCTP1)
				send = 1;

			else	if (state == PadStateStable)
			{
				if (PadSetActAlign(0, &align[0]))
			 		send = 1;
			}
		}
	}
	else
		DualShock = 0;


/* -------- debugging stuff */

#ifdef GAMEDEBUG

	Edge2 = Pad2;

	if (GoodData(&GPad2))
		Pad2  = (~GPad2.data.pad)&0xffff;
	else
		Pad2  = 0;

	Edge2 = ((Edge2 ^ Pad2) & Pad2);

	if (Pad2 == CHEAT)
		level_complete = 1;


	if (Pad2 & PAD_RU)
		level_complete = 1;

	if (Edge2 & PAD_RR)
	{
		static sint16 cut=0;

		GF_Cutscene_Orientation = (uint16)cut*0x4000;
		camera.target_angle = GF_Cutscene_Orientation;
		printf("GF_Cutscene_Orientation = %d\n", cut);
		cut = (cut+1) & 3;
	}

	if (Edge2 & PAD_RD)
		InfoFlag ^= 1;

/* -------- start recording demo */

#ifdef RECORD_DEMOS
#ifdef AUTO_RECORD
	if ((GnGameMode == GAMEMODE_IN_GAME)
#else
	if ((Edge2 & PAD_START)
#endif
	&&  (!record_demo))
	{
		demoptr = (uint32 *)game_malloc(DEMOCOUNT_MAX, 0);
		democount = 0;

	/* -------- SaveLaraDemoPos() */

		*(demoptr+0) = (uint32)lara_item->pos.x_pos;
		*(demoptr+1) = (uint32)lara_item->pos.y_pos;
		*(demoptr+2) = (uint32)lara_item->pos.z_pos;
		*(demoptr+3) = (uint32)lara_item->pos.x_rot;
		*(demoptr+4) = (uint32)lara_item->pos.y_rot;
		*(demoptr+5) = (uint32)lara_item->pos.z_rot;
		*(demoptr+6) = (uint32)lara_item->room_number;
		*(demoptr+7) = (uint32)lara.last_gun_type;
		democount += 8;

//		SeedRandomDraw(RANDOM_SEED);
//		SeedRandomControl(RANDOM_SEED);
		record_demo = 1;

		printf("Recording...\n");
	}

/* -------- save demo data */

#ifdef AUTO_RECORD
	if (((record_demo > 0) && (democount == DEMOCOUNT_MAX))
	||  (Edge2 & PAD_SEL))
#else
	if ((Edge2 & PAD_SEL) && (record_demo))
#endif
	{
		char filename[30];

		if (democount > DEMOCOUNT_MAX-1)
			democount = DEMOCOUNT_MAX-1;

		*(demoptr+democount) = (unsigned long)-1;
		democount++;

		S_MemSet(&filename[0], 0, 30);

		printf("Saving 'demo.dem' - ");

//		FILE_Save(filename, demoptr, democount * sizeof(uint32));
		FILE_Save("demo.dem", demoptr, democount * sizeof(uint32));

		game_free(DEMOCOUNT_MAX, 0);

		record_demo = -1;

		printf("OK\n");
	}

/* -------- write demo input */

	if ((record_demo) && (democount < DEMOCOUNT_MAX))
	{
		*(demoptr+democount) = in;
		democount++;
	}

#endif
#endif

#if defined(ALWAYS_ENABLE_DOZY_CHEAT)
	if (padd == CHEAT)
		in |= IN_C;
#endif
/*
	if (!GoodData(&GPad2) || GetType(&GPad2) != CTL_PAD)		// Additional DEBUG
		pad2 = 0;                                           // keys from Controller 2
	else
		pad2 = (~GPad2.data.pad)&0xffff;
#endif

#ifndef NO_FLY_CHEAT
	if (gameflow.cheat_enable)
	{
		if (pad2&PAD_RD)
			level_complete = 1;

		if (pad2&PAD_RU )
			in |= IN_A;
	}
#endif

#ifdef GAMEDEBUG
	if (pad2 & PAD_FLT )
		lara_item->hit_points = -1;
	if (pad2 & PAD_FRT )
		psx_status = 0;
	if (pad2 & PAD_FRB )
		psx_status = 1;
	if(pad2 & PAD_SEL)				// 2nd Select Button scrolls round VRam
  		in |= IN_E;
*/

#ifdef IPROFILE
	if (Pad2&PAD_LL)
		IPROF_Stop();
	if (Pad2&PAD_LR)
	{
		IPROF_Stop();
		IPROF_Clear();
		IPROF_Start();
	}
	if (Pad2&PAD_LU)
	{
		IPROF_Stop();
		FILE_Save("profile.bin",0x400000,0x200000);
		FILE_Save("mem.bin",0x000000,0x200000);
	}
#endif
//#endif

	input = in;
}
