/***************************************************************************\
| Project: AE Installer														|
| By: Gumby & Iritscen														|
| File: AEInstallerApp.cpp													|
| Function: Sets up the main application window.							|
| Created: 07/05/2009 17:23:39												|
\***************************************************************************/

#include "boost/filesystem.hpp"
#include "boost/lexical_cast.hpp" // int -> string
#include "boost/thread.hpp"
#include "boost/thread/mutex.hpp"
#include <fstream>
#include <string>
#include "installer.h"
#include "aeinstallerapp.h"

////@begin includes
////@end includes

extern int updateStatus;
extern bool installerJustUpdated;
Install_info_cfg currentAE, updateAE;
MainWindow* TheWindow;

////@begin XPM images
////@end XPM images


/*
 * Application instance implementation
 */

////@begin implement app
IMPLEMENT_APP( AEInstallerApp )
////@end implement app


/*
 * AEInstallerApp type definition
 */

IMPLEMENT_CLASS( AEInstallerApp, wxApp )


/*
 * AEInstallerApp event table definition
 */

BEGIN_EVENT_TABLE( AEInstallerApp, wxApp )

////@begin AEInstallerApp event table entries
////@end AEInstallerApp event table entries

END_EVENT_TABLE()


/*
 * Constructor for AEInstallerApp
 */

AEInstallerApp::AEInstallerApp()
{
    Init();
}


/*
 * Member initialisation
 */

void AEInstallerApp::Init()
{
	////@begin AEInstallerApp member initialisation
	////@end AEInstallerApp member initialisation
}

/*
 * Initialisation for AEInstallerApp
 */

/* The OnInit() routine is used to check whether the Installer has the software *\
|  it needs to install mods, whether there is an available update, and whether	 |
\* the user has globalized yet, to allow mods to be installed.					*/
bool AEInstallerApp::OnInit()
{    
	////@begin AEInstallerApp initialisation
	// Remove the comment markers above and below this block
	// to make permanent changes to the code.
#if wxUSE_XPM
	wxImage::AddHandler(new wxXPMHandler);
#endif
#if wxUSE_LIBPNG
	wxImage::AddHandler(new wxPNGHandler);
#endif
#if wxUSE_LIBJPEG
	wxImage::AddHandler(new wxJPEGHandler);
#endif
#if wxUSE_GIF
	wxImage::AddHandler(new wxGIFHandler);
#endif
	MainWindow* mainWindow = new MainWindow( NULL );
	mainWindow->Show(true);
	////@end AEInstallerApp initialisation
	TheWindow = mainWindow;
	
	// Anything after this is done after the window appears...
	
	if (!CheckForRequiredSoftware())
	{
		TheWindow->Close(); // CheckForRequiredSoftware() will have notified the user of what they are missing, so we just quit now
		return true;
	}
	
	if (updateStatus) // updateStatus was set when MainWindow::CreateControls() was called during initialization of the window
	{
		string updateMsg = "An update for the Anniversary Edition is available.\n"
						   "Do you wish to update to Edition version " + updateAE.AEVersion + "?\n"
						   "(Current version is " + currentAE.AEVersion + ")\n"; // ...so we tack the rest on in a second command
		wxMessageDialog* updateNotification;
		
		switch (updateStatus) // for the meanings of these return values, see the comments preceding installer.cpp's GetUpdateStatus()
		{
			case UPDATE_LOG_READ_ERR:
			{
				if (exists("Update.log")) remove("Update.log");
				ofstream logfile("Update.log");
				logfile << "Error: A necessary .cfg file could not be read.";
			} // brackets are needed due to the initialization of the ofstream; silly C!
				break;
			case UPDATE_MNTH_REQD_ERR:
				updateMsg = "There is a patch in the updates/ folder, but it patches the\n";
				updateMsg = updateMsg + updateAE.AEVersion + " release; it cannot update this version of the Edition.";
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxOK | wxICON_EXCLAMATION, wxDefaultPosition);
				updateNotification->ShowModal();
				break;
			case UPDATE_SIMP_AVAIL: // there's an update with no globalization or Installer strings attached
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
				if (updateNotification->ShowModal() == wxID_YES)
					ProcessAEUpdate(&currentAE, &updateAE, &installerJustUpdated);
				break;
			case UPDATE_PKG_AVAIL: // there's an update with no globalization or Installer strings attached
				updateMsg = (string)"One or more individual package updates for the Anniversary Edition are available.\n\n" +
					(string)"Please note that the AE team assumes no responsibility for the content of third party mods, " +
					(string)"nor effects that a third party mod may have on your install.\n\n" +
					(string)"Do you wish to install these update(s)?";
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
				if (updateNotification->ShowModal() == wxID_YES)
					ProcessPackageUpdates("../updates", "./packages");
				break;
			case UPDATE_GLOB_AVAIL:	// there's an update with globalization strings attached
				updateMsg = updateMsg + "**Note that the update requires you to reglobalize, which will take 5-20 minutes.**\n" +
										"Before clicking Yes, MAKE SURE you have backed up any mods not installed through\n " +
										"the Installer, such as plug-ins or direct OniSplit imports.";
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
				if (updateNotification->ShowModal() == wxID_YES)
					ProcessAEUpdate(&currentAE, &updateAE, &installerJustUpdated);
				break;
			case UPDATE_INST_AVAIL: // there's an update with Installer strings attached (globalization is irrelevant while the Installer is not yet updated)
				updateMsg = updateMsg + "**Note that the update requires the Installer to update itself.**\n" +
										"If you click Yes, the Installer will quit and re-launch itself, then\n" +
										"you will be prompted to begin the installation.";
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
				if (updateNotification->ShowModal() == wxID_YES)
				{
					if (ProcessInstallerUpdate(&currentAE, &updateAE)) // there's an intentional logic gap here: if the user clicks "Yes"...
					{												   // ...and then ProcessInstallerUpdate has an error and returns false, the logic gap results...
						TheWindow->Close();							   // ...in the code continuing to execute down through case UPDATE_INST_REPL_ERR
						return true;
					}
				}
				else
					break;
			case UPDATE_INST_REPL_ERR: // the Installer replacement failed, user has to do it :-(
				updateMsg = "The Installer replacement process failed for some reason.\n";
				updateMsg = updateMsg + "In order for the update to continue, go into the folder Edition/updates/" + strEUFN + "/install/ and " + 
										"drag the Installer to Edition/install/, replacing the current Installer application, then launch the " +
										"new version. Click Yes to quit.";
				updateNotification = new wxMessageDialog(TheWindow, updateMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
				if (updateNotification->ShowModal() == wxID_YES)
					TheWindow->Close();
				return true;
		}
	}

	CheckForGlobalization(false); // function will prompt user and initiate globalization if not done already
	
	return true;
}

