#include "IdeFilePool.h"
#include "FileInfo.h"

#include "Settings.h"

namespace Upp
{
	inline unsigned GetHashValue(Ptr<FileInfo> x)
	{
		return GetHashValue((FileInfo *)x);
	}
}

// constructor
IdeFile::IdeFile(String const &path)
{
	_error = false;
	_path = path;
	_modified = false;
	_readOnly = false;
	
	if(FileExists(_path))
	{
		_content = LoadFile(path);
		if(_content.IsVoid())
		{
			_error = true;
			_content = "";
		}
		_fileTime = GetFileTime(_path);

	}
	else
	{
		// check if file can be created
		FileOut f(_path);
		if(!f)
			_error = true;
		else
			FileDelete(_path);
		_fileTime = GetSysTime().AsFileTime();
	}
	
	// start with no references
	_references.Clear();
}

// destructor
IdeFile::~IdeFile()
{
	if(_modified)
		Save();
}

// add-remove reference
int IdeFile::AddRef(Ptr<FileInfo> owner)
{
	_references.Add(owner);
	return _references.GetCount();
}

int IdeFile::RemRef(Ptr<FileInfo> owner)
{
	if(_references.IsEmpty())
		return 0;
	int idx = _references.Find(owner);
	if(idx >= 0)
		_references.Remove(idx);
	return _references.GetCount();
}

// set/update content
void IdeFile::Set(String const &s)
{
	if(_content != s)
	{
		_content = s;
		_modified = true;
	}
}

// reload content from disk
// and signals event to attached FileInfos
bool IdeFile::Reload(void)
{
	if(FileExists(_path))
	{
		_content = LoadFile(_path);
		if(_content.IsVoid())
		{
			_error = true;
			_content = "";
		}
		_fileTime = GetFileTime(_path);
		
		// signal all clients that we're been reloaded
		for(int i = 0; i < _references.GetCount(); i++)
		{
			if(_references[i])
				_references[i]->Reloaded();
		}
		return true;
	}
	return false;
}

// check if file has been changed on disk since last save
bool IdeFile::CheckFileChanges(void)
{
	return _fileTime < GetFileTime(_path);
}

// save file
bool IdeFile::SaveAs(String const &path)
{
	if(!_modified && path == _path)
		return true;

	_path = path;
	FileOut f(_path);
	if(!f)
		return false;
	f << _content;
	f.Close();
	_modified = false;
	_fileTime = GetFileTime(_path);
	return true;
}

bool IdeFile::Save(void)
{
	if(!_modified)
		return true;

	FileOut f(_path);
	if(!f)
		return false;
	f << _content;
	f.Close();
	_modified = false;
	_fileTime = GetFileTime(_path);
	return true;
}

// rename
bool IdeFile::Rename(String const &newName)
{
	// create the new full path
	String newPath = AppendFileName(GetFileDirectory(_path), newName);

	// if new name already exists, fail
	if(FileExists(newPath))
		return false;
	
	// rename the file
	FileMove(_path, newPath);
	_path = newPath;
	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////

// constructor
IdeFilePoolClass::IdeFilePoolClass()
{
}

// destructor
IdeFilePoolClass::~IdeFilePoolClass()
{
	// save all editor contents upon exit
	SaveAll();
}

// get an editor from pool, or load/create it if not there
IdeFile *IdeFilePoolClass::GetIdeFile(Ptr<FileInfo> owner, String const &path)
{
	// first look at editor from served ones
	for(int i = 0; i < _ideFiles.GetCount(); i++)
	{
		IdeFile &e = _ideFiles[i];
		if(path == e.GetPath())
		{
			// found, add a reference and return it
			e.AddRef(owner);
			return &e;
		}
	}
	
	// try to create it
	IdeFile *e = new IdeFile(path);
	
	// if error, release it and return NULL
	if(!e || e->IsError())
	{
		if(e)
			delete e;
		return NULL;
	}
	
	// all ok, add a reference and return it
	e->AddRef(owner);
	_ideFiles.Add(e);
	return e;
}

// releases an editor
void IdeFilePoolClass::ReleaseIdeFile(Ptr<FileInfo> owner, IdeFile *editor)
{
	for(int i = 0; i < _ideFiles.GetCount(); i++)
	{
		if(&_ideFiles[i] == editor)
		{
			//  found, release a reference
			if(!editor->RemRef(owner))
				_ideFiles.Remove(i);
			
			return;
		}
	}
}

// check if a path is opened inside pool
bool IdeFilePoolClass::IsOpened(String const &path) const
{
	for(int i = 0; i < _ideFiles.GetCount(); i++)
		if(_ideFiles[i].GetPath() == path)
			return true;
	return false;
}


// save all codeeditors in pool
void IdeFilePoolClass::SaveAll(void)
{
	for(int i = 0; i < _ideFiles.GetCount(); i++)
		_ideFiles[i].Save();
}

// get an array with all paths inside pool
Vector<String> IdeFilePoolClass::GetPaths(void) const
{
	Vector<String> res;
	for(int i = 0; i < _ideFiles.GetCount(); i++)
		res.Add(_ideFiles[i].GetPath());
	return res;
}

IdeFilePoolClass &__GetIdeFilePool(void)
{
	static IdeFilePoolClass pool;
	return pool;
}