#include "Settings.h"

#include "SettingsDialogs.h"
#include "NetTools.h"
#include "Examples.h"
#include "Package.h"
#include "Libraries.h"
#include "LibraryPool.h"

extern AVersion VERSION;

// get application configuration path
String const &GetConfigPath(void)
{
	static String userConfigPath = "";
	
	if(userConfigPath.IsEmpty())
	{
		String appName = "FishIDE";
			// gets app's user config path
		#if defined(PLATFORM_OSX11)
			userConfigPath = AppendFileName("/Users", GetUserName() + "/Library/" + appName);
		#elif defined(PLATFORM_POSIX)
			userConfigPath = AppendFileName("/home", GetUserName() + "/." + appName);
		#else
			userConfigPath = AppendFileName(GetAppDataFolder(), appName);
		#endif
		
		RealizeDirectory(userConfigPath);
	}
	return userConfigPath;
}


// constructor
SettingsClass::SettingsClass()
{
	// sktchbook
	_sketchFolder = "";
	_examplesURLs.Clear();
	_librariesURLs.Clear();
	_packagesURLs.Clear();
	
	// compiler options
	_compilerVerbosity				= 0;
	_compilerWarnLevel				= 1;
	_compilerUserWarnings			= "-Wall";
	_compilerOptimizations			= 1;
	_compilerFullRebuild			= false;
	_compilerAutoLibDependencies	= true;
	
	// uploader options
	_uploaderVerbosity		= 0;
	
	// ide options
	_ideCreateProjectStubs	= true;
	_ideCreateLibraryStubs	= true;
	_ideRememberFiles		= true;
	_ideRememberProjects	= true;
	_ideSysLibsReadOnly		= true;
	_ideShowSplash			= true;

	// formatter options
	_astyleBracketIndent = false;
	_astyleNamespaceIndent = true;
	_astyleBlockIndent = false;
	_astyleCaseIndent = true;
	_astyleClassIndent = true;
	_astyleLabelIndent = true;
	_astyleSwitchIndent = true;
	_astylePreprocessorIndent = false;
	_astyleMaxInStatementIndentLength = 20;
	_astyleMinInStatementIndentLength = 2;
	_astyleBreakClosingHeaderBracketsMode = 0;
	_astyleBreakElseIfsMode = true;
	_astyleBreakOneLineBlocksMode = true;
	_astyleSingleStatementsMode = true;
	_astyleBreakBlocksMode = false;
	_astyleBreakClosingHeaderBlocksMode = false;
	_astyleBracketFormatMode = 1;
	_astyleParensPaddingMode = 0;
	_astyleParensUnPaddingMode = true;
	_astyleOperatorPaddingMode = false;
	_astyleEmptyLineFill = false;
	_astyleTabSpaceConversionMode = false;
	
	// editor options
	_editorTabsize = 4;
	_editorIndentAmount = 4;
	_editorShowTabs = false;
	_editorIndentSpaces = false;
	_editorNoParenthesisIndent = false;
	_editorShowLineNumbers = false;
	_editorBookmarkPos = true;
	_editorFindpicksel = true;
	_editorFindpicktext = true;
	_editorPersistentFindReplace = false;
	_editorFindReplaceRestorePos = false;
	_editorAutoEnclose = true;
	
		// stuff to speed up loading of packages and libraries
	// after initial load
	packagesLoaded = false;
	librariesLoaded = false;

}

