/* GPC (C) 2017-2018 Stephane Charette <stephanecharette@gmail.com>
 * $Id: LoadWnd.cpp 2512 2018-04-07 06:41:10Z stephane $
 */

#include "GPC.hpp"


LoadWnd::LoadWnd(void) : ThreadWithProgressWindow( "GPC", true, false ),
	finished_loading(false),
	total_number_of_items_to_load(0),
	progress_level(0)
{
	// move the progress window far enough down the screen so it appears beneath (not behind) the splash screen logo
	juce::Rectangle<int> r = getAlertWindow()->getBounds();
	r.setY( r.getY() + GormanSplashLogo().getHeight() + 25 );
	getAlertWindow()->setBounds(r);

	return;
}


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


void LoadWnd::update_progress_bar(const double progress, const std::string msg)
{
	if (msg.empty() == false)
	{
		LOG_MSG("loading: " << msg);
		setStatusMessage(msg);
	}

	if (total_number_of_items_to_load < 100.0)
	{
		total_number_of_items_to_load = gpc().cfg->getIntValue("number_of_items_to_load", 5000 );

		// guard against things like divide-by-zero
		if (total_number_of_items_to_load < 1.0)
		{
			total_number_of_items_to_load = 5000;
		}
	}

	if (progress > 0.0)
	{
		progress_level += progress;
		if (progress_level < 1.0)
		{
			progress_level = 1.0;
		}

		const double tmp = progress_level / total_number_of_items_to_load;
		LOG_MSG("update progress bar: " << progress_level << "/" << total_number_of_items_to_load << " = " << Lox::Numbers::format(tmp * 100.0, 1) << "%");
		setProgress( tmp );
	}

	return;
}


std::string LoadWnd::get_status_message(void)
{
	std::string msg = "Loading...";

	Random r;
	r.setSeedRandomly();
	if (r.nextFloat() >= 0.9)	// 10% chance of getting a random string
	{
		const SStr s =
		{
			"Beam me up..."									,
			"Calling Jenny at 867-5309..."					,
			"Chasing an untamed ornithoid without cause..."	,
			"Chatting with other computers..."				,
			"Comparing Picard and Kirk..."					,
			"Don't worry, be happy!"						,
			"E.T. phone home..."						,
			"Evaluating artificial intelligence..."			,
			"Fixing ozone layer..."							,
			"Going fishing..."								,
			"Looking for evidence of life..."				,
			"Looking for trees..."							,
			"Making him an offer he can't refuse..."		,
			"May the force be with you..."					,
			"Opening the pod bay doors..."					,
			"Party at your place next weekend..."			,
			"Re-booting the internet..."					,
			"Saying the alphabet backwards..."				,
			"Searching for doughnuts..."					,
			"Shall we play a game?"							,
			"Sipping coffee..."								,
			"Starting HAL 9000..."							,
			"Studying Shakespeare..."						,
			"There's no place like home..."					,
			"Thinking BBQ for dinner..."					,
			"Tightening lug nuts..."						,
			"Waiting for summer..."							,
			"We're going to need a bigger boat..."			,
			"You talking to me?"
		};

		// pick a random string to return
		const size_t idx = r.nextInt(s.size());
		SStr::const_iterator iter = s.begin();
		std::advance(iter, idx);
		msg = *iter;
	}

	return msg;
}


void LoadWnd::run(void)
{
	// this is on a secondary thread -- see call to runThread() in GPCApplication::initialise()

	try
	{
		// give more weight to some of the initial tasks to get the status bar started
		update_progress_bar(5.0, "Starting..." );

		auto &errors = gpc().errors;

		if (errors.empty()) directory_handling();
		if (errors.empty()) check_for_new_version();
		if (errors.empty()) check_for_motd();
		if (errors.empty()) check_build_timestamp();
		if (errors.empty()) check_for_json();
		if (errors.empty()) load_all_sessions();
		if (errors.empty()) delete_old_sessions();

		if (errors.empty() && progress_level != total_number_of_items_to_load)
		{
			cfg().setValue( "number_of_items_to_load", String(progress_level) );
		}

		if (errors.empty()) finished_loading = true;
	}
	CATCH_LOG_SHOW_AND_RETHROW

	return;
}


void LoadWnd::directory_handling(void)
{
	// several places we're going to look for our files
	VStr v =
	{
		"C:\\ProgramData\\GormanPrintControl",
		"C:\\Users\\All Users\\GormanPrintControl",
		"C:\\GormanPrintControl",
		"/opt/GormanPrintControl"
	};

	std::string dir_in_cfg;
	if (cfg().containsKey("directory"))
	{
		// if we have a directory listed in configuration, try
		// that before any of the other ones hard-coded above
		dir_in_cfg = cfg().get_str("directory");
		v.insert(v.begin(), dir_in_cfg);
	}

	std::string directory;
	for (const std::string &dir_name : v)
	{
		LOG_MSG("trying directory \"" << dir_name << "\"");
		File dir(dir_name);
		if (dir.isDirectory())
		{
			LOG_MSG("found a usable directory: \"" << dir_name << "\"");
			directory = dir_name;
			break;
		}
	}

	if (directory.empty())
	{
		gpc().errors.push_back(
			"Cannot find the data directory with the expected image and print files.\n\n"
			"Please see the documentation to configure Gorman Print Control.\n\n"
			"Unless manually configured differently, the default data directory should be \"" + v[0] + "\".");
	}
	else
	{
		if (dir_in_cfg != directory)
		{
			cfg().set_str("directory", directory);
		}

		gpc().directory = directory;
	}

	return;
}


