
#include "AdeptHeaders.hpp"
#include "control_mapping.hpp"
#include "Tool.hpp"
#include "Joystick.hpp"
#include "interface.hpp"
#include "missionlang\resource.h"

extern int g_bUseOrgJoy; // jcem

namespace Adept
{
	CControlMappingList *g_ControlList=NULL;
}

CControlMappingList::CControlMappingList (const char *filename,Entity::ClassData *classdata)
{
	m_Mappings.clear ();
	m_nCount = 0;
	m_pcdHead = NULL;
	m_fInvertJoyY = false;
	m_fInvertMouseY = true;
	m_Joystick = 0;
	m_joyStickAllowed = true;
	m_mouseAllowed = false;
	m_joystickButtons = 0;
	m_joyStickShiftValue = -1;
	m_ForceFeedBack = true;
	m_MouseSensitivity = 0.25f;
	m_JoystickXAxisType = TURNING_AXIS;
	m_JoystickRudderAxisType = TORSO_AXIS;
	m_MouseXAxisType = TURNING_AXIS;
	InitializeThrottleConstants();
	LoadList (filename,classdata);
}

CControlMappingList::CControlMappingList (const ResourceID& control_stream)
{
	m_Mappings.clear ();
	m_nCount = 0;
	m_pcdHead = NULL;
	m_fInvertJoyY = false;
	m_fInvertMouseY = true;
	m_Joystick = 0;
	m_joyStickAllowed = true;
	m_mouseAllowed = false;
	m_joystickButtons = 0;
	m_joyStickShiftValue = -1;
	m_ForceFeedBack = true;
	m_MouseSensitivity = 0.25f;
	m_JoystickXAxisType = TURNING_AXIS;
	m_JoystickRudderAxisType = TORSO_AXIS;
	m_MouseXAxisType = TURNING_AXIS;
	InitializeThrottleConstants();
	LoadList (control_stream);
}

CControlMappingList::CControlMappingList (void)
{
	m_Mappings.clear ();
	m_nCount = 0;
	m_pcdHead = NULL;
	m_fInvertJoyY = false;
	m_fInvertMouseY = true;
	m_Joystick = 0;
	m_joyStickAllowed = true;
	m_mouseAllowed = false;
	m_joystickButtons = 0;
	m_joyStickShiftValue = -1;
	m_ForceFeedBack = true;
	m_MouseSensitivity = 0.25f;
	m_JoystickXAxisType = TURNING_AXIS;
	m_JoystickRudderAxisType = TORSO_AXIS;
	m_MouseXAxisType = TURNING_AXIS;
	InitializeThrottleConstants();
}

CControlMappingList::~CControlMappingList (void)
{
	if (m_pcdHead)
	{
		delete[] m_pcdHead;
		m_pcdHead = NULL;
	}
}

void CControlMappingList::SaveList (MemoryStream& resource_locker)
{
	stlport::vector<ControlData>::iterator iter;
	resource_locker << m_Joystick;
	resource_locker << m_ForceFeedBack;
	resource_locker << m_MouseSensitivity;
	resource_locker << m_joyStickShiftValue;
	resource_locker << m_mouseAllowed;
	resource_locker << m_JoystickXAxisType;
	resource_locker << m_JoystickRudderAxisType;
	resource_locker << m_MouseXAxisType;

// Can't save in options.mw4 file, save in options.ini file instead
#if 0
	resource_locker << m_joystick_throttle_center;	
	resource_locker << m_joystick_throttle_dead_low;	
	resource_locker << m_joystick_throttle_dead_high;	
#else
	NotationFile startup_ini("options.ini", NotationFile::Standard, true);
	Stuff::Page *joypage = startup_ini.FindPage("joystick");
	if (!joypage)
	{
		startup_ini.AddPage("joystick");
	}
//  if (g_bUseOrgJoy) { // jcem
	if (Close_Enough(m_joystick_throttle_center, m_joystick_throttle_dead_low))
		joypage->SetEntry("OneWayThrottle", true);
	else
		joypage->SetEntry("OneWayThrottle", false);
//  }

	startup_ini.Save();
#endif

	resource_locker << m_Mappings.size ();
	for (iter = m_Mappings.begin ();iter != m_Mappings.end ();iter++)
	{
		resource_locker.WriteBytes (&(*iter),sizeof (*iter));
	}
}

void CControlMappingList::SaveList (const ResourceID& control_stream)
{
	Check_Object (ControlsManager::Instance);

	Resource resource_locker(control_stream);
	SaveList (resource_locker);
}

