////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// 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.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
///////////////////////////////////////////////////////////////////////////////
//
//	bjoy.cpp
// 
// History:
//		06/02/04 RCG	Started.
//
//////////////////////////////////////////////////////////////////////////////
//
// Handles all Dreamcast specific joypad stuff.
//
//////////////////////////////////////////////////////////////////////////////


#include "Blue.h"
#include <math.h>
#include <kos.h>

//////////////////////////////////////////////////////////////////////////////
// Macros.
//////////////////////////////////////////////////////////////////////////////
#define NUM_CONTROLLERS	4

// For simplicity of CHECK_AXIS_* macros, define RSP macros that match the
// Windows name for the W axis (Windows uses R for Rudder).
#define RSP_JOY_R_POS	RSP_JOY_W_POS
#define RSP_JOY_R_NEG	RSP_JOY_W_NEG

#define MID_THRESHOLD_PERCENT	((float)0.2)	// In % (e.g., 25 would be 25%).

#define SET(p, val)	(p ? *p = val : val)

//////////////////////////////////////////////////////////////////////////////
// Typedefs.
//////////////////////////////////////////////////////////////////////////////

typedef struct
	{
	U32	u32Buttons;
	U32	u32Axes;
	} JoyState;

//////////////////////////////////////////////////////////////////////////////
// Module specific (static) variables.
//////////////////////////////////////////////////////////////////////////////

static JoyState		ms_ajsCurr[NUM_CONTROLLERS];		// Current joystick state.
static JoyState		ms_ajsPrev[NUM_CONTROLLERS];		// Previous joystick state.

//////////////////////////////////////////////////////////////////////////////
// Internal functions.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Externally callable functions.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// To be called by the Blue library itself, only.
// Initializes the joystick module.  NOTE:  May fail if no joysticks attached.
// Returns nothing in order to remind us that even if the init fails the app
// should still be called.
//
//////////////////////////////////////////////////////////////////////////////

static cMapleController* ms_Controllers[NUM_CONTROLLERS];

// todo
extern void Joy_Init(void)
{			 
	// todo get controller number?
	for (int index = 0; index < MAPLE_PORT_COUNT; index++)
	{
		ms_Controllers[index] = new cMapleController(index);
	}   
	ms_Controllers[0]->CanReboot();  
}

// todo
static inline void calcButton(cMapleController* controller, const uint16_t button, U32* bits, const U32 posbit)
{
	if (controller->ButtonDown(button) != false)
		*bits |= posbit;
}

// todo analog support?
static inline void calcTrigger(cMapleController* controller, bool trigger, U32* bits, const U32 posbit)
{
	if (controller->TriggerPulled(trigger) != false)	
	{		
		*bits |= posbit;	
	}		
}

static void calcAxis(cMapleController *controller, const int16_t axis, U32 *bits, const U32 posbit, const U32 negbit)
{
    const int16_t deadzone = (uint16_t) (128.0f * MID_THRESHOLD_PERCENT);
    int16_t x, y;
				
	switch (axis)
	{
		// Stick 1 X
		case 0:
			controller->GetAxis(&x, &y, 0);
			if ((x < 0) && ((-x) > deadzone))
			{				
				*bits |= negbit;				
			}        	
    		else if ((x > 0) && (x > deadzone))
			{
				*bits |= posbit;					
			}        	
		break;
		// Stick 1 Y
		case 1:
			controller->GetAxis(&x, &y, 0);
			if ((y < 0) && ((-y) > deadzone))
			{								
				*bits |= negbit;
			}        	
    		else if ((y > 0) && (y > deadzone))
			{				
				*bits |= posbit;
			}        	
		break;	

		// Stick 2 X
		case 2:
			controller->GetAxis(&x, &y, 1);
			if ((x < 0) && ((-x) > deadzone))
			{				
				*bits |= negbit;				
			}        	
    		else if ((x > 0) && (x > deadzone))
			{
				*bits |= posbit;					
			}        	
		break;
		// Stick 2 Y
		case 3:
			controller->GetAxis(&x, &y, 1);
			if ((y < 0) && ((-y) > deadzone))
			{								
				*bits |= negbit;
			}        	
    		else if ((y > 0) && (y > deadzone))
			{				
				*bits |= posbit;
			}        	
		break;	

		// Stick 3 X
		case 4:
			controller->GetAxis(&x, &y, 2);
			if ((x < 0) && ((-x) > deadzone))
			{				
				*bits |= negbit;				
			}        	
    		else if ((x > 0) && (x > deadzone))
			{
				*bits |= posbit;					
			}        	
		break;
		// Stick 3 Y
		case 5:
			controller->GetAxis(&x, &y, 2);
			if ((y < 0) && ((-y) > deadzone))
			{								
				*bits |= negbit;
			}        	
    		else if ((y > 0) && (y > deadzone))
			{				
				*bits |= posbit;
			}        	
		break;		

		// Stick 4 X
		case 6:
			controller->GetAxis(&x, &y, 3);
			if ((x < 0) && ((-x) > deadzone))
			{				
				*bits |= negbit;				
			}        	
    		else if ((x > 0) && (x > deadzone))
			{
				*bits |= posbit;					
			}        	
		break;
		// Stick 4 Y
		case 7:
			controller->GetAxis(&x, &y, 3);
			if ((y < 0) && ((-y) > deadzone))
			{								
				*bits |= negbit;
			}        	
    		else if ((y > 0) && (y > deadzone))
			{				
				*bits |= posbit;
			}        	
		break;	
	}	
}