void LoadWnd::check_for_json(void)
{
	update_progress_bar(1.0, get_status_message());

	File parent = gpc().directory;
	File json_file = parent.getChildFile("gpc.json");

	if (json_file.existsAsFile() == false)
	{
		LOG_MSG("attempting to create a new session file " + json_file.getFullPathName().toStdString());

		// session file doesn't yet exist, so attempt to create an empty one
		const bool result = json_file.replaceWithText("{}");
		if (result == false)
		{
			LOG_MSG("failed to create a new session file; check permissions or path?");
		}
	}

	if (json_file.existsAsFile() == false)
	{
		gpc().errors.push_back("Cannot find the configuration file \"" + json_file.getFullPathName().toStdString() + "\".");
	}

	return;
}


void LoadWnd::delete_old_sessions(void)
{
	const std::time_t time_limit = 1520016000; // Friday, 2 March 2018 18:40:00

	auto sessions = gpc().sessions;

	auto iter = sessions.begin();
	while (iter != sessions.end())
	{
		auto & uuid = iter->first;
		auto & rec = iter->second;

		if (rec.creationEpoch > time_limit)
		{
			// found a modern entry, we need to leave it alone
			iter ++;
			continue;
		}

		// if we get here, we have an ancient entry that needs to be deleted

		try
		{
			File dir = rec.get_dir();
			dir.deleteRecursively();
		}
		catch (...)
		{
			// ignore the exception -- maybe the directory didn't exist?
		}

		LOG_MSG("deleting old session " << uuid);
		iter = sessions.erase(iter);
	}

	return;
}


void LoadWnd::check_for_new_version(void)
{
	if (cfg().get_bool("get_version_number") == false)
	{
		return;
	}

	update_progress_bar(1.0, "Verifying client version number...");

	// sort by modification time, descending order
	const std::string url = "http://gpc.ccoderun.ca/gpc/download/?C=M;O=D";
	LOG_MSG( "getting download information from " << url );
	const std::string text = get_url( url );

	// if this works, we end up with lines like this:
	//
	//	[...] <td><a href="gpc-1.0.0-2265-win64.exe">gpc-1.0.0-2265-win64.exe</a></td> [...]
	//	[...] <td><a href="gpc-1.0.0-2265-win32.exe">gpc-1.0.0-2265-win32.exe</a></td> [...]
	//
	// from which we'll try to extract the first version number (since they're sorted in descending order)
	const std::regex rx( "href=\"gpc-([0-9-.]+)-win" );
	std::smatch match;
	if ( std::regex_search( text, match, rx ) )
	{
		const std::string new_version_number = match.str(1);

		LOG_MSG( "current version=" << GPC_VERSION << ", most recent version=" << new_version_number );
		
		if (new_version_number > GPC_VERSION)
		{
			AlertWindow::showOkCancelBox(
				AlertWindow::InfoIcon,
				"This version is out of date!",
				"You are using version " + std::string(GPC_VERSION) + " but the newer version " + new_version_number + " is available for download.\n"
				"\n"
				"Open the download page?",
				String::empty,
				String::empty,
				nullptr,
				ModalCallbackFunction::create(
					[url](int result_code)
					{
						if (result_code)
						{
							LOG_MSG( "opening download page " << url );
							URL::createWithoutParsing(url).launchInDefaultBrowser();
							LOG_MSG( "exiting since the download page has been opened" );
							gpc().quit();
						}
						else
						{
							LOG_MSG( "do not open the download page " << url );
						}
					} ) );
		}
	}

	return;
}


void LoadWnd::check_for_motd(void)
{
	if (cfg().get_bool("get_motd") == false)
	{
		return;
	}

	update_progress_bar(1.0, "Getting ready...");

	String motd = Lox::trim( get_url("http://gpc.ccoderun.ca/gpc/motd.txt") );
	if (motd.isEmpty())
	{
		update_progress_bar(1.0);

		motd = Lox::trim( get_url("http://gpc.ccoderun.ca/gpc/download/motd.txt") );
	}

	if (motd.isNotEmpty())
	{
		LOG_MSG( "MOTD: " << motd );

		AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon, "Gorman Print Control", motd );
	}

	return;
}


void LoadWnd::check_build_timestamp(void)
{
	#if 0
	update_progress_bar(1.0);

	const std::time_t tt1 = Lox::timeFromString(__DATE__ " " __TIME__);
	const std::time_t tt2 = std::time(nullptr);
	const std::time_t seconds_in_one_day	= 86400;
	const std::time_t seconds_in_one_week	= 7 * seconds_in_one_day;

	if (tt2 - tt1 > seconds_in_one_week)
	{
		AlertWindow::showMessageBoxAsync(
			AlertWindow::WarningIcon,
			"Gorman Print Control",
			"This build is from " + Lox::approximateTime(tt1) + ". Please consider upgrading to a newer version of GPC.\n"
			"\n"
			"Visit \"https://www.ccoderun.ca/gpc/download/\" for the latest build.");
	}
	#endif

	return;
}
