
#include "Common.h"
#include "pluginspecs_wiimote.h"

#include "ControllerInterface/ControllerInterface.h"
#include "WiimoteEmu/WiimoteEmu.h"

#if defined(HAVE_WX) && HAVE_WX
#include "ConfigDiag.h"
#endif
#include "../../InputPluginCommon/Src/Config.h"

#if defined(HAVE_X11) && HAVE_X11
#include <X11/Xlib.h>
#if defined(HAVE_WX) && HAVE_WX
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#endif
#endif

#define PLUGIN_VERSION		0x0100

#define PLUGIN_NAME		"Dolphin Wiimote New Incomplete"
#ifdef DEBUGFAST
#define PLUGIN_FULL_NAME	PLUGIN_NAME" (DebugFast)"
#else
#ifdef _DEBUG
#define PLUGIN_FULL_NAME	PLUGIN_NAME" (Debug)"
#else
#define PLUGIN_FULL_NAME	PLUGIN_NAME
#endif
#endif

// plugin globals
static Plugin g_plugin( "WiimoteNew", "Wiimote", "Wiimote" );
SWiimoteInitialize g_WiimoteInitialize;

#ifdef _WIN32
class wxDLLApp : public wxApp
{
	bool OnInit()
	{
		return true;
	};
};
IMPLEMENT_APP_NO_MAIN(wxDLLApp)
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
#endif

// copied from GCPad
HINSTANCE g_hInstance;

// copied from GCPad
#if defined(HAVE_WX) && HAVE_WX
wxWindow* GetParentedWxWindow(HWND Parent)
{
#ifdef _WIN32
	wxSetInstance((HINSTANCE)g_hInstance);
#endif
	wxWindow *win = new wxWindow();
#ifdef _WIN32
	win->SetHWND((WXHWND)Parent);
	win->AdoptAttributesFromHWND();
#endif
	return win;
}
#endif
// /

#ifdef _WIN32
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		wxSetInstance(hinstDLL);
		wxInitialize();
		break; 
	case DLL_PROCESS_DETACH:
		wxUninitialize();
		break;
	default:
		break;
	}

	g_hInstance = hinstDLL;
	return TRUE;
}
#endif

void DeInitPlugin()
{
	if ( g_plugin.controller_interface.IsInit() )
	{
		std::vector<ControllerEmu*>::const_iterator
			i = g_plugin.controllers.begin(),
			e = g_plugin.controllers.end();
		for ( ; i!=e; ++i )
			delete *i;
		g_plugin.controllers.clear();

		g_plugin.controller_interface.DeInit();
	}
}

// if plugin isn't initialized, init and load config
void InitPlugin( void* const hwnd )
{
	if ( false == g_plugin.controller_interface.IsInit() )
	{
		// add 4 wiimotes
		for ( unsigned int i = 0; i<4; ++i )
			g_plugin.controllers.push_back( new WiimoteEmu::Wiimote(i) );

		// load the saved controller config
		g_plugin.LoadConfig();
		
		// needed for Xlib and exclusive dinput
		g_plugin.controller_interface.SetHwnd( hwnd );
		g_plugin.controller_interface.Init();

		// update control refs
		std::vector<ControllerEmu*>::const_iterator i = g_plugin.controllers.begin(),
			e = g_plugin.controllers.end();
		for ( ; i!=e; ++i )
			(*i)->UpdateReferences( g_plugin.controller_interface );

	}
}

// I N T E R F A C E 