//////////////////////////////////////////////////////////////////////////////
//
// Updates joystick sJoy's current state and makes the current state the 
// previous.
//
//////////////////////////////////////////////////////////////////////////////
extern cMapleController* rspUpdateJoy(int16_t sJoy)
{
	if ((sJoy > NUM_CONTROLLERS) || (ms_Controllers[sJoy] == NULL))
		return;

	cMapleController* pController = ms_Controllers[sJoy];
	switch (pController->Status())
	{
		case MAPLE::cMapleController::ControllerConnected:
		break;
		case MAPLE::cMapleController::ControllerDisconnected:
		// FLush input
		memset(&ms_ajsCurr[sJoy], 0, sizeof(JoyState));
		memset(&ms_ajsPrev[sJoy], 0, sizeof(JoyState));		
		break;	
		case MAPLE::cMapleController::VmuDisconnected:				
		// todo: If active, throw up message?
		break;
	}

	memcpy(&ms_ajsPrev[sJoy], &ms_ajsCurr[sJoy], sizeof(JoyState));
	ms_ajsCurr[sJoy].u32Axes = 0;
	ms_ajsCurr[sJoy].u32Buttons = 0;

	// Twin-stick / Extended controller		
	if (pController->IsExtended())
	{
		calcButton(pController, CONT_D, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_5);
				
		// Fake analogue sticks using both d-pad inputs.
		calcAxis(pController, 2, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_X_POS, RSP_JOY_X_NEG);		
		calcAxis(pController, 3, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_Y_POS, RSP_JOY_Y_NEG);		
		calcAxis(pController, 4, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_Z_POS, RSP_JOY_Z_NEG);		
		calcAxis(pController, 5, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_W_POS, RSP_JOY_W_NEG);
	}
	else
	{
		// Analogue X/Y
		calcAxis(pController, 0, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_X_POS, RSP_JOY_X_NEG);		
		calcAxis(pController, 1, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_Y_POS, RSP_JOY_Y_NEG);

		// Fake second analogue stick using the button-pad
		calcAxis(pController, 6, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_Z_POS, RSP_JOY_Z_NEG);
		calcAxis(pController, 7, &ms_ajsCurr[sJoy].u32Axes, RSP_JOY_W_POS, RSP_JOY_W_NEG);		
	}
					
	calcButton(pController, CONT_A, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_1);
	calcButton(pController, CONT_B, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_2);
	calcButton(pController, CONT_X, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_3);
	calcButton(pController, CONT_Y, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_4);
	calcButton(pController, CONT_START, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_7);	

	calcButton(pController, CONT_DPAD_UP, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_8);
	calcButton(pController, CONT_DPAD_DOWN, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_9);
	calcButton(pController, CONT_DPAD_LEFT, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_10);
	calcButton(pController, CONT_DPAD_RIGHT, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_11);
		
	calcTrigger(pController, 0, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_16);
	calcTrigger(pController, 1, &ms_ajsCurr[sJoy].u32Buttons, RSP_JOY_BUT_17);	
	
	pController->m_pJumpPack->RumbleUpdate();

	return pController;	
}

extern cMapleController *rspGetJoy(int16_t sJoy)						// In:  Joystick to query.	
{
	return ms_Controllers[sJoy];
}

//////////////////////////////////////////////////////////////////////////////
//
// Reads the joystick sJoy's current state.
// This function returns directions in a digital format (up, down, centered).
//
//////////////////////////////////////////////////////////////////////////////
extern void rspGetJoyState(
	int16_t sJoy,						// In:  Joystick to query.
	U32* pu32Buttons,			// Out: Buttons that are down, if not NULL.
										// An RSP_JOY_BUT_## bit field that is set indicates
										// that button is down.
	U32* pu32Axes /*= NULL*/)	// Out: Directions that are specificed, if not NULL.
										// An RSP_JOY_?_POS bit set indicates the ? axis is positive.
										// An RSP_JOY_?_NEG bit set indicates the ? axis is negative.
										// If neither is set for ? axis, that axis is 0.
{
	SET(pu32Buttons, ms_ajsCurr[sJoy].u32Buttons);
	SET(pu32Axes, ms_ajsCurr[sJoy].u32Axes);
}