void CControlMappingList::LoadList (MemoryStream& resource_locker)
{
	m_Mappings.clear ();
	gos_PushCurrentHeap(Adept::g_Heap);

	int mapping_count;
	resource_locker >> m_Joystick;
	resource_locker >> m_ForceFeedBack;
	resource_locker >> m_MouseSensitivity;
	resource_locker >> m_joyStickShiftValue;
	resource_locker >> m_mouseAllowed;
	resource_locker >> m_JoystickXAxisType;
	resource_locker >> m_JoystickRudderAxisType;
	resource_locker >> m_MouseXAxisType;

// Load from options.ini file instead
#if 0
	resource_locker >> m_joystick_throttle_center;	
	resource_locker >> m_joystick_throttle_dead_low;	
	resource_locker >> m_joystick_throttle_dead_high;
#else
	NotationFile startup_ini("options.ini", NotationFile::Standard, true);
	Stuff::Page *joypage = startup_ini.FindPage("joystick");
	if (joypage)
	{
		bool bOneWay = false;
//      if (g_bUseOrgJoy) { // jcem
		joypage->GetEntry("OneWayThrottle", &bOneWay);
//      }
		if (bOneWay) {
			joypage->GetEntry("OneThrottleCenter", &m_joystick_throttle_center);
			joypage->GetEntry("OneThrottleCenter", &m_joystick_throttle_dead_low);
			joypage->GetEntry("OneThrottleHigh", &m_joystick_throttle_dead_high);
		}
		else {
//          if (g_bUseOrgJoy) { // jcem
			joypage->GetEntry("BiThrottleCenter", &m_joystick_throttle_center);
			joypage->GetEntry("BiThrottleLow", &m_joystick_throttle_dead_low);
			joypage->GetEntry("BiThrottleHigh", &m_joystick_throttle_dead_high);
//          } else { // jcem
//			m_joystick_throttle_center = 0.3;
//			m_joystick_throttle_dead_low = 0.025;
//			m_joystick_throttle_dead_high = 0.025;
//          }
		}
	}
#endif

	if (m_Joystick >= gosJoystick_CountJoysticks (false))
	{
		m_Joystick = 0;
		m_joystickButtons = 0;
	}
	if (m_joystickButtons == 0 && gosJoystick_CountJoysticks(false) > 0)
	{
		gosJoystick_Info ji;
		memset(&ji,0,sizeof(ji));
		gosJoystick_GetInfo(m_Joystick, &ji);

		m_joystickButtons = ji.nButtons;
	}

	resource_locker >> mapping_count;
	const ControlData *mapping = Cast_Pointer(const ControlData*,resource_locker.GetPointer());
	m_Mappings.reserve (mapping_count);
	for (int i=0; i<mapping_count; ++i, ++mapping)
	{
#ifndef LAB_ONLY					// in release build remove no string mapping command, these should only be debug command
		if (mapping->nameresid == 0)
			continue;
#endif
		m_Mappings.push_back (*mapping);
	}
	gos_PopCurrentHeap();
	GetInvertStateOfList();
}

void CControlMappingList::LoadList (const ResourceID& control_stream)
{
	m_Mappings.clear ();

	Resource resource_locker(control_stream);
	LoadList (resource_locker);
}

void CControlMappingList::LoadDefault (void)
{
	m_Joystick = 0;
	m_mouseAllowed = false;
	int count = gosJoystick_CountJoysticks (false);
	if (count)
	{
		gosJoystick_Info info;
		gosJoystick_GetInfo (m_Joystick,&info);
		m_joyStickShiftValue = info.nButtons-1;
		if (m_joyStickShiftValue < 8)
		{
			m_joyStickShiftValue = -1;
		}
	}
	else
	{
		m_joyStickShiftValue = -1;
	}
	m_ForceFeedBack = true;
	m_MouseSensitivity = 0.02f;
	m_JoystickXAxisType = TURNING_AXIS;
	m_JoystickRudderAxisType = TORSO_AXIS;
	m_MouseXAxisType = TURNING_AXIS;
	InitializeThrottleConstants();

	m_Mappings.clear ();
	gos_PushCurrentHeap(Adept::g_Heap);
	FillDefault(m_Mappings);
	gos_PopCurrentHeap();

//	Resource res ("content\\Interfaces\\Common\\interface.control");
//	res.LoadData ();
//	Verify (res.DoesResourceExist ());
//	LoadList (res);
}

int CControlMappingList::GetList(bool fRestoreDefaults)
{
	if (fRestoreDefaults)
	{
		m_Mappings.clear ();
		gos_PushCurrentHeap(Adept::g_Heap);
		FillDefault(m_Mappings);
		gos_PopCurrentHeap();
	}

	stlport::vector<ControlData>::iterator iter;
	for (iter = m_Mappings.begin ();iter != m_Mappings.end ();iter++)
	{
		if (iter->mesg_id != 0)
		{
			if ((iter->group == ControlsManager::VirtualButtonGroupID) && ((iter->element == MOUSE_WHEEL_UP)||(iter->element == MOUSE_WHEEL_DOWN)))
				iter->group = ControlsManager::MouseButtonGroupID;
		}
	}

	//  Calculate worst-case size of array
	m_nCount = 0;
	for (iter = m_Mappings.begin ();iter != m_Mappings.end (); iter++)
	{
		//
		//If this is a Direct command (0 == mesg_id) then we ignore it, UI doesn't allow remapping axes...
		//
		if (iter->mesg_id  && (iter->group == ControlsManager::JoystickButtonGroupID
			|| iter->group == ControlsManager::MouseButtonGroupID
			|| iter->group == ControlsManager::VirtualButtonGroupID
			|| iter->group == ControlsManager::JoystickHatID) )
		{
			m_nCount++;
		}
	}

	//
	// Allocate and clear control array
	// This is allocated to + 1 and cleared to zero to allow for an end-of-list object.
	// It would be far better to maintain a new list size, but we're approaching ZBR, so I'll refrain from
	// casual changes.
	//
	delete[] m_pcdHead;
	m_pcdHead = new ControlDef[m_nCount + 1];		//BUGBUG:DAN - make this use constructor/destructor instead
	memset(m_pcdHead, 0, (m_nCount + 1) * sizeof(ControlDef));

	ControlDef *pcdPtr;
	for (iter = m_Mappings.begin ();iter != m_Mappings.end (); iter++)
	{
		//
		//If this is a Direct command (0 == mesg_id) then we ignore it, UI doesn't allow remapping axes...
		//
		if (iter->mesg_id)
		{
			//
			//Search list of control defs to see if command with this mesg_id already exists.  We do this
			//because each command will have three entries in the vector, one for key/mouse/joy and the order
			//these items are stored in the vector isn't guaranteed.  The code that interacts with gosScript
			//wants all the info for a particular command (unique mesg_id) in one place.
			//
			pcdPtr = m_pcdHead;
			while (pcdPtr->mesg_id && pcdPtr->mesg_id != iter->mesg_id)
			{
				pcdPtr++;
			}

			//
			//Determine type of command being iterated and save the key/joy/mouse id (iter->element)
			//
			bool skip = false;
			switch (iter->group)
			{
			case ControlsManager::MouseButtonGroupID:
				if (!pcdPtr->cd_mouse_valid)
				{
					pcdPtr->nMouse = iter->element;
					pcdPtr->cd_mouse = *iter;
					pcdPtr->cd_mouse_valid = true;
					break;
				}
				skip = true;
				break;

			case ControlsManager::VirtualButtonGroupID:	
				if (!pcdPtr->cd_key_valid)
				{
					pcdPtr->nKey = iter->element;
					pcdPtr->control_mask = iter->control_mask;
					pcdPtr->cd_key = *iter;
					pcdPtr->cd_key_valid = true;
					break;
				}
				skip = true;
				break;

			case ControlsManager::JoystickButtonGroupID:
				if (!pcdPtr->cd_joy_valid)
				{
					pcdPtr->nJoy = iter->element;
					pcdPtr->cd_joy = *iter;
					pcdPtr->cd_joy_valid = true;
					break;
				}
				skip = true;
				break;

			case ControlsManager::JoystickHatID:
				if (!pcdPtr->cd_joy_valid)
				{
					pcdPtr->nJoy = iter->element+10;
					pcdPtr->cd_joy = *iter;
					pcdPtr->cd_joy_valid = true;
					break;
				}
				skip = true;
				break;

			default:
				skip = true;
				break;
			}

			if (!skip)
			{
				//
				//If this is the first vector entry for this command, save the common info
				//
				if (!pcdPtr->mesg_id)
				{
					pcdPtr->mesg_id = iter->mesg_id;
					pcdPtr->nameresid = iter->nameresid;
					pcdPtr->control_type = iter->control_type;
				}
			}
		}
	}


	//
	// Determine the size of the new list by looking for the end-of-list object (mesg_id == 0)
	//
	int nWho = 0;
	pcdPtr = m_pcdHead;
	while (pcdPtr->mesg_id)
	{
		pcdPtr->nIdx = nWho++;	// BUGBUG:DAN Adjust for category offset
		pcdPtr++;
	}

	m_nCount = nWho;	// actual size of used elements in array

	return m_nCount;
}