// __________________________________________________________________________________________________
// Function: Wiimote_Output
// Purpose:  An L2CAP packet is passed from the Core to the Wiimote,
//           on the HID CONTROL channel.
// input:    Da pakket.
// output:   none
//
void Wiimote_ControlChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
	//PanicAlert( "Wiimote_ControlChannel" );
	((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->ControlChannel( _channelID, _pData, _Size );
}

// __________________________________________________________________________________________________
// Function: Wiimote_Input
// Purpose:  An L2CAP packet is passed from the Core to the Wiimote,
//           on the HID INTERRUPT channel.
// input:    Da pakket.
// output:   none
//
void Wiimote_InterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
	//PanicAlert( "Wiimote_InterruptChannel" );
	((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->InterruptChannel( _channelID, _pData, _Size );
}

// __________________________________________________________________________________________________
// Function: Wiimote_Update
// Purpose:  This function is called periodically by the Core.
// input:    none
// output:   none
//
void Wiimote_Update(int _number)
{
	//PanicAlert( "Wiimote_Update" );

	// TODO: change this to a TryEnter, and make it give empty input on failure
	g_plugin.controls_crit.Enter();

	static int _last_number = 4;
	if ( _number <= _last_number && g_plugin.interface_crit.TryEnter() )
	{
		g_plugin.controller_interface.UpdateOutput();
		g_plugin.controller_interface.UpdateInput();
		g_plugin.interface_crit.Leave();
	}
	_last_number = _number;

	((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->Update();

	g_plugin.controls_crit.Leave();
}

// __________________________________________________________________________________________________
// Function: PAD_GetAttachedPads
// Purpose:  Get mask of attached pads (eg: controller 1 & 4 -> 0x9)
// input:	 none
// output:   number of pads
//
unsigned int Wiimote_GetAttachedControllers()
{
	unsigned int attached = 0;
	for (unsigned int i=0; i<4; ++i)
		if (g_plugin.controllers[i]->default_device.ToString().length())
			attached |= (1 << i);
	return attached;
}

// GLOBAL I N T E R F A C E 
// Function: GetDllInfo
// Purpose:  This function allows the emulator to gather information
//           about the DLL by filling in the PluginInfo structure.
// input:    A pointer to a PLUGIN_INFO structure that needs to be
//           filled by the function. (see def above)
// output:   none
//
void GetDllInfo(PLUGIN_INFO* _pPluginInfo)
{
	// don't feel like messing around with all those strcpy functions and warnings
	//char *s1 = CIFACE_PLUGIN_FULL_NAME, *s2 = _pPluginInfo->Name;
	//while ( *s2++ = *s1++ );
	memcpy( _pPluginInfo->Name, PLUGIN_FULL_NAME, sizeof(PLUGIN_FULL_NAME) );
	_pPluginInfo->Type = PLUGIN_TYPE_WIIMOTE;
	_pPluginInfo->Version = PLUGIN_VERSION;
}

// ___________________________________________________________________________
// Function: DllConfig
// Purpose:  This function is optional function that is provided
//           to allow the user to configure the DLL
// input:    A handle to the window that calls this function
// output:   none
//
void DllConfig(HWND _hParent)
{
#if defined(HAVE_WX) && HAVE_WX
	bool was_init = false;

	if ( g_plugin.controller_interface.IsInit() )	// hack for showing dialog when game isnt running
		was_init = true;
	else
	{
#if defined(HAVE_X11) && HAVE_X11
		Window win = GDK_WINDOW_XID(GTK_WIDGET(_hParent)->window);
		g_WiimoteInitialize.hWnd = GDK_WINDOW_XDISPLAY(GTK_WIDGET(_hParent)->window);
		g_WiimoteInitialize.pXWindow = &win;
		InitPlugin(g_WiimoteInitialize.hWnd);
#else
		InitPlugin(_hParent);
#endif
	}

	// copied from GCPad
	wxWindow *frame = GetParentedWxWindow(_hParent);
	ConfigDialog* m_ConfigFrame = new ConfigDialog( frame, g_plugin, PLUGIN_FULL_NAME, was_init );

#ifdef _WIN32
	frame->Disable();
	m_ConfigFrame->ShowModal();
	frame->Enable();
#else
	m_ConfigFrame->ShowModal();
#endif

#ifdef _WIN32
	wxMilliSleep( 50 );	// hooray for hacks
	frame->SetFocus();
	frame->SetHWND(NULL);
#endif

	m_ConfigFrame->Destroy();
	m_ConfigFrame = NULL;
	frame->Destroy();
	// /

	if ( false == was_init )				// hack for showing dialog when game isnt running
		DeInitPlugin();
#endif
}

// ___________________________________________________________________________
// Function: DllDebugger
// Purpose:  Open the debugger
// input:    a handle to the window that calls this function
// output:   none
//
void DllDebugger(HWND _hParent, bool Show)
{
	// wut?
}

// ___________________________________________________________________________
// Function: DllSetGlobals
// Purpose:  Set the pointer for globals variables
// input:    a pointer to the global struct
// output:   none
//
void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals)
{
	// wut?
}

// ___________________________________________________________________________
// Function: Initialize
// Purpose: Initialize the plugin
// input:    Init
// output:   none
//
void Initialize(void *init)
{
	g_WiimoteInitialize = *(SWiimoteInitialize*)init;
	if ( false == g_plugin.controller_interface.IsInit() )
		InitPlugin( g_WiimoteInitialize.hWnd );
}

// ___________________________________________________________________________
// Function: Shutdown
// Purpose:  This function is called when the emulator is shutting down
//           a game allowing the dll to de-initialise.
// input:    none
// output:   none
//
void Shutdown(void)
{
	if ( g_plugin.controller_interface.IsInit() )
		DeInitPlugin();
}

// ___________________________________________________________________________
// Function: DoState
// Purpose:  Saves/load state
// input/output: ptr
// input: mode
//
void DoState(unsigned char **ptr, int mode)
{
	PointerWrap p(ptr, mode);
	for (unsigned int i=0; i<4; ++i)
		((WiimoteEmu::Wiimote*)g_plugin.controllers[i])->DoState(p);
}

// ___________________________________________________________________________
// Function: EmuStateChange
// Purpose: Notifies the plugin of a change in emulation state
// input:    newState
// output:   none
//
void EmuStateChange(PLUGIN_EMUSTATE newState)
{
	// maybe use this later
}
