/* GPC (C) 2017-2018 Stephane Charette <stephanecharette@gmail.com>
 * $Id: GPCApp.cpp 2475 2018-03-12 01:01:37Z stephane $
 */

#include "GPC.hpp"


void GPC_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 GPC_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 GPC_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);
}


GPCApplication::GPCApplication(void)
{
	return;
}


GPCApplication::~GPCApplication(void)
{
	if (SessionRecord::configuration_needs_to_be_saved)
	{
		save_all_sessions();
	}

	unlock_all_files();

	return;
}


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

	std::set_terminate(GPC_CPlusPlus_Terminate_Handler);
	std::set_unexpected(GPC_CPlusPlus_Unexpected_Handler);
	SystemStats::setApplicationCrashHandler(GPC_Juce_Crash_Handler);

	LOG_MSG( "starting Gorman Print Control" );

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

	run( EJob::kInitializeAndLoad );

	return;
}


void GPCApplication::shutdown()
{
	// shutdown the application
	LOG_MSG( "shutting down Gorman Print Control" );

//	log_client_history_event(EHistoryEvent::kDisconnect, DBNullId, "client stop");

	// 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
	summary_wnd				.reset(nullptr);
//	role_pick_wnd			.reset(nullptr);
	cfg						.reset(nullptr);

	reset_gpc_look_and_feel();
	laf						.reset(nullptr);

	return;
}


void GPCApplication::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 GPCApplication::unhandledException(const std::exception *e, const String &sourceFilename, int lineNumber)
{
	log_and_show_exception(e, sourceFilename.toStdString(), lineNumber );

	return;
}


void GPCApplication::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:
			{
				run(EJob::kShowSummaryWindow);
				break;
			}
			case EJob::kShowSummaryWindow:
			{
				LOG_MSG("starting the summary window");
				summary_wnd.reset(new SummaryWnd);
				break;
			}
			case EJob::kShowAboutWindow:
			{
				job_show_about_window();
				break;
			}
			case EJob::kShutdown:
			{
				JUCEApplication::getInstance()->systemRequestedQuit();
				break;
			}
			case EJob::kNoOp:
			{
				break;
			}
		}
	}
	CATCH_AND_LOG

	return;
}


void GPCApplication::job_initialize_and_load(void)
{
	SplashScreen *splash = new SplashScreen( "Gorman Print Control", GormanSplashLogo(), false);
	splash->deleteAfterDelay(RelativeTime::seconds(15), true);

	// Beware!  The order in which these are instantiated is important.
	laf	.reset(init_gpc_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 Print Control", msg, "quit");
		exit(1);
	}

	run(EJob::kShowFirstWindow);

	return;
}


void GPCApplication::job_show_about_window(void)
{
	std::stringstream ss;
	ss	<< "GPC v" << GPC_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
		<< ""																				<< std::endl
		<< "Dir: "	<< directory.getFullPathName().toStdString()							<< std::endl
		<< "Log: "	<< get_log_filename()													<< std::endl
		<< "Cfg: "	<< cfg->getFile().getParentDirectory().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 Print Control", ss.str() );

	return;
}


void GPCApplication::timerCallback(void)
{
	stopTimer();

	if (SessionRecord::configuration_needs_to_be_saved)
	{
		LOG_MSG("timer expired, and flag is set indicating that configuration has changed");
		save_all_sessions();
	}

	return;
}