// xml support
void SettingsClass::Xmlize(XmlIO &xml)
{
	xml
		("SketchFolder"							, _sketchFolder)
	;
	if(xml.IsLoading())
	{
		Vector<String> examplesURLs;
		Vector<String> packagesURLs;
		Vector<String> librariesURLs;
		xml
			("ExamplesURLs"		, examplesURLs)
			("PackageURLs"		, packagesURLs)
			("LibrariesURLs"	, librariesURLs)
		;
		// check if we shall force loading of examples
		if(examplesLoaded)
		{
			if(examplesURLs.GetCount() != _examplesURLs.GetCount())
				examplesLoaded = false;
			else
			{
				for(int i = 0; i < examplesURLs.GetCount(); i++)
					if(examplesURLs[i] != _examplesURLs.GetKey(i))
					{
						examplesLoaded = false;
						break;
					}
			}
		}
		if(!examplesLoaded)
		{
			_examplesURLs.Clear();
			for(int i = 0; i < examplesURLs.GetCount(); i++)
				_examplesURLs.Add(examplesURLs[i]);
		}
		
		
		// check if we shall force loading of packages
		if(packagesLoaded)
		{
			if(packagesURLs.GetCount() != _packagesURLs.GetCount())
				packagesLoaded = false;
			else
			{
				for(int i = 0; i < packagesURLs.GetCount(); i++)
					if(packagesURLs[i] != _packagesURLs.GetKey(i))
					{
						packagesLoaded = false;
						break;
					}
			}
		}
		if(!packagesLoaded)
		{
			_packagesURLs.Clear();
			for(int i = 0; i < packagesURLs.GetCount(); i++)
				_packagesURLs.Add(packagesURLs[i]);
		}

		// check if we shall force loading of libraries
		if(librariesLoaded)
		{
			if(librariesURLs.GetCount() != _librariesURLs.GetCount())
				librariesLoaded = false;
			else
			{
				for(int i = 0; i < librariesURLs.GetCount(); i++)
					if(librariesURLs[i] != _librariesURLs.GetKey(i))
					{
						librariesLoaded = false;
						break;
					}
			}
		}
		if(!librariesLoaded)
		{
			_librariesURLs.Clear();
			for(int i = 0; i < librariesURLs.GetCount(); i++)
				_librariesURLs.Add(librariesURLs[i]);
		}
	}
	else
	{
		Vector<String> examplesURLs(_examplesURLs.GetKeys(), 1);
		Vector<String> packagesURLs(_packagesURLs.GetKeys(), 1);
		Vector<String> librariesURLs(_librariesURLs.GetKeys(), 1);
		xml
			("ExamplesURLs"		, examplesURLs)
			("PackageURLs"		, packagesURLs)
			("LibrariesURLs"	, librariesURLs)
		;
	}
	xml
		("ActiveBoard"							, _activeBoard)
		("ActiveBoardMenus"						, _activeBoardMenus)
		("ActiveDevice"							, _device)

		("CompilerVerbosity"					, _compilerVerbosity)
		("CompilerWarnLevel"					, _compilerWarnLevel)
		("CompilerUserWarnings"					, _compilerUserWarnings)
		("CompilerOptimizations"				, _compilerOptimizations, 1)
		("CompilerFullRebuild"					, _compilerFullRebuild)
		("CompilerAutoLibDependencies"			, _compilerAutoLibDependencies, true)
		("UploaderVerbosity"					, _uploaderVerbosity)
		("IdeCreateProjectStubs"				, _ideCreateProjectStubs)
		("IdeCreateLibraryStubs"				, _ideCreateLibraryStubs)
		("IdeRememberFiles"						, _ideRememberFiles)
		("IdeRememberProjects"					, _ideRememberProjects)
		("IdeSysLibsReadOnly"					, _ideSysLibsReadOnly)
		("IdeShowSplash"						, _ideShowSplash)

		("AStyleBracketIndent"					, _astyleBracketIndent)
		("AStyleNamespaceIndent"				, _astyleNamespaceIndent)
		("AStyleBlockIndent"					, _astyleBlockIndent)
		("AStyleCaseIndent"						, _astyleCaseIndent)
		("AStyleClassIndent"					, _astyleClassIndent)
		("AStyleLabelIndent"					, _astyleLabelIndent)
		("AStyleSwitchIndent"					, _astyleSwitchIndent)
		("AStylePreprocessorIndent"				, _astylePreprocessorIndent)
		("AStyleMinInStatementIndentLength"		, _astyleMinInStatementIndentLength)
		("AStyleMaxInStatementIndentLength"		, _astyleMaxInStatementIndentLength)
		("AStyleBreakClosingHeaderBracketsMode"	, _astyleBreakClosingHeaderBracketsMode)
		("AStyleBreakElseIfsMode"				, _astyleBreakElseIfsMode)
		("AStyleBreakOneLineBlocksMode"			, _astyleBreakOneLineBlocksMode)
		("AStyleSingleStatementsMode"			, _astyleSingleStatementsMode)
		("AStyleBreakBlocksMode"				, _astyleBreakBlocksMode)
		("AStyleBreakClosingHeaderBlocksMode"	, _astyleBreakClosingHeaderBlocksMode)
		("AStyleBracketFormatMode"				, _astyleBracketFormatMode)
		("AStyleParensPaddingMode"				, _astyleParensPaddingMode)
		("AStyleParensUnPaddingMode"			, _astyleParensUnPaddingMode)
		("AStyleOperatorPaddingMode"			, _astyleOperatorPaddingMode)
		("AStyleEmptyLineFill"					, _astyleEmptyLineFill)
		("AStyleTabSpaceConversionMode"			, _astyleTabSpaceConversionMode)

		("EditorTabsize"						, _editorTabsize)
		("EditorIndentAmount"					, _editorIndentAmount)
		("EditorShowTabs"						, _editorShowTabs)
		("EditorIndentSpaces"					, _editorIndentSpaces)
		("EditorNoParenthesisIndent"			, _editorNoParenthesisIndent)
		("EditorShowLineNumbers"				, _editorShowLineNumbers)
		("EditorBookmarkPos"					, _editorBookmarkPos)
		("EditorFindpicksel"					, _editorFindpicksel)
		("EditorFindpicktext"					, _editorFindpicktext)
		("EditorPersistentFindReplace"			, _editorPersistentFindReplace)
		("EditorFindReplaceRestorePos"			, _editorFindReplaceRestorePos)
		("EditorAutoEnclose"					, _editorAutoEnclose)
	;

	if(xml.IsLoading())
	{
		// if examples URLs is empty, insert the default Fishino one
		if(_examplesURLs.IsEmpty())
		{
			_examplesURLs.Add("http://www.fishino.it/arduinoide/example_fishino_index.json", "");
			examplesLoaded = false;
		}
		
		// if packages urls is empty, insert a couple of them
		if(_packagesURLs.IsEmpty())
		{
			_packagesURLs.Add("http://downloads.arduino.cc/packages/package_index.json", "");
			_packagesURLs.Add("http://www.fishino.it/arduinoide/package_fishino_index.json", "");
			_packagesURLs.Add("http://arduino.esp8266.com/stable/package_esp8266com_index.json", "");
			packagesLoaded = false;
		}
		
		// if libraries URLs is empty, insert the default Arduino one
		if(_librariesURLs.IsEmpty())
		{
			_librariesURLs.Add("http://downloads.arduino.cc/libraries/library_index.json", "");
			_librariesURLs.Add("http://www.fishino.it/arduinoide/library_fishino_index.json", "");
			librariesLoaded = false;
		}
		else if(_librariesURLs.GetCount() == 1)
		{
			FindFile ff(AppendFileName(GetConfigPath(), "FishIDE.cfg"));
			if(ff)
			{
				Date cfDate = Time(ff.GetLastWriteTime());
				Date d(2017, 04, 12);
				if(cfDate < d)
					_librariesURLs.Add("http://www.fishino.it/arduinoide/library_fishino_index.json", "");
			}
			librariesLoaded = false;
		}
		
		// refresh examples
		RefreshExamples();
		
		// refresh packages marking installed ones
		// and rescanning boards
		RefreshPackages();
		
		// refreash librares
		RefreshLibraries();
		
		// load stored boards menu items
		VectorMap<String, VectorMap<String, int> >storedMenuItems;
		xml("MenuItems", storedMenuItems);
		
		// pre-select all menu items as got from stored ones
		for(int iBoard = 0; iBoard < storedMenuItems.GetCount(); iBoard++)
		{
			String boardName = storedMenuItems.GetKey(iBoard);
			int i = _availableBoards.Find(boardName);
			if(i < 0)
				continue;
			Board *board = _availableBoards[i];
			board->SetSelectedMenuItems(storedMenuItems[iBoard]);
		}
	}
	else
	{
		// save selected menu items
		VectorMap<String, VectorMap<String, int> >storedMenuItems;
		for(int iBoard = 0; iBoard < _availableBoards.GetCount(); iBoard++)
		{
			Board const *board = _availableBoards[iBoard];
			storedMenuItems.Add(board->GetName(), board->GetSelectedMenuItems());
		}
		xml("MenuItems", storedMenuItems);
	}
}

