// Copyright (C) 2003-2008 Dolphin Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// 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 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/

#include "pluginspecs_wiimote.h"

#include "wiiuse.h"
#include <queue>

#include "common.h"
#include "thread.h"

#include "wiimote_hid.h"
#include "EmuMain.h"


extern SWiimoteInitialize g_WiimoteInitialize;
//extern void __Log(int log, const char *format, ...);
//extern void __Log(int log, int v, const char *format, ...);


namespace WiiMoteReal
{
#define MAX_WIIMOTES 1

	//******************************************************************************
	// Forwording
	//******************************************************************************
	
	class CWiiMote;
	DWORD WINAPI ReadWiimote_ThreadFunc(void* arg);

	//******************************************************************************
	// Variable declarations
	//******************************************************************************
		
	wiimote_t**			g_WiiMotesFromWiiUse = NULL;
	Common::Thread*		g_pReadThread = NULL;
	int					g_NumberOfWiiMotes;
	CWiiMote*			g_WiiMotes[MAX_WIIMOTES];	
	bool				g_Shutdown = false;

	//******************************************************************************
	// Prolly this class should be in its own file
	//******************************************************************************

	class CWiiMote
	{
	public:

		CWiiMote(u8 _WiimoteNumber, wiimote_t* _pWiimote) 
			: m_WiimoteNumber(_WiimoteNumber)
			, m_pWiiMote(_pWiimote)
			, m_LastReportValid(false)
			, m_channelID(0)
			, m_pCriticalSection(NULL)
		{
			m_pCriticalSection = new Common::CriticalSection();

			wiiuse_set_leds(m_pWiiMote, WIIMOTE_LED_4);

#ifdef _WIN32
			// F|RES: i dunno if we really need this
			CancelIo(m_pWiiMote->dev_handle);
#endif
		}

		virtual ~CWiiMote() 
		{
			delete m_pCriticalSection;
		};

		// send raw HID data from the core to wiimote
		void SendData(u16 _channelID, const u8* _pData, u32 _Size)
		{
			m_channelID = _channelID;

			m_pCriticalSection->Enter();
			{
				SEvent WriteEvent;
				memcpy(WriteEvent.m_PayLoad, _pData+1, _Size-1);
				m_EventWriteQueue.push(WriteEvent);
			}
			m_pCriticalSection->Leave();
		}

		// read data from wiimote (but don't send it to the core, just filter and queue)
		void ReadData() 
		{
			m_pCriticalSection->Enter();

			if (!m_EventWriteQueue.empty())
			{
				SEvent& rEvent = m_EventWriteQueue.front();
				wiiuse_io_write(m_pWiiMote, (byte*)rEvent.m_PayLoad, MAX_PAYLOAD);
				m_EventWriteQueue.pop();
			}

			m_pCriticalSection->Leave();

			if (wiiuse_io_read(m_pWiiMote))
			{
				const byte* pBuffer = m_pWiiMote->event_buf;

				// check if we have a channel (connection) if so save the data...
				if (m_channelID > 0)
				{
					m_pCriticalSection->Enter();

					// filter out reports
					if (pBuffer[0] >= 0x30) 
					{
						memcpy(m_LastReport.m_PayLoad, pBuffer, MAX_PAYLOAD);
						m_LastReportValid = true;
					}
					else
					{
						SEvent ImportantEvent;
						memcpy(ImportantEvent.m_PayLoad, pBuffer, MAX_PAYLOAD);
						m_EventReadQueue.push(ImportantEvent);
					}

					m_pCriticalSection->Leave();
				}
			}
		};

		// send queued data to the core
		void Update() 
		{
			m_pCriticalSection->Enter();

			if (m_EventReadQueue.empty())
			{
				if (m_LastReportValid)
					SendEvent(m_LastReport);
			}
			else
			{
				SendEvent(m_EventReadQueue.front());
				m_EventReadQueue.pop();
			}

			m_pCriticalSection->Leave();
		};

	private:

		struct SEvent 
		{
			SEvent()
			{
				memset(m_PayLoad, 0, MAX_PAYLOAD);
			}
			byte m_PayLoad[MAX_PAYLOAD];
		};
		typedef std::queue<SEvent> CEventQueue;

		u8 m_WiimoteNumber; // just for debugging
		u16 m_channelID;
		wiimote_t* m_pWiiMote;

		Common::CriticalSection* m_pCriticalSection;
		CEventQueue m_EventReadQueue;
		CEventQueue m_EventWriteQueue;
		bool m_LastReportValid;
		SEvent m_LastReport;

		void SendEvent(SEvent& _rEvent)
		{
			// we don't have an answer channel
			if (m_channelID == 0)
				return;

			// check event buffer;
			u8 Buffer[1024];
			u32 Offset = 0;
			hid_packet* pHidHeader = (hid_packet*)(Buffer + Offset);
			Offset += sizeof(hid_packet);
			pHidHeader->type = HID_TYPE_DATA;
			pHidHeader->param = HID_PARAM_INPUT;

			memcpy(&Buffer[Offset], _rEvent.m_PayLoad, MAX_PAYLOAD);
			Offset += MAX_PAYLOAD;

			g_WiimoteInitialize.pWiimoteInput(m_channelID, Buffer, Offset);
		}
	};

	//******************************************************************************
	// Function Definitions 
	//******************************************************************************

	int Initialize()
	{
		memset(g_WiiMotes, 0, sizeof(CWiiMote*) * MAX_WIIMOTES);
		g_WiiMotesFromWiiUse = wiiuse_init(MAX_WIIMOTES);
		g_NumberOfWiiMotes = wiiuse_find(g_WiiMotesFromWiiUse, MAX_WIIMOTES, 5);

		for (int i=0; i<g_NumberOfWiiMotes; i++)
		{
			g_WiiMotes[i] = new CWiiMote(i+1, g_WiiMotesFromWiiUse[i]); 
		}

		if (g_NumberOfWiiMotes == 0)
			return 0;

		g_pReadThread = new Common::Thread(ReadWiimote_ThreadFunc, NULL);

		return true;
	}

	void DoState(void* ptr, int mode)
	{}

	void Shutdown(void)
	{
		g_Shutdown = true;

		// stop the thread
		if (g_pReadThread != NULL)
		{
			g_pReadThread->WaitForDeath();
			delete g_pReadThread;
			g_pReadThread = NULL;
		}

		// delete the wiimotes
		for (int i=0; i<g_NumberOfWiiMotes; i++)
		{
			delete g_WiiMotes[i];
			g_WiiMotes[i] = NULL;
		}

		// clean up wiiuse
		wiiuse_cleanup(g_WiiMotesFromWiiUse, g_NumberOfWiiMotes);
	}

	void InterruptChannel(u16 _channelID, const void* _pData, u32 _Size)
	{
		g_WiiMotes[0]->SendData(_channelID, (const u8*)_pData, _Size);
	}

	void ControlChannel(u16 _channelID, const void* _pData, u32 _Size)
	{
		g_WiiMotes[0]->SendData(_channelID, (const u8*)_pData, _Size);
	}
	
	void Update()
	{
		for (int i=0; i<g_NumberOfWiiMotes; i++)
		{
			g_WiiMotes[i]->Update();
		}
	}

	DWORD WINAPI ReadWiimote_ThreadFunc(void* arg)
	{
		while (!g_Shutdown)
		{
			for (int i=0; i<g_NumberOfWiiMotes; i++)
			{
				g_WiiMotes[i]->ReadData();
			}
		}
		return 0;
	}

}; // end of namespace

