// Copyright (C) 2002-2006 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

// This is the main DLL file.

#pragma unmanaged
#include "..\\..\\include\\irrlicht.h"

#ifdef _DEBUG
#pragma comment (lib, "..\\debug\\irrlicht.lib")
#else
#pragma comment (lib, "..\\..\\lib\\Win32-visualstudio\\irrlicht.lib")
#endif
#pragma managed

#include <windows.h>

#include "Irrlicht.NET.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "ICursorControl.h"
#include "IFileSystem.h"
#include "IGUIEnvironment.h"
#include "ITimer.h"
#include "NativeConverter.h"
#include <vcclr.h> // for PtrToStringChars
#include "IGUIListBox.h"

namespace Irrlicht
{

IrrlichtDevice::IrrlichtDevice(Video::DriverType driverType, Core::Dimension2D windowSize, 
	int bits, bool fullScreen, bool stencilBuffer, bool vsync)
: Device(0), ManagedVideoDriver(0), ManagedSceneManager(0), ManagedCursorControl(0),
 ManagedEventReceiver(0)
{
	CreatedDriverType = (irr::video::E_DRIVER_TYPE)driverType;
	Device = irr::createDevice(CreatedDriverType,
		irr::NativeConverter::getNativeDim(windowSize), bits, fullScreen, stencilBuffer, vsync);
   createManagedStuff();
}

IrrlichtDevice::IrrlichtDevice(Video::DriverType driverType)
: Device(0), ManagedVideoDriver(0), ManagedSceneManager(0), ManagedCursorControl(0)
{
	CreatedDriverType = (irr::video::E_DRIVER_TYPE)driverType;
	Device = irr::createDevice(CreatedDriverType);
	createManagedStuff();
}

IrrlichtDevice::IrrlichtDevice(Video::DriverType driverType, Core::Dimension2D windowSize, 
			int bits, bool fullScreen, bool stencilBuffer, bool vsync, bool antiAlias,
			System::IntPtr windowHandle)
: Device(0), ManagedVideoDriver(0), ManagedSceneManager(0), ManagedCursorControl(0)
{
	irr::SIrrlichtCreationParameters p;
	CreatedDriverType = (irr::video::E_DRIVER_TYPE)driverType;

	p.AntiAlias = antiAlias;
	p.Bits = bits;
	p.DriverType = CreatedDriverType;
	p.Fullscreen = fullScreen;
	p.Stencilbuffer = stencilBuffer;
	p.Vsync = vsync;
	p.WindowId = reinterpret_cast<irr::s32>((void*)windowHandle);
	p.WindowSize = irr::NativeConverter::getNativeDim(windowSize);

	Device = irr::createDeviceEx(p);
	createManagedStuff();
}


void IrrlichtDevice::createManagedStuff()
{
	if (!Device)
		throw new System::Exception(new System::String(
		"Irrlicht device could not be created with the parameters you specified."));

	Device->getLogger()->log("Irrlicht.NET running.");

	ManagedVideoDriver = new Video::IVideoDriver(Device->getVideoDriver());
	ManagedSceneManager = new Scene::ISceneManager(Device->getSceneManager());
	ManagedCursorControl = new GUI::ICursorControl(Device->getCursorControl());
	ManagedFileSystem = new IO::IFileSystem(Device->getFileSystem());
	ManagedGUIEnvironment = new GUI::IGUIEnvironment(Device->getGUIEnvironment());
	ManagedTimer = new ITimer(Device->getTimer());
}



//! Destructor
IrrlichtDevice::~IrrlichtDevice()
{
	try
	{
		System::GC::SuppressFinalize(this);

		set_EventReceiver(0);

		if (Device)
		{
			Device->drop();
			Device = 0;
		}
	}
	catch(Exception*)
	{
		// catch this exception if we created the device for OpenGL mode,
		// .net crashed here for some unknown reason (I don't want to know why, really..)
		if ( CreatedDriverType != irr::video::EDT_OPENGL )
			throw;
		else
			System::Diagnostics::Debug::WriteLine("Catched OpenGL shutdown exception");
	}
}

bool IrrlichtDevice::Run()
{
	return Device->run();
}

bool IrrlichtDevice::get_WindowActive()
{
	return Device->isWindowActive();
}

void IrrlichtDevice::CloseDevice()
{
	Device->closeDevice();
	Device->run(); // eat WM_QUIT
}

System::String* IrrlichtDevice::get_Version()
{
	return new System::String(Device->getVersion());
}

void IrrlichtDevice::set_ResizeAble(bool resize)
{
	return Device->setResizeAble(resize);
}

void IrrlichtDevice::set_WindowCaption(System::String* text)
{
	const wchar_t __pin* pinchars = PtrToStringChars(text); 
	Device->setWindowCaption(pinchars);
}

Video::IVideoDriver* IrrlichtDevice::get_VideoDriver()
{
	return ManagedVideoDriver;
}

Scene::ISceneManager* IrrlichtDevice::get_SceneManager()
{
	return ManagedSceneManager;
}

GUI::ICursorControl* IrrlichtDevice::get_CursorControl()
{
	return ManagedCursorControl;
}

IO::IFileSystem* IrrlichtDevice::get_FileSystem()
{
	return ManagedFileSystem;
}

GUI::IGUIEnvironment* IrrlichtDevice::get_GUIEnvironment()
{
	return ManagedGUIEnvironment;
}

ITimer* IrrlichtDevice::get_Timer()
{
	return ManagedTimer;
}

void IrrlichtDevice::set_EventReceiver(IEventReceiver* receiver)
{
	if (EventMapList == 0)
		EventMapList = new System::Collections::ArrayList();
	else
	if (ManagedEventReceiver != 0)
	{
		// search and delete current EventReceiver from list

		for (int i=0; i<EventMapList->get_Count(); ++i)
		{
			SEventMapping* mapping = __try_cast<SEventMapping*>(EventMapList->get_Item(i));
			if (mapping->ManagedEventReceiver == ManagedEventReceiver)
			{
				delete mapping->NativeEventReceiver;
				EventMapList->RemoveAt(i);
				break;
			}
		}
		
	}

	if (receiver != 0)
	{
		SEventMapping* map = new SEventMapping();
		map->ManagedEventReceiver = receiver;
		map->NativeEventReceiver = new NativeEventReceiver();
		ManagedEventReceiver = receiver;

		EventMapList->Add(map);
		Device->setEventReceiver(map->NativeEventReceiver);
	}
	else
	{
		Device->setEventReceiver(0);
		ManagedEventReceiver = 0;
	}
}

bool IrrlichtDevice::NativeEventReceiver::OnEvent(irr::SEvent e)
{
	// find .NET receiver
	System::Collections::ArrayList* l = IrrlichtDevice::get_EventMap();
	if (!l)
		return false;

	// this is not as slow as it looks, because the list usually has
	// only one single entry.
	for (int i=0; i<l->get_Count(); ++i)
	{
		SEventMapping* mapping = __try_cast<SEventMapping*>(l->get_Item(i));
		if (mapping->NativeEventReceiver == this)
		{
			// found .NET receiver, now marshal event
			return mapping->ManagedEventReceiver->OnEvent(Irrlicht::Event(e));
		}
	}

	return false;
}



GUI::IGUIElement* Event::get_GUIEventCaller()
{
	if ( NativeEvent.GUIEvent.Caller == 0 || NativeEvent.EventType != irr::EET_GUI_EVENT )
		return 0;

	irr::gui::EGUI_ELEMENT_TYPE type = NativeEvent.GUIEvent.Caller->getType();

	if ( type == irr::gui::EGUIET_LIST_BOX )
		return new GUI::IGUIListBox( (irr::gui::IGUIListBox*)NativeEvent.GUIEvent.Caller );

	return new GUI::IGUIElement(NativeEvent.GUIEvent.Caller);
}


} // end namespace 