//////////////////////////////////////////////////////////////////////////////
//
// Asura_PSP_Input.cpp
//
// Created by Andrew Weinkove - 08/04/04.
//
// Copyright (c) 2004 Rebellion. All rights reserved.
//
// Implementation of PSP controller manager. 
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Input/Asura_PSP_Input.h"

#include "Core/Asura_Maths.h"
#include "Input/Asura_PlatformInput.h"
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
#include "Interface/Asura_CommandConsole.h"
#endif

#include <ctrlsvc.h> //needed for controller functions
#include <kernel.h> // Needed for power tick
#ifdef ASURA_PSP_DEMO
#include <pspdemo.h>
#endif//def ASURA_PSP_DEMO

// C O N S T A N T S /////////////////////////////////////////////////////////

// static members
float*  Asura_PSP_Input::s_pfAsuraAxis = 0;
u_char* Asura_PSP_Input::s_pucAsuraCurrentInput = 0;
float   Asura_PSP_Input::s_fAnalogStickDeadZone = 32.0f;
bool   Asura_PSP_Input::s_bUseConversionArray = false;

const float afASURA_PSP_STICKCONVERT[8*8] =
{
	0.14142f,	0.07654f,	0.00000f,	0.00000f,	0.00000f,	0.00000f,	0.00000f,	0.00000f,
	0.18478f,	0.14142f,	0.07654f,	0.07654f,	0.07654f,	0.11481f,	0.22962f,	0.00000f,
	0.20000f,	0.18478f,	0.14142f,	0.14142f,	0.11481f,	0.11481f,	0.22962f,	0.38270f,
	0.20000f,	0.18478f,	0.14142f,	0.14142f,	0.21213f,	0.22962f,	0.38270f,	0.38270f,
	0.20000f,	0.18478f,	0.27717f,	0.21213f,	0.42426f,	0.70710f,	0.70710f,	0.38270f,
	0.30000f,	0.27717f,	0.27717f,	0.55434f,	0.70710f,	0.70710f,	0.70710f,	0.70710f,
	0.60000f,	0.55434f,	0.55434f,	0.92390f,	0.70710f,	0.70710f,	0.70710f,	0.70710f,
	1.00000f,	1.00000f,	0.92390f,	0.92390f,	0.92390f,	0.70710f,	0.70710f,	0.70710f,
};
const float* Asura_PSP_Input::s_pfConversionArray = afASURA_PSP_STICKCONVERT;

u_char Asura_PSP_Input::s_ucPrevx(0);
u_char Asura_PSP_Input::s_ucPrevy(0);

bool Asura_PSP_Input::s_bIntercepted = false;

const u_char uASURA_PSP_AXISCENTRE(0x80);


