#include <vcclr.h>
#include "orbitersdk.h"
#include "Launcher.h"

using namespace Orbiter::Interfaces;
using namespace Orbiter::Launcher;
using namespace System;
using namespace System::IO;
using namespace System::Reflection;

#pragma unmanaged

HINSTANCE dllHandle;

void WINAPI clrInitialize(DWORD sd)
{
	InitModule(dllHandle);
}

BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,
  DWORD fdwReason,
  LPVOID lpvReserved
)
{
	DWORD threadID;
	switch(fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		dllHandle=hinstDLL;		
		CreateThread(0, 0, (LPTHREAD_START_ROUTINE) clrInitialize, 0, 0, &threadID);
		break;
	}
	return TRUE;
}

#pragma managed

gcroot<Launcher^> launcher=gcnew Launcher();

DLLCLBK void InitModule (HINSTANCE hDLL)
{
	//Just a dummy to start CLR	
}

DLLCLBK void ExitModule (HINSTANCE hDLL)
{
	//Actually never called due to current single-AppDomain solution
	launcher->Module->ExitModule((IntPtr)hDLL);
	launcher=nullptr;
}

DLLCLBK VESSEL *ovcInit (OBJHANDLE hVessel,int flightmodel)
{
	return (VESSEL *)(VESSEL2 *)(void *)(launcher->VesselModule->InitVessel((IntPtr)hVessel, flightmodel));
}

DLLCLBK void ovcExit (VESSEL *vessel)
{
	launcher->VesselModule->ExitVessel((IntPtr)vessel);
}

DLLCLBK void opcOpenRenderViewport (HWND renderWnd,DWORD width,DWORD height,bool fullscreen)
{
	launcher->PluginModule->OpenRenderViewport((IntPtr)renderWnd,width,height,fullscreen);		
}
 
DLLCLBK void opcCloseRenderViewport ()
{
	launcher->PluginModule->CloseRenderViewport();
}
 
DLLCLBK void opcPreStep (double SimT,double SimDT,double mjd)
{
	launcher->PluginModule->PreStep(SimT,SimDT,mjd);
}
 
DLLCLBK void opcPostStep (double SimT,double SimDT,double mjd)
{
	launcher->PluginModule->PostStep(SimT,SimDT,mjd);
}
 
DLLCLBK void opcFocusChanged (OBJHANDLE new_focus,OBJHANDLE old_focus)
{
	launcher->PluginModule->FocusChanged((IntPtr)new_focus,(IntPtr)old_focus);
}
 
DLLCLBK void opcTimeAccChanged (double nWarp,double oWarp)
{
	launcher->PluginModule->TimeAccelerationChanged(nWarp,oWarp);
}
 
DLLCLBK void opcPause (bool pause)
{
	launcher->PluginModule->Pause(pause);
}

DLLCLBK CELBODY *InitInstance (OBJHANDLE hBody)
{
	return (CELBODY *)(void *)launcher->PlanetModule->InitInstance((IntPtr)hBody);
}

DLLCLBK void ExitInstance (CELBODY *body)
{
	launcher->PlanetModule->ExitInstance((IntPtr)body);
}

namespace Orbiter
{
	namespace Launcher
	{
		Launcher::Launcher()
		{
			Assembly^ launcher=Assembly::GetExecutingAssembly();
			String^ name=Path::GetFileName(launcher->Location);
			String^ dir=Path::GetDirectoryName(launcher->Location);
			Assembly^ assm=Assembly::LoadFrom(Path::Combine(Path::Combine(dir, "DotNET"), name));
			Type^ vtype=Orbiter::Interfaces::Module::typeid;			
			if (assm!=nullptr)
			{
				for each (Type^ t in assm->GetTypes())
				{
					if (vtype->IsAssignableFrom(t))
					{
						module=(Orbiter::Interfaces::Module^)assm->CreateInstance(t->FullName);
						break;
					}
				}
			}
			module->InitModule((IntPtr)(void *)dllHandle);
			vesselModule=dynamic_cast<Orbiter::Interfaces::VesselModule^>(module);
			if (vesselModule==nullptr)
				vesselModule=gcnew Orbiter::Interfaces::Templates::BaseVesselModule();
			pluginModule=dynamic_cast<Orbiter::Interfaces::PluginModule^>(module);
			if (pluginModule==nullptr)
				pluginModule=gcnew Orbiter::Interfaces::Templates::BasePluginModule();
			planetModule=dynamic_cast<Orbiter::Interfaces::PlanetModule^>(module);
			if (planetModule==nullptr)
				planetModule=gcnew Orbiter::Interfaces::Templates::BasePlanetModule();
		}

		Orbiter::Interfaces::VesselModule^ Launcher::VesselModule::get(void){return vesselModule;}
		Orbiter::Interfaces::PluginModule^ Launcher::PluginModule::get(void){return pluginModule;}
		Orbiter::Interfaces::PlanetModule^ Launcher::PlanetModule::get(void){return planetModule;}
		Orbiter::Interfaces::Module^ Launcher::Module::get(void){return module;}
	}
}