// refresh examples -- needed upon loading or settings changed
void SettingsClass::RefreshExamples(bool force)
{
	if(force)
		examplesLoaded = false;
	
	// refresh packages json contents from URLs
	RefreshExamplesJSONs();
}

// refresh packages -- needed upon loading or settings changed
void SettingsClass::RefreshPackages(bool force)
{
	if(force)
		packagesLoaded = false;
	
	// refresh packages json contents from URLs
	RefreshPackagesJSONs();
	
	// mark installed packages and tools
	MarkInstalled();
	
	// scan available boards
	scanBoards();
}

// refresh installed platforms and tools
void SettingsClass::RefreshInstalled(void)
{
	for(int iPackage = 0; iPackage < Packages.GetCount(); iPackage++)
	{
		Package &package = Packages[iPackage];
		Array<Platform> &platforms = package.GetPlatforms();
		for(int iPlatform = 0; iPlatform < platforms.GetCount(); iPlatform++)
		{
			Platform &platform = platforms[iPlatform];
			if(platform.IsInstalled())
			{
				// parse platform board file
				platform.GetActiveVersion()->ParseBoardsFile();
				
				// parse platform file
				platform.GetActiveVersion()->ParsePlatformFile();
			}
		}
	}

	scanBoards();
}

// mark installed platforms and tools upon loading config
void SettingsClass::MarkInstalled(void)
{
	for(int iPackage = 0; iPackage < Packages.GetCount(); iPackage++)
	{
		Package &package = Packages[iPackage];
		Array<Platform> &platforms = package.GetPlatforms();
		for(int iPlatform = 0; iPlatform < platforms.GetCount(); iPlatform++)
		{
			Platform &platform = platforms[iPlatform];

			// @@ maybe we should do some sanity check here....
			
			platform._installed = false;
			ArrayMap<String, PlatformVersion> &versions = platform.GetVersions();
			
			for(int iVersion = 0; iVersion < versions.GetCount(); iVersion++)
			{
				// now check if main folder is still there
				PlatformVersion &platformVersion = versions[iVersion];
			
				// get platform local folder
				String localFolder = platformVersion.GetLocalFolder();
			
				// we shall check if folders and important files are there
				if(!DirectoryExists(localFolder))
					continue;
				if(!FileExists(AppendFileName(localFolder, "boards.txt")))
					continue;
				if(!FileExists(AppendFileName(localFolder, "platform.txt")))
					continue;
			
				platform._installed = true;
				platform._installedVersion = platformVersion.GetVersion();
			
				// parse platform board file
				platform.GetActiveVersion()->ParseBoardsFile();

				// parse platform file
				platform.GetActiveVersion()->ParsePlatformFile();
				
				// stop on first version found (it should be the only one!)
				break;
			}
		}
	
		Array<Tool>  &tools = package.GetTools();
		for(int iTool = 0; iTool < tools.GetCount(); iTool++)
		{
			Tool &tool = tools[iTool];
			tool._installed = false;
			
			// check if tool local folder is there
			String localFolder = tool.GetLocalFolder();
			if(DirectoryExists(localFolder))
				tool._installed = true;
		}
	}
}

