#include "FishIDE.h"

#include "Settings.h"
#include "SettingsDialogs.h"
#include "LibraryPool.h"
#include "Libraries.h"
#include "Examples.h"

#include "Theme.h"

#include "Themes.brc"
#include "SettingsDefault.brc"
#include "FileFinder.h"

#include <MDNS/MDNS.h>

#define TFILE <FishIDE/FishIDE.t>
#include <Core/t.h>

#define IMAGECLASS FishIDEImg
#define IMAGEFILE <FishIDE/FishIDE.iml>
#include <Draw/iml_source.h>

AVersion VERSION("2.0.0");

#ifdef WIN32
extern "C"
{
	static FILE file[] = {*stdin, *stdout, *stderr};
	FILE *__iob_func() { return file; }
};
#endif

// splash screen stuffs
Splash &splash()
{
	static bool initialized;
	static Splash sp;
	if(!initialized)
	{
		initialized = true;
		sp.SetSize(75);
		sp.SetImage(FishIDEImg::FishIDE);
	}
	return sp;
}

FishIDE::FishIDE()
{
	Title("FishIDE");
	Icon(FishIDEImg::FishIcon(), FishIDEImg::FishIcon());

	// set up file opener for LastFiles
//	lastFiles.Opener = THISBACK(AddJob0);
	
	// set up menu
//	AddFrame(menu.MaxIconSize(Size(100, 50)));
	AddFrame(menu);
	AddFrame(toolBar);

	updateMenuCb();
	
	// setup statusbar
	AddFrame(statusBar);
//	StatusChanged(NULL);
	
	// allow zoom and resize
	Sizeable().Zoomable();
	
	// adds the tabbed ctrl
	Add(projectsCtrl.SizePos());
	projectsCtrl.WhenChange = THISBACK(updateMenuCb);
	
	// setup boards selector
/*
	Board const *board = Settings.GetBoard();
	if(board)
		boardSelector.Set(board->GetName());
*/
	boardSelector.WhenBoard = THISBACK(selectBoardCb);
	
	// setup active device
	deviceSelector.Set(Settings.GetDevice());
	deviceSelector.WhenDevice = THISBACK(selectDeviceCb);
	deviceSelector.EnableNetwork();

	find_pick_sel = true;
	find_pick_text = false;
	
	Settings.WhenChange = THISBACK(settingsChangedCb);
	settingsChangedCb();
	
	Themes.WhenApply = THISBACK(themeChangedCb);

}

// destructor
FishIDE::~FishIDE()
{
	KillTimeCallback(TIMEID_MENU);
}

// propagate theme application
void FishIDE::themeApplyCb(Theme const *t)
{
	// apply theme to all open projects
	projectsCtrl.ThemeChanged(t);
}

// triggered by changes in settings
void FishIDE::settingsChangedCb(void)
{
	LibraryPool.Scan();

	// re-fill droplist with available boads
	boardSelector.Refresh();
	boardSelector.Set(Settings.GetActiveBoard());
	
	// propagate settings changes to all opened projects
	projectsCtrl.SettingsChanged();
}

// triggered by changes in libraries
void FishIDE::librariesChangedCb(void)
{
	settingsChangedCb();
}
	
// triggered by changes in themes
void FishIDE::themeChangedCb(Theme const *t)
{
//@@	IdeFilePool.RestyleEditors(t);
	projectsCtrl.ThemeChanged(t);
}

// mdns loop -- checks periodically for network ports changes
void FishIDE::mdnsLoopCb(void)
{
	// just run MDNS class Loop
	MDNS.Loop();
	
	// and schedule for next run
	PostCallback(THISBACK(mdnsLoopCb));
}

void FishIDE::mdnsCb(void)
{
	deviceSelector.refreshDevice();
}

// start MDNS
void FishIDE::StartMDNS(void)
{
	// initialize MDNS
	MDNS.WhenService = THISBACK(mdnsCb);
	MDNS.ListenFor("arduino", "tcp");
	MDNS.Listen();
	MDNS.Query();
	mdnsLoopCb();
}

// store/loads last opened projects
void FishIDE::RestoreOpenedProjects(void)
{
	String projectsFile = AppendFileName(GetConfigPath(), "PROJECTS.xml");
	if(FileExists(projectsFile))
		LoadFromXMLFile(projectsCtrl, projectsFile);
}