bool CheckForRequiredSoftware(void)
{
#ifdef WIN32
	// test for .NET 2.0 or higher
	HKEY hKey;
	if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\.NETFramework\\policy\\v2.0", 0L, KEY_READ , &hKey) == ERROR_SUCCESS)
	{
		string dotnetMsg = "You don't have .NET 2.0 installed! .NET is a framework required by the Edition.\n";
		dotnetMsg = dotnetMsg + "You can download it from:\n" +
								"http://gumby.oni2.net/dotnet\n" +
								"Please install .NET 2.0, then open this Installer again.\n\n" +
								"Would you like to open the download webpage?";
		wxMessageDialog* DotNetDialogOfDeath = new wxMessageDialog(TheWindow, dotnetMsg.c_str(), "AE Installer Alert",
																 wxYES_NO | wxICON_EXCLAMATION	, wxDefaultPosition);
		if (DotNetDialogOfDeath->ShowModal() == wxID_YES)
			system("start http://www.microsoft.com/downloads/details.aspx?familyid=0856eacb-4362-4b0d-8edd-aab15c5e04f5");
		RegCloseKey(hKey);
		return false;
	}
#else // on Mac...
	// test for the third-party "mono" framework, because without it, we are up a creek
	FILE *fWhichMono = NULL;
	char chrWhichMono[32];
	fWhichMono = popen("which mono", "r");
	fgets(chrWhichMono, sizeof(chrWhichMono), fWhichMono);
	pclose(fWhichMono);
	string strWhichMono = (string)chrWhichMono;
	string::size_type loc = strWhichMono.find("mono", 0);
	
	if (loc == string::npos) // this means that "which mono" did not return a path leading to the mono binary -- abort! abort! abort!
	{
		string monoMsg = "You don't have 'mono' installed! 'mono' is a command-line tool required by the Edition.\n";
		monoMsg = monoMsg + "You can download it from: http://www.go-mono.com/mono-downloads/download.html (OS X 10.4+)\n" +
							"or http://edt.oni2.net/AE/MonoFramework10.3.dmg (OS X 10.3)\n\n" +
							"Please install 'mono', then open this Installer again.";
		wxMessageDialog* MonoDialogOfDeath = new wxMessageDialog(TheWindow, monoMsg.c_str(), "AE Installer Alert", wxOK | wxICON_EXCLAMATION, wxDefaultPosition);
		MonoDialogOfDeath->ShowModal();
		return false; // it's quittin' time, Joe
	}
#endif
	return true;
}

bool CheckForGlobalization(bool justDoIt)
{
	if (!exists("../GameDataFolder"))
	{
		string globMsg = "You haven't globalized yet!\n";
		globMsg = globMsg + "You must globalize to use the Anniversary Edition framework.\n" +
							"Would you like to globalize now? (This could take a while...)\n" +
							"(Selecting \"No\" will exit this program...)";
		wxMessageDialog* YesNoDialog = new wxMessageDialog(TheWindow, globMsg.c_str(), "AE Installer Alert", wxYES_NO | wxICON_EXCLAMATION, wxDefaultPosition);
		
		if (YesNoDialog->ShowModal() == wxID_NO) // if the user said no...
		{
			TheWindow->Close();
			return true;
		}
	}
	else if (!justDoIt)
		return false;
	// Code below this point runs if user clicks "Yes" or if they are never asked but justDoIt is true
#ifdef WIN32
	boost::thread thrd3(globalize2);
#else // cannot use multi-threading in Mac build
	TheWindow->InstallButton->Disable();
	TheWindow->ReglobalizeButton->Disable();
	globalizeData();
	TheWindow->InstallButton->Enable();
	TheWindow->ReglobalizeButton->Enable();
#endif
	
	return true;
}

void setStatusArea(string s)
{
	wxString wxs(s.c_str(), wxConvUTF8);
	
	TheWindow->StatusArea->SetStatusText(wxs);
}


/*
 * Cleanup for AEInstallerApp
 */

int AEInstallerApp::OnExit()
{    
	////@begin AEInstallerApp cleanup
	return wxApp::OnExit();
	////@end AEInstallerApp cleanup
}
void doglobalizeData()
{
	globalizeData();
#ifdef WIN32
	while(1) Sleep(-1);
#endif
}