// refresh examples jsons
void SettingsClass::RefreshExamplesJSONs(void)
{
	if(examplesLoaded)
		return;
	
	for(int i = 0; i < _examplesURLs.GetCount(); i++)
	{
		String url = _examplesURLs.GetKey(i);
		String json = NetTools::Download(url, 2000);
		if(json != "")
		{
			_examplesURLs.Get(url) = json;
			SaveFile(AppendFileName(GetConfigPath(), GetFileName(url)), json);
		}
		else
			_examplesURLs.Get(url) = LoadFile(AppendFileName(GetConfigPath(), GetFileName(url)));
	}

	// reload libraries data
	Examples.Clear();
	for(int i = 0; i < _examplesURLs.GetCount(); i++)
		Examples.ParseJSON(_examplesURLs[i]);
	
	examplesLoaded = true;
}
		

// refresh packages jsons
void SettingsClass::RefreshPackagesJSONs(void)
{
	if(packagesLoaded)
		return;
	
	for(int i = 0; i < _packagesURLs.GetCount(); i++)
	{
		String url = _packagesURLs.GetKey(i);
		String json = NetTools::Download(url, 2000);
		if(json != "")
		{
			_packagesURLs.Get(url) = json;
			SaveFile(AppendFileName(GetConfigPath(), GetFileName(url)), json);
		}
		else
			_packagesURLs.Get(url) = LoadFile(AppendFileName(GetConfigPath(), GetFileName(url)));
	}
	
	// after refreshing, we need to rebuild packages descriptors
	Packages.Clear();
	for(int i = 0; i < _packagesURLs.GetCount(); i++)
		Packages.ParseJSON(_packagesURLs[i]);

	packagesLoaded = true;
}