void CControlMappingList::JoystickXAxis (int value, Plug* owner)
{
	Verify (value>=0);
	Verify (value < 3);
	m_JoystickXAxisType = value;
	if (owner)
		ApplyList (owner);
}
void CControlMappingList::JoystickRudderAxis (int value, Plug* owner)
{
	Verify (value>=0);
	Verify (value < 3);
	m_JoystickRudderAxisType = value;
	if (owner)
		ApplyList (owner);
}
void CControlMappingList::MouseXAxis (int value, Plug* owner)
{
	Verify (value>=0);
	Verify (value < 3);
	m_MouseXAxisType = value;
	if (owner)
		ApplyList (owner);
}
void CControlMappingList::ForceFeedBack (bool value, Plug* owner)
{
	m_ForceFeedBack = value;
	if (owner)
		ApplyList (owner);
}

void CControlMappingList::MouseSensitivity (Scalar value, Plug* owner)
{
	m_MouseSensitivity = value;
	if (owner)
		ApplyList (owner);
}
void CControlMappingList::Joystick(int id, Plug* owner)
{
	m_Joystick = id;

	gosJoystick_Info ji;
	memset(&ji,0,sizeof(ji));
	gosJoystick_GetInfo(id, &ji);

	m_joystickButtons = ji.nButtons;

	if (owner)
		ApplyList(owner);
}

void CControlMappingList::SetJoyYInvert(bool b, Plug* owner)
{
	m_fInvertJoyY = b;
	SetInvertStateOfList();
	if (owner)
		ApplyList(owner);
}

void CControlMappingList::SetMouseYInvert(bool b, Plug* owner)
{
	m_fInvertMouseY = b;
	SetInvertStateOfList();
	if (owner)
		ApplyList(owner);
}

void CControlMappingList::SetMouseAllowed(bool b, Plug* owner)
{
	m_mouseAllowed = b;
	if (owner)
		ApplyList(owner);
}

void CControlMappingList::SetJoyStickAllowed(bool b, Plug* owner)
{
	m_joyStickAllowed = b;
	if (owner)
		ApplyList(owner);
}

int CControlMappingList::SetJoyStickShiftAllowed(bool b, Plug* owner, int* pIndex1, int* pIndex2)
{
	int retCount = 0;
	if (b)
	{
		int count = gosJoystick_CountJoysticks (false);
		if (count)
		{
			gosJoystick_Info info;
			gosJoystick_GetInfo (m_Joystick,&info);
			m_joyStickShiftValue = info.nButtons-1;
			if (m_joyStickShiftValue < 8)
			{
				m_joyStickShiftValue = -1;
			}
		}
		else
		{
			m_joyStickShiftValue = -1;
		}
		retCount = SetJoyStickShiftValue(m_joyStickShiftValue, owner, pIndex1, pIndex2);
	}
	else
	{
		m_joyStickShiftValue = -1;
	}
	if (owner)
		ApplyList(owner);
	return retCount;
}

int CControlMappingList::SetJoyStickShiftValue(int n, Plug* owner, int* pChangeIndex1, int* pChangeIndex2)
{
	m_joyStickShiftValue = n;

	if (m_pcdHead == NULL)
		GetList(false);

	// Looking to unmap any use of this button (shifted or non-shifted)
	int returnIndex[2];
	int returnCount = 0;
	for (int i = 0; i < m_nCount; i++)
	{
		if (m_pcdHead[i].cd_joy_valid && m_pcdHead[i].nJoy == m_joyStickShiftValue)
		{
			returnIndex[returnCount] = i;
			returnCount++;

			m_pcdHead[i].nJoy = 0;
			m_pcdHead[i].cd_joy_valid = false;
			m_pcdHead[i].cd_joy.element = 0;
			m_pcdHead[i].cd_joy.control_mask = 0;
		}
	}
	gosASSERT(returnCount <= 2);
	SetList(owner);

	*pChangeIndex1 = returnIndex[0];
	*pChangeIndex2 = returnIndex[1];
	return returnCount;
}

