#include "ProjectsCtrl.h"

#include "Settings.h"

// constructor
ProjectsCtrl::ProjectsCtrl()
{
	// allow close all tabs
	MinTabCount(0);
	CancelClose = THISBACK(closeTabCb);
	WhenAction = THISBACK(changeCb);
	
	// initial splitters values
	hSplitter = 1500;
	vSplitter = 8000;
	flSplitter = 5000;
}

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

// detect some changes in tabs
void ProjectsCtrl::changeCb(void)
{
	int idx = GetCursor();
	if(idx >= 0)
	{
		projects[idx].SetSplitters(hSplitter, vSplitter, flSplitter);
		projects[idx].Activate();
	}
	WhenChange();
}
	
// check if project exists
bool ProjectsCtrl::CheckExist(String const &path) const
{
	// first look in opened projects
	if(CheckOpened(path))
		return true;
		
	// then check if project is on file
	return FileExists(path);
}

// check if project already opened
bool ProjectsCtrl::CheckOpened(String const &path) const
{
	for(int i = 0; i < projects.GetCount(); i++)
		if(projects[i].GetFullPath() == path)
			return true;
	return false;
}
		
		
// remove control when closing tab
bool ProjectsCtrl::closeTabCb(Value tab)
{
	Project *project = (Project *)GetCtrl(tab);
	if(project)
	{
		project->Save();
		int idx = FindProjectIndex(project);
		RemoveCtrl(tab);
		projects.Remove(idx);
	}
	else
		RemoveCtrl(tab);

	WhenChange();
	return true;
}
	
// finds project index in array
int ProjectsCtrl::FindProjectIndex(Project *proj) const
{
	for(int i = 0; i < projects.GetCount(); i++)
		if(&projects[i] == proj)
			return i;
	return -1;
}

// finds project tab
int ProjectsCtrl::FindProjectTab(Project *proj)
{
	for(int i = 0; i < GetCount(); i++)
		if(GetCtrl(i) == proj)
			return i;
	return -1;
}

// new empty project
bool ProjectsCtrl::NewProject(String const &path, bool createDefault)
{
	if(!RealizeDirectory(GetFileFolder(path)))
		return false;
	projects.Add(new Project(path));
	if(createDefault)
		projects.Top().CreateDefaultContent();
	String name = GetFileTitle(path);
	AddCtrl(projects.Top(), name);
	projects.Top().WhenChange = THISBACK(changeCb);
	projects.Top().SetSplitters(hSplitter, vSplitter, flSplitter);
	projects.Top().WhenSplitter = THISBACK(splitterCb);
	projects.Top().WhenDeletingFile = THISBACK(deletingFileCb);
	return true;
}

// open existing project
bool ProjectsCtrl::OpenProject(String path)
{
	// if an '.ino' file was selected, look if there's a '.fish'
	// in same folder; in case open that one
	if(ToLower(GetFileExt(path)) == ".ino")
	{
		String fishPath = ForceExt(path, ".fish");
		if(FileExists(fishPath))
			path = fishPath;
		else
			return ImportProject(path, fishPath);
	}
	
	// first look if project is already opened
	for(int i = 0; i < projects.GetCount(); i++)
	{
		if(projects[i].GetFullPath() == path)
		{
			SetCtrl(projects[i]);
			projects[i].SetSplitters(hSplitter, vSplitter, flSplitter);
			return true;
		}
	}
	One<Project> proj = new Project(path);
	if(proj->Open())
	{
		String name = GetFileTitle(path);
		projects.Add(proj.Detach());
		AddCtrl(projects.Top(), name);
		projects.Top().WhenChange = THISBACK(changeCb);
		projects.Top().SetSplitters(hSplitter, vSplitter, flSplitter);
		projects.Top().WhenSplitter = THISBACK(splitterCb);
		projects.Top().WhenDeletingFile = THISBACK(deletingFileCb);
		return true;
	}
	return false;
}

