#include "IdeEnvironment.h"

#include "Settings.h"
#include "VarProcessor.h"

#include "FishIDE.h"


// constructor
IdeEnvironment::IdeEnvironment()
{
	_project = NULL;
	_board = NULL;
	_platform = NULL;
	_version = NULL;
	
	// patterns for source file scans
	_sourcesPattern << "*.c" << "*.cpp" << "*.cxx" << "*.S";
	_includesPattern << "*.h" << "*.hpp" << "*.hxx";
	_objectsPattern << "*.o";
}

// destructor
IdeEnvironment::~IdeEnvironment()
{
}

// setup environment for build/upload
bool IdeEnvironment::Setup(Project *project)
{
	// store project
	_project = project;
	
	_board = NULL;
	_platform = NULL;
	_version = NULL;
	
	// clear proctools output
	_procTools.Clear();
	
	// get current board
	Board *board = Settings.GetSelectedBoard();
	if(!board)
		return false;
	
	// get current platform
	Platform &platform = board->GetPlatform();
	if(!platform.IsInstalled())
		return false;
	
	// get current platform version
	PlatformVersion *version = platform.GetActiveVersion();
	if(!version)
		return false;
	
	_board = board;
	_platform = &platform;
	_version = version;
	
	// setup build path
	_buildPath = GetConfigPath();
	_buildPath = AppendFileName(_buildPath, "BUILD");
	_buildPath = AppendFileName(_buildPath, _platform->GetArchitecture());
	_buildPath = AppendFileName(_buildPath, _board->GetName());
	_buildPath = AppendFileName(_buildPath, _project->GetTitle());
	
	// current architecture
	_architecture = _platform->GetArchitecture();
		
	// get platform root
	_platformRoot = _version->GetLocalFolder();

	// get platform variables
	VectorMap<String, String> platformVars = VectorMap<String, String>(_version->GetValues(), 1);
	
	// get board variables
	VectorMap<String, String>  boardVars = _board->GetMergedValues();
	
	// tools vars
	VectorMap<String, String> toolsVars = _version->GetToolsValues();
	
	// platform libraries location 
	_platformLibsRoot = AppendFileName(_platformRoot, "libraries");

	// user libraries path
	_userLibsRoot = AppendFileName(Settings.GetSketchFolder(), "libraries");

	// check if we reference another package
	int colonPos;
	
	// setup core to build
	_core = boardVars.Get("build.core");
	if( (colonPos = _core.Find(':')) >= 0)
	{
		// get core from another package
		String vendor = _core.Left(colonPos);
		_core = _core.Mid(colonPos + 1);
		boardVars.Get("build.core") = _core;
		Package *pack = Packages.FindPackage(vendor);
		if(!pack)
			return false;
		Platform *plat = pack->FindPlatformByArchitecture(_architecture);
		if(!plat)
			return false;
		PlatformVersion *ver = plat->GetActiveVersion();
		if(!ver)
			return false;
		_coreRoot = AppendFileName(AppendFileName(ver->GetLocalFolder(), "cores"), _core);
		
		// if we use another core, we probably need its platform variables
		// so we merge with our ones; ours take precedence
		VectorMap<String, String>tmpVars = pick(platformVars);
		platformVars = VectorMap<String, String>(ver->GetValues(), 1);
		VarProcessor::MergeVars(platformVars, tmpVars);
		
		// ... and we need that core tools too
		// here we prefere core tools to ours
		VarProcessor::MergeVars(toolsVars, ver->GetToolsValues());
		
		// ... and probably we haven't libraries here either
		if(!DirectoryExists(_platformLibsRoot))
			_platformLibsRoot = AppendFileName(ver->GetLocalFolder(), "libraries");
	}
	else
	{
		_coreRoot = AppendFileName(AppendFileName(_platformRoot, "cores"), _core);
	}

	// setup variant to build
	_variant = boardVars.Get("build.variant");
	if( (colonPos = _variant.Find(':')) >= 0)
	{
		// get variant from another package
		String vendor = _core.Left(colonPos);
		_variant = _variant.Mid(colonPos + 1);
		boardVars.Get("build.variant") = _variant;
		Package *pack = Packages.FindPackage(vendor);
		if(!pack)
			return false;
		Platform *plat = pack->FindPlatformByArchitecture(_platform->GetArchitecture());
		if(!plat)
			return false;
		PlatformVersion *ver = plat->GetActiveVersion();
		if(!ver)
			return false;
		_variantRoot = AppendFileName(AppendFileName(ver->GetLocalFolder(), "variants"), _variant);
	}
	else
	{
		_variantRoot = AppendFileName(AppendFileName(_platformRoot, "variants"), _variant);
	}
//	DUMP(boardVars);
	
	// now it's time to merge all variables
	_variables = pick(platformVars);
	VarProcessor::MergeVars(_variables, toolsVars);
	VarProcessor::MergeVars(_variables, boardVars);

	// appendo some constants
	_variables.GetAdd("build.core.path") = _coreRoot;
	_variables.GetAdd("build.variant.path") = _variantRoot;

	// append ide version
	VarProcessor::MergeVars(_variables, "runtime.ide.version", VERSION.ToStringUndotted());
	
	// append build path
	VarProcessor::MergeVars(_variables, "build.path", _buildPath);
	
	// remove hard.coded 'arduino.ar' file, replacing with variable '{archive_file}
	// some boards (esp8266) use hard-coded name in ar recipe
	for(int iVar = 0; iVar < _variables.GetCount(); iVar++)
	{
		String &var = _variables[iVar];
		if(var.Find("arduino.ar") >= 0)
			var.Replace("arduino.ar", "{archive_file}");
	}

	// append project name
	VarProcessor::MergeVars(_variables, "build.project_name", _project->GetTitle());
	
	// full rebuild flag
	_fullRebuild = Settings.GetCompilerFullRebuild();
	
	// verbosity levels
	_compilerVerbosity = Settings.GetCompilerVerbosity();
	_uploaderVerbosity = Settings.GetUploaderVerbosity();

	// warning level
	_compilerWarningLevel = Settings.GetCompilerWarnLevel();
	String _compilerUserWarnings = Settings.GetCompilerUserWarnings();
	
	// setup warning level
	switch(_compilerWarningLevel)
	{
		case 0:
		default:
			_variables.Get("compiler.warning_flags") = _variables.Get("compiler.warning_flags.none", "");
			break;

		case 1:
			_variables.Get("compiler.warning_flags") = _variables.Get("compiler.warning_flags.more", "");
			break;

		case 2:
			_variables.Get("compiler.warning_flags") = _variables.Get("compiler.warning_flags.all", "");
			break;

		case 3:
			_variables.Get("compiler.warning_flags") = _variables.Get("compiler.warning_flags.all", "") + " -Wpedantic";
			break;

		case 4:
			_variables.Get("compiler.warning_flags") = _compilerUserWarnings;
			break;
	}
	
	// optimization level
	_compilerOptimizations = Settings.GetCompilerOptimizations();
	
	// setup optimization level
	_variables.GetAdd("compiler.optimization_flags");
	switch(_compilerOptimizations)
	{
		case 0:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.none", "");
			break;

		default:
		case 1:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.normal", "");
			break;

		case 2:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.low", "");
			break;

		case 3:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.medium", "");
			break;

		case 4:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.high", "");
			break;

		case 5:
			_variables.Get("compiler.optimization_flags") = _variables.Get("compiler.optimization_flags.size", "");
			break;
	}
	
	return true;
	
}