void CControlMappingList::JoyStickThrottleCenter(Stuff::Scalar s, Plug* owner)
{
	m_joystick_throttle_center = s;
	if (owner)
		ApplyList(owner);
}

void CControlMappingList::JoyStickThrottleLow(Stuff::Scalar s, Plug* owner)
{
	m_joystick_throttle_dead_low = s;
	if (owner)
		ApplyList(owner);
}

void CControlMappingList::JoyStickThrottleHigh(Stuff::Scalar s, Plug* owner)
{
	m_joystick_throttle_dead_high = s;
	if (owner)
		ApplyList(owner);
}	

void CControlMappingList::SetInvertStateOfList()
{
	//
	//Iterate vector and set correct inversion state for any joystick or mouse Y axes
	//
	stlport::vector<ControlData>::iterator iter;
	for (iter = m_Mappings.begin (); iter != m_Mappings.end (); iter++)
	{
		if (0 == iter->mesg_id)
		{
			switch (iter->group)
			{
			case ControlsManager::JoystickAxisGroupID:
				if (Joystick::YAxis == iter->element)
				{
					iter->invert = m_fInvertJoyY;
				}
				break;

			case ControlsManager::MousePosYID:
			case ControlsManager::MouseDeltaYID:
				iter->invert = m_fInvertMouseY;
				break;
			}
		}
	}
}

void CControlMappingList::GetInvertStateOfList()
{
	//
	//Iterate vector and set correct inversion state for any joystick or mouse Y axes
	//
	stlport::vector<ControlData>::iterator iter;
	bool joyInvertFound = false;
	bool mouseInvertFound = false;
	for (iter = m_Mappings.begin (); iter != m_Mappings.end (); iter++)
	{
		if (0 == iter->mesg_id)
		{
			switch (iter->group)
			{
				case ControlsManager::JoystickAxisGroupID:
				if (Joystick::YAxis == iter->element)
				{
					m_fInvertJoyY = iter->invert;
					joyInvertFound = true;
					if (mouseInvertFound)
						return;
				}
				break;

				case ControlsManager::MousePosYID:
				case ControlsManager::MouseDeltaYID:
				{
					m_fInvertMouseY = iter->invert;
					mouseInvertFound = true;
					if (joyInvertFound)
						return;
				}
				break;
			}
		}
	}
	gosASSERT(mouseInvertFound);
	gosASSERT(joyInvertFound);
}

void CControlMappingList::SetList(Plug* owner)
{
	gosASSERT(m_pcdHead != NULL);
	if (m_pcdHead == NULL)
		GetList(false);

	//
	//Put everything that's not already in our mapping list into the dest list
	//
	stlport::vector<ControlData>::iterator sIter;
	stlport::vector<ControlData> newList;
	for (sIter = m_Mappings.begin ();sIter != m_Mappings.end (); ++sIter)
	{
			//
		if (sIter->mesg_id == 0)
			newList.push_back(*sIter);
		else if (sIter->group != ControlsManager::JoystickButtonGroupID
			&& sIter->group != ControlsManager::MouseButtonGroupID
			&& sIter->group != ControlsManager::VirtualButtonGroupID
			&& sIter->group != ControlsManager::JoystickHatID)
		{
			newList.push_back(*sIter);
		}
	}


	//
	// Now put everything in our mapping list into the ControlData form and add it to the new list
	//
	ControlDef* pcdPtr = m_pcdHead;
	for (int i = 0; i < m_nCount; ++i)
	{
		if (pcdPtr->cd_key_valid || (!pcdPtr->cd_mouse_valid && !pcdPtr->cd_joy_valid))
		{
			newList.push_back(pcdPtr->cd_key);
		}

		if (pcdPtr->cd_mouse_valid)
		{
			newList.push_back(pcdPtr->cd_mouse);
		}

		if (pcdPtr->cd_joy_valid)
		{
			newList.push_back(pcdPtr->cd_joy);
		}
		pcdPtr++;
	}

	//
	//  Now copy the new temp list to the final m_Mappings position
	//
	m_Mappings = newList;
	SetInvertStateOfList();
	if (owner)
		ApplyList(owner);
}



void CControlMappingList::FillDefault (stlport::vector<ControlData>& list)
{
	HKL hkl;
	char *str;
	int index;

	hkl = GetKeyboardLayout (0);
	index = PRIMARYLANGID (LOWORD (hkl));
	switch (index)
	{
		case LANG_FRENCH:
			str = "content\\Interfaces\\Common\\freinterface.control";
			break;
		case LANG_GERMAN:
			str = "content\\Interfaces\\Common\\gerinterface.control";
			break;
		case LANG_SPANISH:
			str = "content\\Interfaces\\Common\\spainterface.control";
			break;
		case LANG_JAPANESE:
			str = "content\\Interfaces\\Common\\japinterface.control";
			break;
		case LANG_KOREAN:
			str = "content\\Interfaces\\Common\\korinterface.control";
			break;
		case LANG_CHINESE:
			str = "content\\Interfaces\\Common\\chiinterface.control";
			break;
		case LANG_ITALIAN:
			str = "content\\Interfaces\\Common\\itainterface.control";
			break;
		case LANG_PORTUGUESE:
			str = "content\\Interfaces\\Common\\porinterface.control";
			break;
		case LANG_DANISH:
			str = "content\\Interfaces\\Common\\daninterface.control";
			break;
		case LANG_NORWEGIAN:
			str = "content\\Interfaces\\Common\\norinterface.control";
			break;
		case LANG_SWEDISH:
			str = "content\\Interfaces\\Common\\sweinterface.control";
			break;
		case LANG_FINNISH:
			str = "content\\Interfaces\\Common\\fininterface.control";
			break;
		default:
			str = "content\\Interfaces\\Common\\interface.control";
			break;
	}
	Resource res (str);
	res.LoadData ();
	Verify (res.DoesResourceExist ());

	int mapping_count;
	res >> m_Joystick;
	res >> m_ForceFeedBack;
	res >> m_MouseSensitivity;
	res >> m_joyStickShiftValue;
	res >> m_mouseAllowed;
	res >> m_JoystickXAxisType;
	res >> m_JoystickRudderAxisType;
	res >> m_MouseXAxisType;
	res >> mapping_count;
	const ControlData *mapping = Cast_Pointer(const ControlData*,res.GetPointer());
	list.reserve (mapping_count);
	for (int i=0; i<mapping_count; ++i, ++mapping)
	{
#ifndef LAB_ONLY					// in release build remove no string mapping command, these should only be debug command
		if (mapping->nameresid == 0)
			continue;
#endif
		list.push_back (*mapping);
	}
	GetInvertStateOfList();
}

