#include "stdafx.h"

#include "DLLInterface.h"

#include "General/AtlasEventLoop.h"

#include "General/Datafile.h"
#include "ActorEditor/ActorEditor.h"
#include "ColourTester/ColourTester.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "FileConverter/FileConverter.h"
#include "ArchiveViewer/ArchiveViewer.h"

#include "GameInterface/MessagePasser.h"

#include "wx/config.h"
#include "wx/debugrpt.h"
#include "wx/file.h"

// Shared memory allocation functions
ATLASDLLIMPEXP void* ShareableMalloc(size_t n)
{
	// TODO: make sure this is thread-safe everywhere. (It is in MSVC with the
	// multithreaded CRT.)
	return malloc(n);
}
ATLASDLLIMPEXP void ShareableFree(void* p)
{
	return free(p);
}
// Define the function pointers that we'll use when calling those functions.
// (The game loads the addresses of the above functions, then does the same.)
namespace AtlasMessage
{
	void* (*ShareableMallocFptr) (size_t) = &ShareableMalloc;
	void (*ShareableFreeFptr) (void*) = &ShareableFree;
}


// Global variables, to remember state between DllMain and StartWindow and OnInit
wxString g_InitialWindowType;
HINSTANCE g_Module;
bool g_IsLoaded = false;


BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID WXUNUSED(lpReserved))
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		g_Module = hModule;
		return TRUE;

	case DLL_PROCESS_DETACH:
		if (g_IsLoaded)
		{
			wxEntryCleanup();
			g_IsLoaded = false;
		}
		break;
	}

	return TRUE;
}

using namespace AtlasMessage;

MessagePasser* AtlasMessage::g_MessagePasser = NULL;

ATLASDLLIMPEXP void Atlas_SetMessagePasser(MessagePasser* passer)
{
	g_MessagePasser = passer;
}

ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type)
{
	g_InitialWindowType = type;
	wxEntry(g_Module);
}


class wxDLLApp : public wxApp
{
public:
	virtual bool OnInit()
	{
		if (! wxIsDebuggerRunning())
			wxHandleFatalExceptions();

		// Initialise the global config file
		wxConfigBase::Set(new wxConfig(_T("Atlas Editor"), _T("Wildfire Games")));

		// Assume that the .exe is located in .../binaries/system. (We can't
		// just use the cwd, since that isn't correct when being executed by
		// dragging-and-dropping onto the program in Explorer.)
		Datafile::SetSystemDirectory(argv[0]);

		// Display the appropriate window
		wxFrame* frame;
#define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else
		MAYBE(ActorEditor)
		MAYBE(ColourTester)
		MAYBE(ScenarioEditor)
		MAYBE(FileConverter)
		MAYBE(ArchiveViewer)
#undef MAYBE
		// else
		{
			wxFAIL_MSG(_("Internal error: invalid window type"));
			return false;
		}

		frame->Show();
		SetTopWindow(frame);

		AtlasWindow* win = wxDynamicCast(frame, AtlasWindow);
		if (win)
		{
			// One argument => argv[1] is probably a filename to open
			if (argc > 1)
			{
				wxChar* filename = argv[1];

				if (filename[0] != _T('-')) // ignore -options
				{
					if (wxFile::Exists(filename))
					{
						win->OpenFile(filename);
					}
					else
						wxLogError(_("Cannot find file '%s'"), filename);
				}
			}
		}

		return true;
	}


	bool OpenDirectory(const wxString& dir)
	{
		// Code largely copied from wxLaunchDefaultBrowser:
		// (TODO: portability)

		typedef HINSTANCE (WINAPI *LPShellExecute)(HWND hwnd, const wxChar* lpOperation,
			const wxChar* lpFile,
			const wxChar* lpParameters,
			const wxChar* lpDirectory,
			INT nShowCmd);

		HINSTANCE hShellDll = ::LoadLibrary(_T("shell32.dll"));
		if (hShellDll == NULL)
			return false;

		LPShellExecute lpShellExecute =
			(LPShellExecute) ::GetProcAddress(hShellDll,
			wxString(_T("ShellExecute")
#ifdef _UNICODE
			  _T("W")
#else
			  _T("A")
#endif
			).mb_str(wxConvLocal));

		if (lpShellExecute == NULL)
			return false;

		/*HINSTANCE nResult =*/ (*lpShellExecute)(NULL, _T("explore"), dir.c_str(), NULL, NULL, SW_SHOWNORMAL);
			// ignore return value, since we're not going to do anything if this fails

		::FreeLibrary(hShellDll);

		return true;
	}

	virtual void OnFatalException()
	{
		wxDebugReport report;
		wxDebugReportPreviewStd preview;

		report.AddAll();

		if (preview.Show(report))
		{
			wxString dir = report.GetDirectory(); // save the string, since it gets cleared by Process
			report.Process();
			OpenDirectory(dir);
		}
	}

	virtual int MainLoop()
	{
		// Override the default MainLoop so that we can provide our own event loop

		wxEventLoop* old = m_mainLoop;
		m_mainLoop = new AtlasEventLoop;

		int ret = m_mainLoop->Run();

		delete m_mainLoop;
		m_mainLoop = old;
		return ret;
	}
};

IMPLEMENT_APP_NO_MAIN(wxDLLApp)