void Asura_PSP_Input::Initialise(unsigned char *pucAsuraCurrentInput, float *pfAsuraAxis)
{
	s_pucAsuraCurrentInput = pucAsuraCurrentInput;
	s_pfAsuraAxis = pfAsuraAxis;
	ReconfigureKeys();
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
#ifndef ASURA_COMMAND_CONSOLE_READONLY
    Asura_CommandConsole::AddVar( L"PSP.Deadzone", &s_fAnalogStickDeadZone, 0.0f, 128.0f, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
    Asura_CommandConsole::AddVar( L"PSP.UseConversionArray", &s_bUseConversionArray, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif //ifndef ASURA_COMMAND_CONSOLE_READONLY
#endif //ASURA_SUPPORT_COMMAND_CONSOLE
}

void Asura_PSP_Input::Shutdown()
{

}

void Asura_PSP_Input::Reset()
{

}

bool Asura_PSP_Input::IsKeyToggled(int iKey)
{
	return false;
}

float Asura_PSP_Input::ConvertPSPAxisToAsuraAxis(u_char ucPSPAxis)
{
	// Take into acount deadzones etc.
	float fA = static_cast<float>(ucPSPAxis) - 127.f;

	if(fA > s_fAnalogStickDeadZone)
	{
		fA = (fA - s_fAnalogStickDeadZone) * (128.0f / (128.0f - s_fAnalogStickDeadZone));
	}
	else if(fA < -s_fAnalogStickDeadZone)
	{
		fA = (fA + s_fAnalogStickDeadZone) * (127.0f / (127.0f - s_fAnalogStickDeadZone));
	}
	else
	{
        return 0.f;
	}
	
	// Normalise
	fA *= fA < 0.0f ? 1.0f / 127.0f : 1.0f / 128.0f;
	return fA;		// now in range -1.f to 1.f
}

// (NY - 18.05.2007) derived from SCE's analog_ctrl sample, with optimisations and asura-fied
//also split up one constant that should have been two.
void Asura_PSP_Input::ConvertPosXY(u_char ucContX, u_char ucContY, float* pfX, float* pfY)
{
	const u_char uDeadZone_Abs(static_cast<u_char>(Asura_Maths::ClampToRange(Asura_Maths::FloatToInt_Near(s_fAnalogStickDeadZone), 0, static_cast<int>(uASURA_PSP_AXISCENTRE))));
	const u_char uDeadZone_Low(uASURA_PSP_AXISCENTRE - uDeadZone_Abs);
	const u_char uDeadZone_High(uASURA_PSP_AXISCENTRE + uDeadZone_Abs);
	const u_char uDeadZone_MovementThreshold(5);
	const u_char uDeadZone_MovementValue(0.2f);

	*pfX = 0.0f;
	*pfY = 0.0f;
	bool bFlipX = false;
	bool bFlipY = false;
	int iAbsX;
	int iAbsY;

	//work with absolute values for both axes, as conversion array is just one corner
	iAbsX = ucContX - uASURA_PSP_AXISCENTRE;
	iAbsY = ucContY - uASURA_PSP_AXISCENTRE;
	if (iAbsX < 0)
	{
		iAbsX = -iAbsX - 1;
		bFlipX = true;
	}
	if (iAbsY < 0)
	{
		iAbsY = -iAbsY - 1;
		bFlipY = true;
	}

	//deadzone - returns low value if moving with deadzone
	if (iAbsX <= uDeadZone_Abs && iAbsY <= uDeadZone_Abs)
	{
		if (uDeadZone_Low <= s_ucPrevx && s_ucPrevx <= uDeadZone_High
			&& uDeadZone_Low <= s_ucPrevy && s_ucPrevy <= uDeadZone_High)
		{
			if ((ucContX < uASURA_PSP_AXISCENTRE && uDeadZone_MovementThreshold < s_ucPrevx - ucContX)
				|| (uASURA_PSP_AXISCENTRE < ucContX && uDeadZone_MovementThreshold < ucContX - s_ucPrevx))
			{
				*pfX = uDeadZone_MovementValue;
			}

			if ((ucContY < uASURA_PSP_AXISCENTRE && uDeadZone_MovementThreshold < s_ucPrevy - ucContY)
				|| (uASURA_PSP_AXISCENTRE < ucContY && uDeadZone_MovementThreshold < ucContY - s_ucPrevy))
			{
				*pfY = uDeadZone_MovementValue;
			}
		}
	}
	else
	{
		//perform lookup - Y values are same as x with input values swapped
		int iX = iAbsX / 16;
		int iY = iAbsY / 16;
		*pfX = s_pfConversionArray[iX * 8 + iY];
		*pfY = s_pfConversionArray[iY * 8 + iX];
	}

	if (bFlipX)
	{
		*pfX = -*pfX;
	}
	if (!bFlipY)//we want y inverted for asura
	{
		*pfY = -*pfY;
	}

	//store last values to compare for smart deadzone
	s_ucPrevx = ucContX;
	s_ucPrevy = ucContY;

	return;
}

void Asura_PSP_Input::SetUseConversionArray( bool bUseConversionArray, const float* pfConversionArray /*= 0*/ )
{
	s_bUseConversionArray = bUseConversionArray;
	if (pfConversionArray)
	{
		s_pfConversionArray = pfConversionArray;
	}
}

void Asura_PSP_Input::Update()
{
	// Read the pad
	SceCtrlData axBuffer[8];
	int iRet;
	u_int uButtonFlags;
	u_char ucX;
	u_char ucY;

	iRet = sceCtrlReadBufferPositive(axBuffer, 8);

	if (iRet) 
	{
		uButtonFlags = axBuffer[0].Buttons;
		ucX = axBuffer[0].Lx;        // X axis:0x00-0xff
		ucY = axBuffer[0].Ly;        // Y axis:0x00-0xff
	}
	else 
	{
		uButtonFlags = 0;
		ucX = uASURA_PSP_AXISCENTRE;            // X axis centre (0x80)
		ucY = uASURA_PSP_AXISCENTRE;            // Y axis centre (0x80)
	}

	if (s_bUseConversionArray)
	{
		//perform lookup using both axes
		ConvertPosXY(ucX, ucY, (s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_H), (s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_V));
	}
	else
	{
		*(s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_H) = ConvertPSPAxisToAsuraAxis(ucX);
		*(s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_V) = -ConvertPSPAxisToAsuraAxis(ucY);
	}

	const bool bAxisUpdate = ((*(s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_H) != 0.0f) || (*(s_pfAsuraAxis + ASURA_AXIS_PAD1_LSTICK_V) != 0.0f));
	if(bAxisUpdate)
	{
		// Axis updates don't automatically suppress power save, so make sure they do
		sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DEFAULT);
	}

	// Translate the buttons.
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_CROSS) = ((uButtonFlags & SCE_CTRL_CROSS) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_CIRCLE) = ((uButtonFlags & SCE_CTRL_CIRCLE) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_SQUARE) = ((uButtonFlags & SCE_CTRL_SQUARE) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_TRIANGLE) = ((uButtonFlags & SCE_CTRL_TRIANGLE) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_START) = ((uButtonFlags & SCE_CTRL_START) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_SELECT) = ((uButtonFlags & SCE_CTRL_SELECT) != 0) * 255;
#if (SCE_DEVKIT_VERSION >= 0x02080000)
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_L1) = ((uButtonFlags & SCE_CTRL_L) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_R1) = ((uButtonFlags & SCE_CTRL_R) != 0) * 255;
#else
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_L1) = ((uButtonFlags & SCE_CTRL_L1) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_R1) = ((uButtonFlags & SCE_CTRL_R1) != 0) * 255;
#endif
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_UP) = ((uButtonFlags & SCE_CTRL_UP) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_DOWN) = ((uButtonFlags & SCE_CTRL_DOWN) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_LEFT) = ((uButtonFlags & SCE_CTRL_LEFT) != 0) * 255;
	*(s_pucAsuraCurrentInput + ASURA_PSP_KEYID_PAD1_RIGHT) = ((uButtonFlags & SCE_CTRL_RIGHT) != 0) * 255;

#ifdef ASURA_PSP_DEMO
	int iData = 0;
	if(uButtonFlags || bAxisUpdate)
	{
		sceDemoService(SCE_PSPDEMO_SERVICE_FRAME_NONIDLE, &iData, NULL);
	}
	else
	{
		sceDemoService(SCE_PSPDEMO_SERVICE_FRAME_IDLE, &iData, NULL);
	}
#endif//def ASURA_PSP_DEMO

	// Check for intercepted flag, useful for discovering if HOME has been pressed.
	s_bIntercepted = (uButtonFlags & SCE_CTRL_INTERCEPTED) != 0;
}

void Asura_PSP_Input::ReconfigureKeys()
{
}