void CControlMappingList::ApplyList (Plug *owner)
{
	Verify (owner);
	ControlsManager::Instance->SetJoystick (m_Joystick);
	if (IsJoyStickShiftAllowed())
		ControlsManager::Instance->JoyStickShift (m_joyStickShiftValue);
	else
		ControlsManager::Instance->JoyStickShift (-1);

	if (owner->IsDerivedFrom (Interface::DefaultData))
	{
		Interface *inter;
		inter = Cast_Object (Interface *,owner);
		inter->ForceFeedBack (m_ForceFeedBack);
		inter->MouseSensitivity (m_MouseSensitivity);
		inter->MouseAllowed (m_mouseAllowed);
		inter->JoystickXAxisType (m_JoystickXAxisType);
		inter->JoystickRudderAxisType (m_JoystickRudderAxisType);
		inter->MouseXAxisType (m_MouseXAxisType);
		inter->JoyStickThrottleCenter(m_joystick_throttle_center);
		inter->JoyStickThrottleLow(m_joystick_throttle_dead_low);
		inter->JoyStickThrottleHigh(m_joystick_throttle_dead_high);
	}

	ControlsManager::Instance->RemoveAllMappings ();

	ControlsManager::Instance->CreateMapping(								// need to add the escape key
		NULL,
		0,
		Application::GetInstance(),
		Application::PauseGameMessageID,
		Application::GetInstance(),
		-1,
		ControlsManager::VirtualButtonGroupID,
		KEY_ESCAPE,
		false,
		1,
		1
	);

	stlport::vector<ControlData>::iterator iter;
	for (iter = m_Mappings.begin ();iter != m_Mappings.end ();iter++)
	{
		if (iter->mesg_id != 0)
		{
			if ((iter->group == ControlsManager::MouseButtonGroupID) && ((iter->element == MOUSE_WHEEL_UP)||(iter->element == MOUSE_WHEEL_DOWN)))
				iter->group = ControlsManager::VirtualButtonGroupID;
		}
	}

	for (iter = m_Mappings.begin ();iter != m_Mappings.end ();iter++)
	{
		//
		//----------------------
		// Connect mapping to us
		//----------------------
		//
		Entity *entity = Cast_Object (Entity *,owner);
		Check_Object(entity);

		//
		//-------------------------------------------------------
		// If this is a direct mapping, find the attribute to use
		//-------------------------------------------------------
		//
		if (iter->mesg_id == 0)
		{
			if (!AxisButtonUsed (iter->group,iter->element))
			{
				ControlsManager::Instance->CreateMapping(entity,iter->attr_id,NULL,0,owner,iter->control_mask,iter->group,iter->element,iter->invert,iter->nameresid,iter->control_type);
			}
		}
		else
		{
			if ((iter->group == ControlsManager::MouseButtonGroupID) && ((iter->element == MOUSE_WHEEL_UP)||(iter->element == MOUSE_WHEEL_DOWN)))
				iter->group = ControlsManager::VirtualButtonGroupID;

			if (!((iter->group == ControlsManager::VirtualButtonGroupID) && ((iter->element == 0)||(iter->element >= LAST_AXIS_MAPID))))
				ControlsManager::Instance->CreateMapping(NULL,0,entity,iter->mesg_id,owner,iter->control_mask,iter->group,iter->element,false,iter->nameresid,iter->control_type);
		}
	}
}

bool CControlMappingList::AxisButtonUsed (int group,int element)
{
	int value1=-1,value2=-1,value3=-1,value4=-1;				// there is no -1, so a comparison against this will fail
	switch (group)
	{
		case ControlsManager::JoystickAxisGroupID:
			switch (element)
			{
				case Joystick::XAxis:
					value1 = JOYX_LEFT;
					value2 = JOYX_RIGHT;
					break;
				case Joystick::YAxis:
					value1 = JOYY_UP;
					value2 = JOYY_DOWN;
					break;
				case Joystick::ThrottleAxis:
					value1 = JOYTHROTTLE_UP;
					value2 = JOYTHROTTLE_DOWN;
					break;
				case Joystick::RudderAxis:
					value1 = JOYRUDDER_LEFT;
					value2 = JOYRUDDER_RIGHT;
					break;
			}
			break;
		case ControlsManager::JoystickHatID:
			value1 = HAT_LEFT;
			value2 = HAT_RIGHT;
			value3 = HAT_UP;
			value4 = HAT_DOWN;
			break;
		case ControlsManager::MousePosXID:
		case ControlsManager::MouseDeltaXID:
			value1 = MOUSEX_LEFT;
			value2 = MOUSEX_RIGHT;
			break;
		case ControlsManager::MousePosYID:
		case ControlsManager::MouseDeltaYID:
			value1 = MOUSEY_UP;
			value2 = MOUSEY_DOWN;
			break;
		case ControlsManager::MouseWheelDeltaID:
			value1 = MOUSE_WHEEL_UP;
			value2 = MOUSE_WHEEL_DOWN;
			break;
		default:
			return false;
	}
	stlport::vector<ControlData>::iterator iter;
	for (iter = m_Mappings.begin ();iter != m_Mappings.end ();iter++)
	{
		if (iter->group == ControlsManager::VirtualButtonGroupID)
		{
			if (iter->element == value1)
				return true;
			if (iter->element == value2)
				return true;
			if (iter->element == value3)
				return true;
			if (iter->element == value4)
				return true;
		}
		
	}
	return false;
}