void FishIDE::SaveOpenedProjects(void)
{
	// store opened projects
	String projectsFile = AppendFileName(GetConfigPath(), "PROJECTS.xml");
	StoreAsXMLFile(projectsCtrl, "FishIDE opened projects", projectsFile);
}

// auto setup
bool FishIDE::AutoSetup(void)
{
	// try to locate sketchbook folder
	Settings.SetSketchFolder(Settings.LocateSketchbook());
	
	// first, locate arduino package and its avr platform
	Package *package = Packages.FindPackage("arduino");
	if(!package)
		return false;
	
	// next locate avr platform inside package
	Platform *platform = package->FindPlatformByArchitecture("avr");
	if(!platform)
		return false;
	
	// get last available version
	String ver = platform->GetLastVersion();
	if(ver.IsEmpty())
		return false;
	
	// install it
	if(!platform->Install(ver))
		return false;
	
	// refresh settings state
	Settings.RefreshInstalled();
	
	// now libraries turn...
	const char *libs[] = {
		"Bridge", "Esplora", "Ethernet", "Firmata", "GSM", "Keyboard", "LiquidCrystal", "Mouse", "Robot_Control",
		"RobotIRremote", "Robot_Motor", "SD", "Servo", "SpacebrewYun", "Stepper", "Temboo", "TFT", "WiFi"
	};
	const int nLibs = sizeof(libs) / sizeof(const char *);
	for(int i = 0; i < nLibs; i++)
	{
		String name = libs[i];
		Library *lib = Libraries.Find(name);
		if(!lib)
			continue;
		
		MaintainerVersion ver = lib->GetLatestVersion();
		lib->Install(ver);
	}
	librariesChangedCb();
	
	// and finally, examples turn...
	const char *examples[] = {
		"AnalogReadSerial", "BareMinimum", "Blink", "DigitalReadSerial", "Fade", "ReadAnalogVoltage",
		"BlinkWithoutDelay", "Button", "Debounce", "DigitalInputPullup", "StateChangeDetection",
		"toneKeyboard", "toneMelody", "toneMultiple", "tonePitchFollower", "AnalogInOutSerial",
		"AnalogInput", "AnalogWriteMega", "Calibration", "Fading", "Smoothing", "ASCIITable",
		"Dimmer", "Graph", "Midi", "MultiSerial", "PhysicalPixel", "ReadASCIIString", "SerialCallResponse",
		"SerialCallResponseASCII", "SerialEvent", "VirtualColorMixer", "Arrays", "ForLoopIteration",
		"IfStatementConditional", "switchCase", "switchCase2", "WhileStatementConditional", "ADXL3xx",
		"Knock", "Memsic2125", "Ping", "barGraph", "RowColumnScanning", "CharacterAnalysis", "StringAdditionOperator",
		"StringAppendOperator", "StringCaseChanges", "StringCharacters", "StringComparisonOperators",
		"StringConstructors", "StringIndexOf", "StringLength", "StringLengthTrim", "StringReplace",
		"StringStartsWithEndsWith", "StringSubstring", "StringToInt", "KeyboardLogout", "KeyboardMessage",
		"KeyboardReprogram", "KeyboardSerial", "KeyboardAndMouseControl", "ButtonMouseControl", "JoystickMouseControl",
		"p02_SpaceshipInterface", "p03_LoveOMeter", "p04_ColorMixingLamp", "p05_ServoMoodIndicator",
		"p06_LightTheremin", "p07_Keyboard", "p08_DigitalHourglass", "p09_MotorizedPinwheel", "p10_Zoetrope",
		"p11_CrystalBall", "p12_KnockLock", "p13_TouchSensorLamp", "p14_TweakTheArduinoLogo", "p15_HackingButtons", "ArduinoISP",
	};
	const int nExamples = sizeof(examples) / sizeof(const char *);
	for(int i = 0; i < nExamples; i++)
	{
		String name = examples[i];
		Example *example = Examples.Find(name);
		if(!example)
			continue;
		
		MaintainerVersion ver = example->GetLatestVersion();
		example->Install(ver);
	}
	
	return true;
}


INITBLOCK {
	RegisterGlobalConfig("MainWindow");
	RegisterGlobalConfig("FileDialogs");
	RegisterGlobalConfig("OthersFileDialogs");
	RegisterGlobalConfig("Keys");
}