// refresh libraries jsons
void SettingsClass::RefreshLibrariesJSONs(void)
{
	if(librariesLoaded)
		return;
	
	for(int i = 0; i < _librariesURLs.GetCount(); i++)
	{
		String url = _librariesURLs.GetKey(i);
		String json = NetTools::Download(url, 2000);
		if(json != "")
		{
			_librariesURLs.Get(url) = json;
			SaveFile(AppendFileName(GetConfigPath(), GetFileName(url)), json);
		}
		else
			_librariesURLs.Get(url) = LoadFile(AppendFileName(GetConfigPath(), GetFileName(url)));
	}

	// reload libraries data
	Libraries.Clear();
	for(int i = 0; i < _librariesURLs.GetCount(); i++)
		Libraries.ParseJSON(_librariesURLs[i]);
	Libraries.ScanLocal();
	
	librariesLoaded = true;
}

// refresh libraries
void SettingsClass::RefreshLibraries(bool force)
{
	if(force)
		librariesLoaded = false;
	
	// refresh JSON packages
	RefreshLibrariesJSONs();
	
	// rescan libraries
	LibraryPool.Scan();

//	RLOG("WE SHALL CHECK FOR LIBRARY UPDATES HERE");
}


// get package urls in string format (\n terminates each url)
String SettingsClass::GetPackagesURLs(void) const
{
	String res;
	for(int i = 0; i < _packagesURLs.GetCount(); i++)
		res += _packagesURLs.GetKey(i) + "\n";
	if(res.GetCount())
		res = res.Left(res.GetCount() - 1);
	return res;
}

SettingsClass &SettingsClass::SetPackagesURLs(String const &s)
{
	Vector<String> ar = Split(s, "\n");
	
	// preserve previous JSON contents, if any
	VectorMap<String, String> newURLs;
	
	for(int i = 0; i < ar.GetCount(); i++)
	{
		String l = TrimBoth(ar[i]);
		if(!l.IsEmpty())
		{
			String json;
			if(_packagesURLs.Find(l) >= 0)
				json = _packagesURLs.Get(l);
			newURLs.Add(l, json);
		}
	}
	
	_packagesURLs = pick(newURLs);
	return *this;
}

// get examples urls in string format (\n terminates each url)
String SettingsClass::GetExamplesURLs(void) const
{
	String res;
	for(int i = 0; i < _examplesURLs.GetCount(); i++)
		res += _examplesURLs.GetKey(i) + "\n";
	if(res.GetCount())
		res = res.Left(res.GetCount() - 1);
	return res;
}

SettingsClass &SettingsClass::SetExamplesURLs(String const &s)
{
	Vector<String> ar = Split(s, "\n");
	
	// preserve previous JSON contents, if any
	VectorMap<String, String> newURLs;
	
	for(int i = 0; i < ar.GetCount(); i++)
	{
		String l = TrimBoth(ar[i]);
		if(!l.IsEmpty())
		{
			String json;
			if(_examplesURLs.Find(l) >= 0)
				json = _examplesURLs.Get(l);
			newURLs.Add(l, json);
		}
	}
	
	_examplesURLs = pick(newURLs);
	return *this;
}