stlport::vector<ControlData>::iterator CControlMappingList::StartList (void)
{
	return m_Mappings.begin ();
}

stlport::vector<ControlData>::iterator CControlMappingList::EndList (void)
{
	return m_Mappings.end ();
}

void CControlMappingList::ReplaceList (stlport::vector<ControlData>::iterator start,stlport::vector<ControlData>::iterator end)
{
}

void CControlMappingList::LoadList (const char *filename,Entity::ClassData *classdata)
{
		//-------------------------
		// Open up the mapping file
		//-------------------------
		//
		Tool *tool = Tool::Instance;
		Check_Object(tool);

		//
		//--------------------------------------------------------
		// Create a list of all the control mappings, one per page
		//--------------------------------------------------------
		//
		m_Mappings.clear ();
		NotationFile mapping_file (filename);

		NotationFile::PageIterator *mappings = mapping_file.MakePageIterator();
		Check_Object(mappings);
		int mapping_count = mappings->GetSize();
		if (!mapping_count)
			STOP(("%s: Empty file!", mapping_file.GetFileName()));

		//
		//----------------------------------
		// For each page, create one mapping
		//----------------------------------
		//
		Page *mapping;
		while ((mapping = mappings->ReadAndNext()) != NULL)
		{
			ControlData data;

			//
			//-------------------
			// Get the IO Mapping
			//-------------------
			//
			const char *control_name,*page_name = mapping->GetName();

			mapping->GetEntry("IOMapping", &control_name, true);
			if (
				!tool->FindControlGroupAndElement(
					&data.group,
					&data.element,
					control_name
				)
			)
				STOP((
					"%s: {[%s]IOMapping=%s}: Unknown control name!",
					mapping_file.GetFileName(),
					page_name,
					control_name
				));

			//
			//----------------------------------
			// Find out who this mapping goes to
			//----------------------------------
			//
			Entity::ClassData *class_data = classdata;

			//
			//---------------------------------------
			// If there is a modemask entry, parse it
			//---------------------------------------
			//
			data.control_mask = ControlsManager::PlainFlag;
			const char *control_mask;
			if (mapping->GetEntry("ControlMask", &control_mask))
			{
				//
				//----------------------------------
				// Initialize the string for parsing
				//----------------------------------
				//
				data.control_mask = 0;
				int len = strlen(control_mask) + 1;
				char *modes = new char[len];
				Register_Pointer(modes);
				Mem_Copy(modes, control_mask, len, len);
				char *mode_copy = modes;
				while (*modes)
				{
					//
					//---------------------
					// Skip any white space
					//---------------------
					//
					while (*modes == ' ' || *modes == '\t' || *modes == '|')
						++modes;
					if (!*modes)
						break;

					//
					//----------------------------
					// Isolate the string we found
					//----------------------------
					//
					char *mode = modes;
					while (
						*modes != ' '
						 && *modes != '\t'
						 && *modes != '|'
						 && *modes
					)
					{
						++modes;
					}
					if (*modes)
						*modes++ = '\0';

					//
					//--------------------------
					// Parse the isolated string
					//--------------------------
					//

					int new_mode;
					if (!_stricmp(mode, "Plain"))
						new_mode = ControlsManager::PlainFlag;
					else if (!_stricmp(mode, "Shift"))
						new_mode = ControlsManager::ShiftFlag;
					else if (!_stricmp(mode, "Ctrl"))
						new_mode = ControlsManager::CtrlFlag;
					else if (!_stricmp(mode, "Alt"))
						new_mode = ControlsManager::AltFlag;
					else if (!_stricmp(mode, "JoyShift"))
						new_mode = ControlsManager::ShiftFlag;
					else if (!_stricmp(mode, "CtrlShift"))
						new_mode = ControlsManager::CtrlShiftFlag;
					else if (!_stricmp(mode, "ShiftAlt"))
						new_mode = ControlsManager::ShiftAltFlag;
					else if (!_stricmp(mode, "CtrlAlt"))
						new_mode = ControlsManager::CtrlAltFlag;
					else if (!_stricmp(mode, "CtrlShiftAlt"))
						new_mode = ControlsManager::CtrlShiftAltFlag;
					else if (!_stricmp(mode, "AlwaysActive"))
						new_mode = ControlsManager::AlwaysActive;
					else
					{
						STOP((
							"%s: {[%s]ControlMask=%s}: %s is an unknown control mode!",
							mapping_file.GetFileName(),
							page_name,
							control_mask,
							mode
						));
						new_mode = 0;
					}

					data.control_mask |= new_mode;
				}
				if (!data.control_mask)
					data.control_mask = ControlsManager::PlainFlag;
				Unregister_Pointer(mode_copy);
				delete mode_copy;
			}

			//
			//------------------------------------
			// Find the type of connection to make
			//------------------------------------
			//
			const char *type;
			mapping->GetEntry("Type", &type, true);

			//
			//----------------------------------------------------------------------
			// If this is an event mapping, we have to have a message ID.  How do we
			// check a message name against an unknown message list?
			//----------------------------------------------------------------------
			//
			if (!_stricmp(type, "EventMapping"))
			{
//				mapping_request->mappingType = ControlsMappingRequest::EventMapping;
				const char *message;
				mapping->GetEntry("MessageID", &message, true);
				if (class_data)
				{
					Check_Object(class_data);
					const Receiver::MessageEntry *entry =
						class_data->FindMessageEntry(message);
					if (!entry)
						STOP((
							"%s: {[%s]MessageID=%s}: Unknown message ID!",
							mapping_file.GetFileName(),
							page_name,
							message
						));

					data.mesg_id = entry->messageID;
				}
			}

			//
			//----------------------------------------------------------------------
			// If this is a direct mapping, we have to have an attribute ID.  How do
			// we find an unknown attribute of an unknown subsystem?
			//----------------------------------------------------------------------
			//
			else if (!_stricmp(type, "DirectMapping"))
			{
//				mapping_request->mappingType = ControlsMappingRequest::DirectMapping;
				const char *attribute;
				mapping->GetEntry("AttributeID", &attribute, true);
				bool invert=false;
				mapping->GetEntry("Invert",&invert,false);
				data.invert = invert;
				if (class_data)
				{
					Check_Object(class_data);
					AttributeEntry *entry =
						Cast_Pointer(Entity::ClassData*,class_data)
							->attributeTable.GetAttributeEntry(attribute);
					if (!entry)
					{
						STOP((
							"%s: {[%s]AttributeID=%s}: Unknown attribute ID!",
							mapping_file.GetFileName(),
							page_name,
							attribute
						));
					}
					Check_Object(entry);
					data.attr_id = entry->attributeID;
				}
			}
			else
			{
				STOP((
					"%s: {[%s]Type=%s}: Unknown mapping type!",
					mapping_file.GetFileName(),
					page_name,
					type
				));
			}

// MSL 5.05 MFD Comm
			int nameid=0;
			mapping->GetEntry("NameResID", &nameid, false);
			data.nameresid = nameid;
			type = "FIRE_COMMAND";
			mapping->GetEntry("Area",&type,false);
			if (!_stricmp(type, "FIRE_COMMAND"))
				data.control_type = ControlData::Fire_Control;
			else if (!_stricmp(type, "MOVE_COMMAND"))
				data.control_type = ControlData::Move_Control;
			else if (!_stricmp(type, "MISC_COMMAND"))
				data.control_type = ControlData::Misc_Control;
			else if (!_stricmp(type, "LANCEMATE_COMMAND"))
				data.control_type = ControlData::Lancemate_Control;
			else if (!_stricmp(type, "COMM_COMMAND"))
				data.control_type = ControlData::Comm_Control;
			else
			{
				STOP(("%s: {[%s]Control Area=%s}: Unknown Control Area!",mapping_file.GetFileName(),page_name,type));
			}

			m_Mappings.push_back (data);
		}

		Check_Object(mappings);
		delete mappings;
}



