/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/src/sgx/input/input.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/input/input.hpp"

CSGXInputManager *CSGXInputManager::m_pSingleton;

CSGXInputManager *CSGXInputManager::create()
{
	m_pSingleton = new CSGXInputManager();
	if (m_pSingleton) {
		m_pSingleton->initialize();
	}
	return m_pSingleton;
}

CSGXInputManager *CSGXInputManager::get()
{
	if (!m_pSingleton) {
		return create();
	}
	return m_pSingleton;
}

void 
CSGXInputManager::initializeGeneric()
{
}

void 
CSGXInputManager::initialize()
{
	// create original input buffers. All control data is written
	// initially into these structures. These can, potentially, be
	// platform-specific.
	const tUINT32 &controllers = getMaxControllers();
	m_pDeviceKeyboard = createKeyboard();
	m_pDeviceMouse = createMouse();

	m_pDeviceController = new CSGXInputManager::CControllerJoypad *[controllers];
    m_pController = new CSGXInputManager::CControllerJoypad *[controllers];
	for(tUINT32 i=0;i<controllers;++i) {
		m_pController[i] = new CControllerJoypad(i);	// always generic
		m_pDeviceController[i] = createJoypad(i);
	}

	m_Mapping = new tUINT32[getMaxControllers()];

	createMapping();
}

CSGXInputManager::CControllerJoypad *
CSGXInputManager::createJoypad(tUINT32 idx) {
	return new CControllerJoypad(idx);
}

CSGXInputManager::CControllerKeyboard *
CSGXInputManager::createKeyboard() {
	return new CControllerKeyboard();
}

CSGXInputManager::CControllerMouse *
CSGXInputManager::createMouse() {
	return new CControllerMouse();
}

tUINT32 
CSGXInputManager::getNumControllers()
{
	return 1;
}

tUINT32 
CSGXInputManager::getMaxControllers()
{
	return 2;
}

void 
CSGXInputManager::suspend(const tBOOL suspend) {
	for(tMEM_SIZE i=0;i<getMaxControllers();i++) {
		m_pController[i]->suspend(suspend);
	}
}

void 
CSGXInputManager::stopAllRumbles() {
	for(tMEM_SIZE i=0;i<getMaxControllers();i++) {
		m_pController[i]->stopAllRumbles();
	}
}

void 
CSGXInputManager::update(const tREAL32 telaps)
{
	// We update the buffered input, and
	// write the results back to the cross-platform version
	for(tMEM_SIZE i=0;i<getMaxControllers();i++) {
		if (m_pDeviceController[i]) {
			m_pDeviceController[i]->update(telaps, *m_pController[i]);
		}
	}

	if (m_pDeviceKeyboard) {
		m_pDeviceKeyboard->update(telaps, m_Keyboard);
	}

	if (m_pDeviceMouse) {
	   m_pDeviceMouse->update(telaps, m_Mouse);
	}
	
}

void 
CSGXInputManager::createMapping()
{
    for(tUINT32 i=0; i<getMaxControllers(); i++) {
        m_Mapping[i] = i;
    }
}

//
// Command Handlers
//


tBOOL	
CSGXInputManager::setCommandHandler(const sgxString &id, const CSGXInputManager::CCommandHandlerParameters &cmd) {
	return setCommandHandler(sgxGetCRC32(id), cmd);
}

tBOOL	
CSGXInputManager::getCommandState(const sgxString &id) {
	return getCommandState(sgxGetCRC32(id));
}

tREAL32	
CSGXInputManager::getCommandPosition(const sgxString &id) {
	return getCommandPosition(sgxGetCRC32(id));
}

void	
CSGXInputManager::resetCommandHandler() {
	m_CommandHandlers.clear();
}
//
void		
CSGXInputManager::setCommandHandlerKeyboardMapping(const tUINT32 *pCmdIDKeyList, const tUINT32 size) {
	for(tUINT32 i=0;i<size;++i) {
		setCommandHandlerKeyboardMapping(pCmdIDKeyList[2*i], pCmdIDKeyList[2*i + 1]);
	}
}

void		
CSGXInputManager::setCommandHandlerKeyboardMapping(const tUINT32 cmdID, const tUINT32 key) {
	setCommandHandler(cmdID,CCommandHandlerParameters(&m_Keyboard.m_Key[key]));
}

tBOOL
CSGXInputManager::setCommandHandler(const tUINT32 id, const CCommandHandlerParameters &cmd) {
	m_CommandHandlers[id] = cmd;	
	return TRUE;//m_CommandHandlers[id];
}

tBOOL	
CSGXInputManager::getCommandState(tUINT32 id) {
	const CCommandHandlerParameters &cmd = m_CommandHandlers[id];
	if (cmd.pCommand) {
		return cmd.pCommand->getState(cmd);
	}
	return FALSE;
}

tBOOL	
CSGXInputManager::wasCommandPressed(tUINT32 id) {
	const CCommandHandlerParameters &cmd = m_CommandHandlers[id];
	if (cmd.pCommand) {
		return cmd.pCommand->wasPressed(cmd);
	}
	return FALSE;
}

tBOOL	
CSGXInputManager::wasCommandReleased(tUINT32 id) {
	const CCommandHandlerParameters &cmd = m_CommandHandlers[id];
	if (cmd.pCommand) {
		return cmd.pCommand->wasReleased(cmd);
	}
	return FALSE;
}

tREAL32	
CSGXInputManager::getCommandPosition(tUINT32 id) {
	const CCommandHandlerParameters &cmd = m_CommandHandlers[id];
	if (cmd.pCommand) {
		return cmd.pCommand->getPosition(cmd);
	}
	return 0;
}


CSGXInputManager::CCommandHandlerParameters::CCommandHandlerParameters(const CControlCommand *pCommand, tREAL32 rangeFrom, tREAL32 rangeTo) {
	this->pCommand = pCommand;
	fRangeFrom = rangeFrom;
	fRangeTo = rangeTo;
	fTargetScaleFrom = 0;
	fTargetScaleRangeTo = 1.0f;
}

CSGXInputManager::CCommandHandlerParameters::CCommandHandlerParameters(CSGXInputManager::tControl commandControl, tREAL32 rangeFrom, tREAL32 rangeTo) {
	this->pCommand = &CSGXInputManager::get()->getControlButton(CSGXInputManager::eFirst, commandControl);
	fRangeFrom = rangeFrom;
	fRangeTo = rangeTo;
	fTargetScaleFrom = 0;
	fTargetScaleRangeTo = 1.0f;
}


tREAL32
CSGXInputManager::CCommandHandlerParameters::prepareRange(const tREAL32 value) const {

	// check whether it's in the required range of the stick
	tREAL32 newValue = sgxRange(value, fRangeFrom, fRangeTo);

	// rescale to match the users requirements
	return sgxRescale(newValue, fRangeFrom, fRangeTo, fTargetScaleFrom, fTargetScaleRangeTo);
}