// get libraries urls in string format (\n terminates each url)
String SettingsClass::GetLibrariesURLs(void) const
{
	String res;
	for(int i = 0; i < _librariesURLs.GetCount(); i++)
		res += _librariesURLs.GetKey(i) + "\n";
	if(res.GetCount())
		res = res.Left(res.GetCount() - 1);
	return res;
}

SettingsClass &SettingsClass::SetLibrariesURLs(String const &s)
{
	Vector<String> ar = Split(s, "\n");
	
	// preserve previous JSON contents, if any
	VectorMap<String, String> newURLs;
	
	for(int i = 0; i < ar.GetCount(); i++)
	{
		String l = TrimBoth(ar[i]);
		if(!l.IsEmpty())
		{
			String json;
			if(_librariesURLs.Find(l) >= 0)
				json = _librariesURLs.Get(l);
			newURLs.Add(l, json);
		}
	}
	
	_librariesURLs = pick(newURLs);
	return *this;
}

// scan available boards
void SettingsClass::scanBoards(void)
{
	_availableBoards.Clear();
	
	for(int iPackage = 0; iPackage < Packages.GetCount(); iPackage++)
	{
		Package &package = Packages[iPackage];
		Array<Platform> &platforms = package.GetPlatforms();

		for(int iPlatform = 0; iPlatform < platforms.GetCount(); iPlatform++)
		{
			Platform &platform = platforms[iPlatform];
			if(!platform.IsInstalled())
				continue;
			PlatformVersion *version = platform.GetActiveVersion();
			if(!version)
				continue;
			ArrayMap<String, Board> &boards = version->GetBoards();
			for(int iBoard = 0; iBoard < boards.GetCount(); iBoard++)
			{
				String const &name = boards.GetKey(iBoard);
				Board &board = boards[iBoard];
				if(_availableBoards.Find(name) < 0)
					_availableBoards.Add(name, &board);
			}
		}
	}
	
	// fix current board
	if(_availableBoards.Find(_activeBoard) < 0 && !_availableBoards.IsEmpty())
		_activeBoard = _availableBoards.GetKey(0);

	// setup relevant folders
	SetupFolders();
}

// setup lib and core folders
void SettingsClass::SetupFolders(void)
{
	// setup user libraries folder
	_userLibsRoot = AppendFileName(_sketchFolder, "libraries");
	
	// setup system libraries folder
	_systemLibsRoot = AppendFileName(GetConfigPath(), "libraries");

	// setup core root and core libraries folder
	Board *board = GetSelectedBoard();
	if(!board)
		return;
	Platform &platform = board->GetPlatform();
	_architecture = platform.GetArchitecture();
	PlatformVersion *version = platform.GetActiveVersion();
	String platformRoot = version->GetLocalFolder();
	
	// platform libraries location 
	_platformLibsRoot = AppendFileName(platformRoot, "libraries");
	
	// if core libraries doesn't exist, they're probably located
	// inside another referenced core, so search for it...
	if(!DirectoryExists(_platformLibsRoot))
	{
		// check if we reference another package
		int colonPos;
		
		// setup core to build
		String core = board->GetMergedValues().Get("build.core");
		if( (colonPos = core.Find(':')) >= 0)
		{
			// get core from another package
			String vendor = core.Left(colonPos);
			core = core.Mid(colonPos + 1);
			Package *pack = Packages.FindPackage(vendor);
			if(!pack)
				return;
			Platform *plat = pack->FindPlatformByArchitecture(platform.GetArchitecture());
			if(!plat)
				return;
			PlatformVersion *ver = plat->GetActiveVersion();
			if(!ver)
				return;
			String coreRoot = AppendFileName(AppendFileName(ver->GetLocalFolder(), "cores"), core);
			
			_platformLibsRoot = AppendFileName(ver->GetLocalFolder(), "libraries");
		}
	}
	
	// rescan libraries
	LibraryPool.Scan();
	
	// signal a settings change
	WhenChange();
}

// set active board name
SettingsClass &SettingsClass::SetActiveBoard(String const &name)
{
	_activeBoard = name;
	
	// setup relevant folders
	SetupFolders();

	return *this;
}