int CControlMappingList::compareControls(const void *elem1, const void *elem2)
{
	ControlDef *pcd1 = (ControlDef *) elem1;
	ControlDef *pcd2 = (ControlDef *) elem2;

	return pcd1->control_type - pcd2->control_type;
}


int CControlMappingList::AssignControlKey(Plug* owner, int nRow, int newKey, int newModifier)
{
	gosASSERT(nRow < m_nCount);
	gosASSERT(newKey != 0);

	//
	//Look for event collisions so we can unmap re-used events...
	//
	int returnIndex = nRow;
	ControlData cd;
	for (int i = 0; i < m_nCount; i++)
	{
		if (m_pcdHead[i].nKey == newKey  &&  m_pcdHead[i].control_mask == newModifier)
		{
			if (i != nRow)
			{
				returnIndex = i;
				m_pcdHead[i].nKey = 0;
				m_pcdHead[i].control_mask = 0;
				m_pcdHead[i].cd_key.element = 0;
				m_pcdHead[i].cd_key.control_mask = 0;
			}
			break;
		}
	}

	//  Init properties that change from old settings
	cd = m_pcdHead[nRow].cd_key;
	cd.control_mask = newModifier;
	cd.element = newKey;
	gosASSERT(cd.group == ControlsManager::VirtualButtonGroupID);

	m_pcdHead[nRow].nKey = newKey;
	m_pcdHead[nRow].control_mask = newModifier;
	m_pcdHead[nRow].cd_key = cd;
	m_pcdHead[nRow].cd_key_valid = true;

	SetList(owner);
	return returnIndex;
}


int CControlMappingList::AssignControlJoy(Plug* owner, int nRow, int newJoy, int shift)
{
	gosASSERT(nRow < m_nCount);

	//
	//Look for event collisions so we can unmap re-used events...
	//
	int returnIndex = nRow;
	ControlData cd;
	for (int i = 0; i < m_nCount; i++)
	{
		if (m_pcdHead[i].cd_joy_valid && m_pcdHead[i].nJoy == newJoy && m_pcdHead[i].cd_joy.control_mask == shift)
		{
			if (i != nRow)
			{
				returnIndex = i;
				m_pcdHead[i].nJoy = 0;
				gosASSERT(m_pcdHead[i].cd_joy_valid);
				m_pcdHead[i].cd_joy_valid = false;
				m_pcdHead[i].cd_joy.element = 0;
				m_pcdHead[i].cd_joy.control_mask = 0;
			}
			break;
		}
	}

	cd = m_pcdHead[nRow].cd_key;

	//  Init properties that change from old settings
	if (newJoy<10) {
		cd.group = ControlsManager::JoystickButtonGroupID;
		cd.element = newJoy;
		cd.control_mask = shift;
		cd.mesg_id = m_pcdHead[nRow].mesg_id;
		cd.nameresid = m_pcdHead[nRow].nameresid;
	}
	else {
		cd.group = ControlsManager::JoystickHatID;
		cd.element = newJoy-10;
		cd.control_mask = shift;
		cd.mesg_id = m_pcdHead[nRow].mesg_id;
		cd.nameresid = m_pcdHead[nRow].nameresid;
	}

	m_pcdHead[nRow].nJoy = newJoy;
	m_pcdHead[nRow].cd_joy = cd;
	m_pcdHead[nRow].cd_joy_valid = true;

	SetList(owner);
	return returnIndex;
}



