/* GMM (C) 2018 Stephane Charette <stephanecharette@gmail.com>
 * $Id: GMMApp.cpp 2624 2018-09-26 06:19:56Z stephane $
 */

#include "GMM.hpp"


void GMM_Juce_Crash_Handler(void *ptr)
{
#ifndef WIN32
	const long signal_number = (long)ptr;
	LOG_MSG( "crash handler invoked for signal #" << signal_number << " (" << strsignal(signal_number) << ")" );
#else
	LOG_MSG( "crash handler invoked" );
#endif

	const Lox::Exception e( LOX_WHERE, "crash handler invoked" );
	LOG_MSG( e.to_string() );

	exit(1);
}


void GMM_CPlusPlus_Terminate_Handler(void)
{
	LOG_MSG( "terminate handler invoked" );

	const Lox::Exception e( LOX_WHERE, "terminate handler invoked" );
	LOG_MSG( e.to_string() );

	exit(2);
}


void GMM_CPlusPlus_Unexpected_Handler(void)
{
	LOG_MSG( "unexpected handler invoked" );

	const Lox::Exception e( LOX_WHERE, "unexpected handler invoked" );
	LOG_MSG( e.to_string() );

	exit(3);
}


GMMApplication::GMMApplication(void)
{
	return;
}


GMMApplication::~GMMApplication(void)
{
	return;
}


void GMMApplication::initialise(const String& commandLine)
{
	// This method is where you should put your application's initialisation code.

	std::set_terminate(GMM_CPlusPlus_Terminate_Handler);
	std::set_unexpected(GMM_CPlusPlus_Unexpected_Handler);
	SystemStats::setApplicationCrashHandler(GMM_Juce_Crash_Handler);

	LOG_MSG( "starting Gorman Moisture Measurement" );

	std::srand(Time::getMillisecondCounter());

	run( EJob::kInitializeAndLoad );

	return;
}


void GMMApplication::shutdown()
{
	// shutdown the application
	LOG_MSG( "shutting down Gorman Moisture Measurement" );

	// strictly speaking, we don't have to reset the unique ptr since all the
	// objects will be deleted when the unique pointers go out of scope, but
	// this way by being explicit we can control the exact order in which
	// they're deleted
	wnd.reset(nullptr);
	cfg.reset(nullptr);

	reset_gmm_look_and_feel();
	laf.reset(nullptr);

	return;
}


void GMMApplication::anotherInstanceStarted(const String& commandLine)
{
	// When another instance of the app is launched while this one is running,
	// this method is invoked, and the commandLine parameter tells you what
	// the other instance's command-line arguments were.

	return;
}


void GMMApplication::unhandledException(const std::exception *e, const String &sourceFilename, int lineNumber)
{
	log_and_show_exception(e, sourceFilename.toStdString(), lineNumber );

	return;
}


void GMMApplication::run( const EJob job )
{
	LOG_MSG("running job #" << (int)job << ": \"" << to_string(job) << "\"");

	try
	{
		switch(job)
		{
			case EJob::kInvalid:
			{
				break;
			}
			case EJob::kInitializeAndLoad:
			{
				job_initialize_and_load();
				break;
			}
			case EJob::kShowFirstWindow:
			{
				// figure out which one of the possible windows we should be showing

				LOG_MSG("need to invoke the main window");
				run(EJob::kShowMainWindow);
				break;
			}
			case EJob::kShowMainWindow:
			{
				LOG_MSG("starting the main window");
				wnd.reset(new GMMWnd);
				break;
			}
			case EJob::kShowAboutWindow:
			{
				job_show_about_window();
				break;
			}
			case EJob::kShutdown:
			{
				if (wnd)
				{
					wnd->setVisible(false);
				}
				JUCEApplication::getInstance()->systemRequestedQuit();
				break;
			}
			case EJob::kNoOp:
			{
				break;
			}
		}
	}
	CATCH_AND_LOG

	return;
}


void GMMApplication::job_initialize_and_load(void)
{
	SplashScreen *splash = new SplashScreen( "Gorman Moisture Meter", GormanSplashLogo(), false);
	splash->deleteAfterDelay(RelativeTime::seconds(5), true);

	// Beware!  The order in which these are instantiated is important.
	laf	.reset(init_gmm_look_and_feel());
	cfg	.reset(new Cfg);

	LoadWnd load_wnd;
	load_wnd.runThread();	// wont return until LoadWnd::run() is done
	if (load_wnd.finished_loading == false)
	{
		errors.push_back("Load thread failed to complete.");
	}

	if (errors.size())
	{
		// we have 1 or more fatal error conditions

		for (const auto &msg : errors)
		{
			LOG_MSG("Error detected:" << std::endl << msg);
		}
		const std::string msg = errors[0];
		AlertWindow::showMessageBox(AlertWindow::WarningIcon, "Gorman Moisture Meter", msg, "quit");
		exit(1);
	}

	run(EJob::kShowFirstWindow);

	return;
}


void GMMApplication::job_show_about_window(void)
{
	std::stringstream ss;
	ss	<< "GMM v" << GMM_VERSION					<< std::endl
		<< ""										<< std::endl
		<< "stephanecharette@gmail.com"				<< std::endl
		<< "http://www.ccoderun.ca/"				<< std::endl
		<< ""										<< std::endl
		<< "Built: " << __DATE__ << " " << __TIME__	<< std::endl
		<< "Type: "
#if WIN32
		<< "Windows"
#else
		<< "Linux"
#endif
		<< (sizeof(void*) == 4 ? ", X86 build" : ", AMD64 build")							<< std::endl
		<< "User: "	<< SystemStats::getLogonName() << "@" << SystemStats::getComputerName()	<< std::endl
		<< "O/S: "	<< (SystemStats::isOperatingSystem64Bit() ? "64-bit " : "32-bit ")
					<< SystemStats::getOperatingSystemName()	<< ", "
					<< SystemStats::getUserLanguage()			<< ", "
					<< SystemStats::getUserRegion()											<< std::endl
		<< "Log: "	<< get_log_filename()													<< std::endl
		<< "Cfg: "	<< cfg->getFile().getFullPathName().toStdString()						<< std::endl
		<< ""																				<< std::endl
		<< "Juce: version "			<< JUCE_MAJOR_VERSION << "."
									<< JUCE_MINOR_VERSION << "."
									<< JUCE_BUILDNUMBER 									<< std::endl
		<< "Loxahatchee: version "	<< Lox::version();

	LOG_MSG("About window information:" << std::endl << ss.str());

	AlertWindow::showMessageBoxAsync(AlertWindow::AlertIconType::InfoIcon, "Gorman Moisture Meter", ss.str() );

	return;
}