// compiler options
SettingsClass &SettingsClass::SetCompilerVerbosity(int v)
{
	_compilerVerbosity = v;
	return *this;
}


SettingsClass &SettingsClass::SetCompilerWarnLevel(int v)
{
	_compilerWarnLevel = v;
	return *this;
}

SettingsClass &SettingsClass::SetCompilerUserWarnings(String const &s)
{
	_compilerUserWarnings = s;
	return *this;
}

SettingsClass &SettingsClass::SetCompilerOptimizations(int o)
{
	_compilerOptimizations = o;
	return *this;
}

SettingsClass &SettingsClass::SetCompilerFullRebuild(bool b)
{
	_compilerFullRebuild = b;
	return *this;
}

SettingsClass &SettingsClass::SetCompilerAutoLibDependencies(bool b)
{
	_compilerAutoLibDependencies = b;
	return *this;
}

// uploader options
SettingsClass &SettingsClass::SetUploaderVerbosity(int v)
{
	_uploaderVerbosity = v;
	return *this;
}

// ide options
SettingsClass &SettingsClass::SetIdeCreateProjectStubs(bool b)
{
	_ideCreateProjectStubs = b;
	return *this;
}

SettingsClass &SettingsClass::SetIdeCreateLibraryStubs(bool b)
{
	_ideCreateLibraryStubs = b;
	return *this;
}

SettingsClass &SettingsClass::SetIdeRememberFiles(bool b)
{
	_ideRememberFiles = b;
	return *this;
}

SettingsClass &SettingsClass::SetIdeRememberProjects(bool b)
{
	_ideRememberProjects = b;
	return *this;
}

SettingsClass &SettingsClass::SetIdeSysLibsReadOnly(bool b)
{
	_ideSysLibsReadOnly = b;
	return *this;
}

SettingsClass &SettingsClass::SetIdeShowSplash(bool b)
{
	_ideShowSplash = b;
	return *this;
}

// try to detect sketchbook path. If not found, return a standard path
// depending on OS
String SettingsClass::LocateSketchbook(void) const
{
	String bestFolder;
#ifdef PLATFORM_POSIX
	// look in home folder, for 'arduino' and 'sketchbook' folder
	// give preferences to former and/or one containing a 'libraries' subfolder
	String folder = AppendFileName(GetHomeDirectory(), "arduino");
	if(DirectoryExists(folder))
	{
		if(DirectoryExists(AppendFileName(folder, "libraries")))
			return folder;
		String folder2 = AppendFileName(GetHomeDirectory(), "sketchbook");
		if(!DirectoryExists(folder2))
			return folder;
		if(DirectoryExists(AppendFileName(folder, "libraries")))
			return folder2;
		return folder;
	}
	String folder2 = AppendFileName(GetHomeDirectory(), "sketchbook");
	if(DirectoryExists(folder2))
		return folder2;
	
	// not found, create it and return
	RealizeDirectory(folder);
	return folder;

#else
	// look in documents folder, for 'arduino' and 'sketchbook' folder
	// give preferences to former and/or one containing a 'libraries' subfolder
	String folder = AppendFileName(GetDocumentsFolder(), "Arduino");
	if(DirectoryExists(folder))
	{
		if(DirectoryExists(AppendFileName(folder, "libraries")))
			return folder;
		String folder2 = AppendFileName(GetDocumentsFolder(), "sketchbook");
		if(!DirectoryExists(folder2))
			return folder;
		if(DirectoryExists(AppendFileName(folder, "libraries")))
			return folder2;
		return folder;
	}
	String folder2 = AppendFileName(GetDocumentsFolder(), "sketchbook");
	if(DirectoryExists(folder2))
		return folder2;
	
	// not found, create it and return
	RealizeDirectory(folder);
	return folder;

#endif
}

// get selected board
Board *SettingsClass::GetSelectedBoard(void)
{
	if(_activeBoard.IsEmpty())
		return NULL;
	int i = _availableBoards.Find(_activeBoard);
	if(i >= 0)
		return _availableBoards[i];
	return NULL;
}

String SettingsClass::GetVersion() const
{
	return VERSION.ToString();
}

SettingsClass &__GetSettings(void)
{
	static SettingsClass s;
	return s;
}