int CControlMappingList::AssignControlMouse(Plug* owner, int nRow, int newMouse)
{
	gosASSERT(nRow < m_nCount);

	//
	//Look for event collisions so we can unmap re-used events...
	//
	int returnIndex = m_pcdHead[nRow].nIdx;
	ControlData cd;
	for (int i = 0; i < m_nCount; i++)
	{
		if (m_pcdHead[i].cd_mouse_valid && m_pcdHead[i].nMouse == newMouse)
		{
			if (i != nRow)
			{
				returnIndex = i; //m_pcdHead[i]->nIdx;
				m_pcdHead[i].nMouse = 0;
				gosASSERT(m_pcdHead[i].cd_mouse_valid);
				m_pcdHead[i].cd_mouse_valid = false;
				m_pcdHead[i].cd_mouse.element = 0;
			}
			break;
		}
	}
	cd = m_pcdHead[nRow].cd_key;
	cd.group = ControlsManager::MouseButtonGroupID;
	//  Init properties that change from old settings
	cd.element = newMouse;
	cd.mesg_id = m_pcdHead[nRow].mesg_id;
	cd.nameresid = m_pcdHead[nRow].nameresid;

	m_pcdHead[nRow].nMouse = newMouse;
	m_pcdHead[nRow].cd_mouse = cd;
	m_pcdHead[nRow].cd_mouse_valid = true;

	SetList(owner);
	return returnIndex;
}



CControlMappingList::ControlDef::ControlDef()
{
	mesg_id = 0;
	nameresid = 0;
	control_type = 0;
	control_mask = 0;
	nKey = 0;
	nMouse = 0;
	nJoy = 0;
	nIdx = 0;
	cd_mouse_valid = false;
	cd_joy_valid = false;
	cd_key_valid = false;
}

CControlMappingList::ControlDef::~ControlDef()
{
}

static char buffer[200];



const char* CControlMappingList::ControlDef::KeyName() const
{
	const int MyCtrl = 1;
	const int MyShift = 2;
	const int MyAlt = 4;

	buffer[0] = 0;
	if (nKey == 0)
		return buffer;

	int flag = 0;
	switch(control_mask)
	{
		case ControlsManager::AlwaysActive:
		case ControlsManager::PlainFlag:
			break;

		case ControlsManager::ShiftFlag:
			flag = MyShift;
			break;

		case ControlsManager::CtrlFlag:
			flag = MyCtrl;
			break;

		case ControlsManager::AltFlag:
			flag = MyAlt;
			break;

		case ControlsManager::CtrlShiftFlag:
			flag = MyCtrl | MyShift;
			break;

		case ControlsManager::ShiftAltFlag:
			flag = MyAlt | MyShift;
			break;

		case ControlsManager::CtrlAltFlag:
			flag = MyAlt | MyCtrl;
			break;

		case ControlsManager::CtrlShiftAltFlag:
			flag = MyShift | MyCtrl | MyAlt;
			break;

		default:
			gosASSERT(!"Unknown control data type");
			break;
	}

	//  Now composite each modifier present into one long string
	buffer[0] = 0;
	if (flag & MyShift)
	{
		strcpy(buffer, gos_DescribeKey(KEY_LSHIFT << 8));
		strcat(buffer, " ");
	}

	if (flag & MyCtrl)
	{
		strcat(buffer, gos_DescribeKey(KEY_LCONTROL << 8));
		strcat(buffer, " ");
	}

	if (flag & MyAlt)
	{
		strcat(buffer, gos_DescribeKey(KEY_LMENU << 8));
		strcat(buffer, " ");
	}

	strcat(buffer, gos_DescribeKey(nKey << 8));

	return buffer;
}

const char* CControlMappingList::ControlDef::JoyName() const
{
	char buffer2[200];
	buffer2[0] = 0;
	buffer[0] = 0;

	if (cd_joy.control_mask == ControlsManager::ShiftFlag)
	{
		strcpy(buffer, gos_DescribeKey(KEY_LSHIFT << 8));
		strcat(buffer, " ");
	}

	if (cd_joy_valid && nJoy < 10)
		sprintf(buffer2, "%d", nJoy + 1);
	else if (cd_joy_valid) 
		strcat(buffer, Application::GetInstance()->GetLocString(IDS_CONTROL_HATLEFT+nJoy-10));
	else
		buffer2[0] = 0;

	strcat(buffer, buffer2);

	return buffer;
}

const char* CControlMappingList::ControlDef::MouseName() const
{
	if (cd_mouse_valid)
		sprintf(buffer, "%d", nMouse + 1);
	else
		buffer[0] = 0;
	return buffer;
}

void CControlMappingList::InitializeThrottleConstants()
{
	bool bNeedToSave = false;
	NotationFile startup_ini("options.ini", NotationFile::Standard, true);
	Stuff::Page *joypage = startup_ini.FindPage("joystick");
	if (!joypage)
	{
		joypage = startup_ini.AddPage("joystick");
		bNeedToSave = true;
	}

	Stuff::Scalar temp;
	if (!joypage->GetEntry("BiThrottleCenter", &temp)) {
		joypage->AppendEntry("BiThrottleCenter", 0.30f);
		bNeedToSave = true;
	}

	if (!joypage->GetEntry("BiThrottleLow", &temp)) {
		joypage->AppendEntry("BiThrottleLow", 0.25f);
		bNeedToSave = true;
	}

	if (!joypage->GetEntry("BiThrottleHigh", &temp)) {
		joypage->AppendEntry("BiThrottleHigh", 0.06f);
		bNeedToSave = true;
	}

	if (!joypage->GetEntry("OneThrottleCenter", &temp)) {
		joypage->AppendEntry("OneThrottleCenter", 0.30f);
		bNeedToSave = true;
	}

	if (!joypage->GetEntry("OneThrottleHigh", &temp)) {
		joypage->AppendEntry("OneThrottleHigh", 0.06f);
		bNeedToSave = true;
	}

	if (bNeedToSave)
		startup_ini.Save();

	joypage->GetEntry("BiThrottleCenter", &m_joystick_throttle_center);
	joypage->GetEntry("BiThrottleLow", &m_joystick_throttle_dead_low);
	joypage->GetEntry("BiThrottleHigh", &m_joystick_throttle_dead_high);
}