// import arduino project
bool ProjectsCtrl::ImportProject(String const &arduPath, String const &path)
{
	// if project already exists, return false
	if(CheckExist(path))
		return false;
	
	One<Project> proj = new Project(path);
	if(proj->Import(arduPath))
	{
		String name = GetFileTitle(path);
		projects.Add(proj.Detach());
		AddCtrl(projects.Top(), name);
		projects.Top().WhenChange = THISBACK(changeCb);
		projects.Top().SetSplitters(hSplitter, vSplitter, flSplitter);
		projects.Top().WhenSplitter = THISBACK(splitterCb);
		projects.Top().WhenDeletingFile = THISBACK(deletingFileCb);
		projects.Top().Save();
		return true;
	}
	return false;
}

// save project
bool ProjectsCtrl::SaveProject(Project *proj)
{
	if(!proj)
		return false;
	return proj->Save();
}

// save project with a new name
bool ProjectsCtrl::SaveProjectAs(Project *proj, String const &path)
{
	if(!proj)
		return false;
	if(!proj->SaveAs(path))
		return false;
	String name = GetFileTitle(path);
	int idx = FindProjectTab(proj);
	Set(idx, name, name);
	return true;
}

// remove current project
bool ProjectsCtrl::RemoveProject(Project *proj)
{
	if(!proj)
		return false;

	Project *curProj = GetCurrentProject();
	bool current = (curProj == proj);

	int idx = FindProjectIndex(proj);
	int iTab = FindProjectTab(proj);
	RemoveCtrl(iTab);
	projects.Remove(idx);

	if(current)
	{
		iTab--;
		if(iTab < 0)
			iTab = GetCount() - 1;
		if(iTab >= 0)
			SetCursor(iTab);
	}
	WhenChange();
	return true;
}

// get current project
Project *ProjectsCtrl::GetCurrentProject(void)
{
	if(!projects.GetCount())
		return NULL;
	return (Project *)GetCurrentCtrl();
}
		
// projects splitters handling
void ProjectsCtrl::splitterCb(int h, int v, int fl)
{
	hSplitter = h;
	vSplitter = v;
	flSplitter = fl;
}

// deleting file event handler
// forward file close to all projects
void ProjectsCtrl::deletingFileCb(String const &path)
{
	for(int i = 0; i < projects.GetCount(); i++)
		projects[i].closeFile(path);
}

// serialization support -- just serializes splitters layouts
void ProjectsCtrl::Serialize(Stream &s)
{
	// serialize splitters positions
	s % hSplitter % vSplitter % flSplitter;
}

// xml support -- stores opened projects
void ProjectsCtrl::Xmlize(XmlIO &xml)
{
	Vector<String> opened;
	String current;
	if(xml.IsStoring())
	{
		// store opened projects upon destruction
		for(int i = 0; i < projects.GetCount(); i++)
			opened << projects[i].GetFullPath();
		Project *prj = GetCurrentProject();
		if(prj)
			current = prj->GetFullPath();
		xml
			("OpenedProjects"		, opened)
			("CurrentProject"		, current)
		;
	}
	else
	{
		xml
			("OpenedProjects"		, opened)
			("CurrentProject"		, current)
		;
		Clear();
		projects.Clear();
		for(int i = 0; i < opened.GetCount(); i++)
		{
			String path = opened[i];
			if(FileExists(path))
				OpenProject(path);
		}
		for(int i = 0; i < projects.GetCount(); i++)
			if(projects[i].GetFullPath() == current)
			{
				SetCursor(i);
				break;
			}
	}
}

// handler for changes in settings
void ProjectsCtrl::SettingsChanged(void)
{
	for(int i = 0; i < projects.GetCount(); i++)
		projects[i].SettingsChanged();
}

// propagate theme to all projects
void ProjectsCtrl::ThemeChanged(Theme const *t)
{
	for(int i = 0; i < projects.GetCount(); i++)
		projects[i].ThemeChanged(t);
}