GUI_APP_MAIN
{
	// setup default language
	SetLanguage(GetSystemLNG());
	SetDefaultCharset(CHARSET_UTF8);
	
	// first-run flag
	bool firstRun = false;
	
	// if exists a config file, load it
	String configFile = AppendFileName(GetConfigPath(), "FishIDE.cfg");
	if(FileExists(configFile))
	{
		FileIn f(configFile);
		SerializeGlobalConfigs(f);
	}
	else
		firstRun = true;
	
	// stream in Settings data, if file exists
	String settingsFile = AppendFileName(GetConfigPath(), "SETTINGS.xml");
	if(!FileExists(settingsFile))
		SaveFile(settingsFile, (const char *)SettingsDefault);
	LoadFromXMLFile(Settings, settingsFile);
	
	// load themes
	String themesFile = AppendFileName(GetConfigPath(), "THEMES.xml");
	if(FileExists(themesFile))
		LoadFromXMLFile(Themes, themesFile);
	
	// if we've got some built in themes, append them
	ArrayMap<String, Theme> builtins;
	LoadFromXML(builtins, (const char *)builtin_themes);
	Themes.Append(builtins);
	builtins.Clear();
	
	// apply current theme
	Themes.ApplyCurrent();
	
	// load find dialog settings
	String findDialogFile = AppendFileName(GetConfigPath(), "FINDDLG.xml");
	if(FileExists(findDialogFile))
		LoadFromXMLFile(FileFinder, findDialogFile);
	
	// splash screen
	if(Settings.GetIdeShowSplash())
	{
		String message = t_(
			"[*5a@6 "
			"Integrated&"
		    "    Development&"
		    "        Environment&"
			"For Arduino-like boards&"
			"Copyright(C) 2017&"
		    "    by Massimo Del Fedele"
		    "]"
	    );
		splash().FadeIn(15);
//		splash().ShowVersion(message);
		splash().ShowVersion("");
		SetTimeCallback(2000, callback1(&splash(), &Splash::FadeOut, 15));
	}

	// main application class
	One<FishIDE> fishIDE = new FishIDE;

	// restores window position/size
	LoadFromGlobal(*fishIDE, "MainWindow");

	// restires keys definitions
	String keyData;
	LoadFromGlobal(keyData, "Keys");
	if(!keyData.IsEmpty())
		RestoreKeys(keyData);
	
	// stream in previously opened projects, if we wants so
	if(Settings.GetIdeRememberProjects())
		fishIDE->RestoreOpenedProjects();
	
	// if that's the first run, offer the possibility to auto-setup
	// default arduino packages and samples
	if(firstRun)
	{
		int res = PromptYesNo(t_(
				"[*5@6= RUN AUTO SETUP ?]&&"
				"It seems that this is the first time you run [* FishIDE]&"
				"Would you like to have default arduino boards, libraries and examples&"
				"downloaded and installed automatically?&&"
				"If you answer [* NO] to this, you will have to install all needed elements manually&"
				"If you are an [* Arduino IDE] user, we suggest that you answer [@6* YES] here&"
				"(note : autosetup will need some time and an active internet connection)"
		));
		while(res)
		{
			if(!fishIDE->AutoSetup())
			{
				res = ErrorAbortRetryIgnore(t_(
					"Auto setup failed&"
					"You can do one of the following:&"
					"[* abort]  to quit the application&"
					"[* retry]  to try autosetup again now&"
					"[* ignore] to run FishIDE and setup manually later"
				));
				switch(res)
				{
					case 1: // abort
						Exit(1);
						break;
						
					case 0: // retry
						res = 1;
						break;
						
					default: // ignore
						res = 0;
						break;
				}
			}
			else
				res = 0;
		}
	}

	// load recent file list
	// @@ TO DO
	
	// start MDNS processing
	fishIDE->StartMDNS();
	
	// run the ide
	fishIDE->Run();
	
	// save recent file list
	// @@ TO DO
	
	// save current opened projects
	fishIDE->SaveOpenedProjects();
	
	// save keys definitions
	keyData = StoreKeys();
	StoreToGlobal(keyData, "Keys");
	
	// saves window position-size
	StoreToGlobal(*fishIDE, "MainWindow");

	// save find dialog settings
	StoreAsXMLFile(FileFinder, "Find dialog", findDialogFile);
	
	// save themes
	StoreAsXMLFile(Themes, "Themes", themesFile);

	// save settings
	StoreAsXMLFile(Settings, "FishIDE settings", settingsFile);
	
	// save global configuration to file
	FileOut f(configFile);
	SerializeGlobalConfigs(f);
	
}