//////////////////////////////////////////////////////////////////////////////
//
// Reads the joystick sJoy's previous state.
// This function returns directions in a digital format (up, down, centered).
//
//////////////////////////////////////////////////////////////////////////////
extern void rspGetJoyPrevState(
	int16_t sJoy,						// In:  Joystick to query.
	U32* pu32Buttons,			// Out: Buttons that are down, if not NULL.
										// An RSP_JOY_BUT_## bit field that is set indicates
										// that button is down.
	U32* pu32Axes /*= NULL*/)	// Out: Directions that are specificed, if not NULL.
										// An RSP_JOY_?_POS bit set indicates the ? axis is positive.
										// An RSP_JOY_?_NEG bit set indicates the ? axis is negative.
										// If neither is set for ? axis, that axis is 0.
{
	SET(pu32Buttons, ms_ajsPrev[sJoy].u32Buttons);
	SET(pu32Axes, ms_ajsPrev[sJoy].u32Axes);
}

//////////////////////////////////////////////////////////////////////////////
// Functions to convert bitfields to joybutton numbers and back again.
//////////////////////////////////////////////////////////////////////////////

extern int16_t JoyBitfieldToIndex(U32 bitfield)
{
	// Clunky.
	int16_t result;
	if (bitfield & RSP_JOY_BUT_1)
		result = 1;
	else if (bitfield & RSP_JOY_BUT_2)
		result = 2;
	else if (bitfield & RSP_JOY_BUT_3)
		result = 3;
	else if (bitfield & RSP_JOY_BUT_4)
		result = 4;
	else if (bitfield & RSP_JOY_BUT_5)
		result = 5;
	else if (bitfield & RSP_JOY_BUT_6)
		result = 6;
	else if (bitfield & RSP_JOY_BUT_7)
		result = 7;
	else if (bitfield & RSP_JOY_BUT_8)
		result = 8;
	else if (bitfield & RSP_JOY_BUT_9)
		result = 9;
	else if (bitfield & RSP_JOY_BUT_10)
		result = 10;
	else if (bitfield & RSP_JOY_BUT_11)
		result = 11;
	else if (bitfield & RSP_JOY_BUT_12)
		result = 12;
	else if (bitfield & RSP_JOY_BUT_13)
		result = 13;
	else if (bitfield & RSP_JOY_BUT_14)
		result = 14;
	else if (bitfield & RSP_JOY_BUT_15)
		result = 15;
	else if (bitfield & RSP_JOY_BUT_16)
		result = 16;
	else if (bitfield & RSP_JOY_BUT_17)
		result = 17;
	else if (bitfield & RSP_JOY_BUT_18)
		result = 18;
	else
		// Default
		result = 0;

	if (bitfield != 0 || result != 0)
		TRACE("JoyBitfieldToIndex(%i) = %i\n", bitfield, result);

	return result;
}

extern U32 JoyIndexToBitfield(int16_t index)
{
	// Also clunky.
	U32 result;
	switch (index)
	{
		case 1:
			result = RSP_JOY_BUT_1;
			break;
		case 2:
			result = RSP_JOY_BUT_2;
			break;
		case 3:
			result = RSP_JOY_BUT_3;
			break;
		case 4:
			result = RSP_JOY_BUT_4;
			break;
		case 5:
			result = RSP_JOY_BUT_5;
			break;
		case 6:
			result = RSP_JOY_BUT_6;
			break;
		case 7:
			result = RSP_JOY_BUT_7;
			break;
		case 8:
			result = RSP_JOY_BUT_8;
			break;
		case 9:
			result = RSP_JOY_BUT_9;
			break;
		case 10:
			result = RSP_JOY_BUT_10;
			break;
		case 11:
			result = RSP_JOY_BUT_11;
			break;
		case 12:
			result = RSP_JOY_BUT_12;
			break;
		case 13:
			result = RSP_JOY_BUT_13;
			break;
		case 14:
			result = RSP_JOY_BUT_14;
			break;
		case 15:
			result = RSP_JOY_BUT_15;
			break;
		case 16:
			result = RSP_JOY_BUT_16;
			break;
		case 17:
			result = RSP_JOY_BUT_17;
			break;
		case 18:
			result = RSP_JOY_BUT_18;
			break;
		default:
			result = 0;
			break;
	}

	if (index != 0 || result != 0)
		printf("JoyIndexToBitfield(%i) = %i\n", index, result);

	return result;
}

//////////////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////////////
