#include "LibraryPool.h"

#include "FileTools.h"
#include "Settings.h"
#include "SourceTools.h"

// constructor
LibraryPoolClass::LibraryPoolClass()
{
}

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

// scan a folder for libraries
bool LibraryPoolClass::ScanFolder(String const &path, LibraryInfo::Kind kind)
{
	Vector<String> folders = FileTools::ScanFolder(path, "*", true);
	for(int iFolder = 0; iFolder < folders.GetCount(); iFolder++)
	{
		LibraryInfo info(AppendFileName(path, folders[iFolder]));
		if(info.IsEmpty())
			continue;
		
		// if we're scanning platform libs folder, we force the architecture
		// to platform's one
		if(kind == LibraryInfo::PLATFORM)
			info.SetArchitectures(Vector<String>() << Settings.GetArchitecture());
		
		// if not given by properties, scan the library includes
		// if we've an include with same name of class, use it
		// otherwise use all of them
		if(info.GetIncludes().IsEmpty())
		{
			Vector<String> incs = info.ScanIncludes(false);
			Vector<String> myIncs;
			for(int i = 0; i < incs.GetCount(); i++)
			{
				if(GetFileTitle(incs[i]) == info.GetName())
				{
					myIncs << incs[i];
					break;
				}
			}
			if(myIncs.IsEmpty())
				info.SetIncludes(incs);
			else
				info.SetIncludes(myIncs);
		}
		
		// set library kind
		info.SetKind(kind);
		
		// append the new library
		_libraries.Add(pick(info));
	}
	return true;
}

namespace Upp
{
	dword GetHashValue(Ptr<LibraryInfo> const &info) { return GetHashValue((void *)~info); }
};

/*
// build first-level dependencies for a library
Vector<Ptr<LibraryInfo>> LibraryPoolClass::LibraryDependencies(Ptr<LibraryInfo> const &lib, String const &architecture)
{
//	RLOG("\nLIBRARY : " << lib->GetName());
	Index<Ptr<LibraryInfo>> res;
	
	// get library include files
	Vector<String> files = lib->ScanIncludes(true);
	
	// get a copy into an index, stripping paths
	Index<String> myIncludes;
	for(int i = 0; i < files.GetCount(); i++)
		myIncludes.Add(GetFileName(files[i]));
//	DUMP(myIncludes);
		
	// get library source files
	Vector<String> sources = lib->ScanSources(true);
//	DUMP(sources);
	
	// append files which are NOT into utility subfolder
	// which should not use external libraries
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String source = sources[i];
		if(GetFileName(GetFileFolder(source)) != "utility")
			files.Add(source);
	}
//	DUMP(files);

	// get all include files
	Vector<String> includes = SourceTools::ScanIncludes(files);
//	DUMP(includes);
	
	// remove own ones and includes having a path inside them
	// which are considered internal ones (i.e. utility/xxxxx.h)
	Vector<String> cleanIncludes;
	for(int i = 0; i < includes.GetCount(); i++)
	{
		String inc = UnixPath(includes[i]);
		if(inc.Find('/') < 0 && myIncludes.Find(inc) < 0)
			cleanIncludes.Add(inc);
	}
//	DUMP(cleanIncludes);

	// now find corresponding libraries and add to dependency list
	Vector<Ptr<LibraryInfo>> deps = FindByIncludes(cleanIncludes, architecture);
	for(int iDep = 0; iDep < deps.GetCount(); iDep++)
		if(deps[iDep] != lib)
			res.FindAdd(~deps[iDep]);

	return res.PickKeys();
}
*/

// build first-level dependencies for a library
Vector<Ptr<LibraryInfo>> LibraryPoolClass::LibraryDependencies(Ptr<LibraryInfo> const &lib, String const &architecture)
{
//	RLOG("\nLIBRARY : " << lib->GetName());
	
	String sourcesRoot = lib->GetSourcesRoot();
//	RLOG("PATH : " << sourcesRoot);
	Index<Ptr<LibraryInfo>> res;
	
	// get library include files
	Vector<String> files = lib->ScanIncludes(true);
	
	// append library source files
	files.Append(lib->ScanSources(true));
//	DUMP(files);
	
	// get all include files -- we must keep subpaths here
	// in order to be able to filter out includes of same lib
	Vector<String>includes;
	for(int iFile = 0; iFile < files.GetCount(); iFile++)
	{
		String file = files[iFile];
		String subPath = GetFileFolder(file).Mid(sourcesRoot.GetCount());
		Vector<String> fileIncludes = SourceTools::ScanIncludes(file);
		for(int iInc = 0; iInc < fileIncludes.GetCount(); iInc++)
			includes.Add(AppendFileName(subPath, fileIncludes[iInc]));
	}
	
	// remove includes of same library -- it happens that similar libs
	// have similar includes...
	Vector<String> cleanIncludes;
	for(int i = 0; i < includes.GetCount(); i++)
	{
		String inc = includes[i];
		String thisInc = AppendFileName(sourcesRoot, inc);
		if(FileExists(thisInc))
		{
//			RLOG("SKIPPING " << thisInc);
			continue;
		}
		inc = GetFileName(inc);
//		RLOG("ADDING " << inc);
		cleanIncludes.Add(inc);
	}
//	DUMP(cleanIncludes);

	// now find corresponding libraries and add to dependency list
	Vector<Ptr<LibraryInfo>> deps = FindByIncludes(cleanIncludes, architecture);
	for(int iDep = 0; iDep < deps.GetCount(); iDep++)
		if(deps[iDep] != lib)
			res.FindAdd(~deps[iDep]);

/*
RLOG("DEPENDENT LIBRARIES:");
for(int i = 0; i < res.GetCount(); i++)
	RLOG(res[i]->GetName());
*/
	
	return res.PickKeys();
}

// given a list of libraries, add to it dependent ones
Vector<Ptr<LibraryInfo>> LibraryPoolClass::SolveDependencies(Vector<Ptr<LibraryInfo>> const &libs, String const &architecture)
{
	// first copy source libraries into result
	Index<Ptr<LibraryInfo>> res(libs, 1);
	
	// then to a scan of libraries, adding dependencies at end
	// for this a simple loop should be enough as index will be grown at end
	// as dependencies are found
	for(int iLib = 0; iLib < res.GetCount(); iLib++)
	{
		Ptr<LibraryInfo> lib = res[iLib];
		Vector<Ptr<LibraryInfo>> deps = LibraryDependencies(lib, architecture);
		for(int iDep = 0; iDep < deps.GetCount(); iDep++)
			res.FindAdd(deps[iDep]);
	}
	return res.PickKeys();
}


// rescan all available libraries
bool LibraryPoolClass::Scan(void)
{
	_libraries.Clear();
	
	// first scan user libs
	ScanFolder(Settings.GetUserLibsRoot(), LibraryInfo::USER);
	
	// then scan system libs
	ScanFolder(Settings.GetSystemLibsRoot(), LibraryInfo::SYSTEM);
	
	// and finally scan platform libs
	ScanFolder(Settings.GetPlatformLibsRoot(), LibraryInfo::PLATFORM);

	// scan all available architectures
	Index<String> availableArchitectures;
	for(int i = 0; i < _libraries.GetCount(); i++)
	{
		LibraryInfo const &info = _libraries[i];
		if(info.SupportsAnyArchitecture())
			continue;
		Vector<String> const &archs = info.GetArchitectures();
		for(int iArch = 0; iArch < archs.GetCount(); iArch++)
			availableArchitectures.FindAdd(archs[iArch]);
	}

	// now create the include and name maps
	_includeMap.Clear();
	_nameMap.Clear();
	ArrayMap<String, int> &anyArchIncludeMap = _includeMap.Add("*");
	ArrayMap<String, int> &anyArchNameMap = _nameMap.Add("*");
	for(int iArch = 0; iArch < availableArchitectures.GetCount(); iArch++)
	{
		_includeMap.Add(availableArchitectures[iArch]);
		_nameMap.Add(availableArchitectures[iArch]);
	}
	
	// and finally fill them
	for(int iLib = 0; iLib < _libraries.GetCount(); iLib++)
	{
		LibraryInfo const &info = _libraries[iLib];
		String const &name = info.GetName();
		Vector<String> const &includes = info.GetIncludes();
		Vector<String> const &archs = info.GetArchitectures();

		anyArchNameMap.Add(name, iLib);
		if(info.SupportsAnyArchitecture())
		{
/*
			for(int iMap = 1; iMap < _includeMap.GetCount(); iMap++)
				_nameMap[iMap].Add(name, iLib);
*/
		}
		else
		{
			for(int iArch = 0; iArch < archs.GetCount(); iArch++)
				_nameMap.Get(archs[iArch]).Add(name, iLib);
		}
		
		for(int iInc = 0; iInc < includes.GetCount(); iInc++)
		{
			anyArchIncludeMap.Add(includes[iInc], iLib);
			if(info.SupportsAnyArchitecture())
			{
/*
				for(int iMap = 1; iMap < _includeMap.GetCount(); iMap++)
					_includeMap[iMap].Add(includes[iInc], iLib);
*/
			}
			else
			{
				for(int iArch = 0; iArch < archs.GetCount(); iArch++)
					_includeMap.Get(archs[iArch]).Add(includes[iInc], iLib);
			}
		}
	}
	
	// TEST -- find FishinoWebServer library and get its dependencies
/*
	Ptr<LibraryInfo> lib = FindByName("FishinoWebServer", "avr");
	Vector<Ptr<LibraryInfo>> libs;
	libs << lib;
	libs = SolveDependencies(libs, "avr");
	String s = "[";
	s << lib->GetName();
	s << ":(";
	for(int i = 1; i < libs.GetCount(); i++)
		s << libs[i]->GetName() << ",";
	if(s.EndsWith(","))
		s = s.Left(s.GetCount()-1);
	s << ")]";
	RLOG("LIB : " << s);
*/
// DUMP(_includeMap);		
	
	return true;
}

// find a library by include file and architecture name
Ptr<LibraryInfo> LibraryPoolClass::FindByInclude(String const &include, String const &arch)
{
	// first search by architecture
	int idx = _includeMap.Find(arch);
	if(idx >= 0)
	{
		ArrayMap<String, int> const &map = _includeMap[idx];
		int jdx = map.Find(include);
		if(jdx >= 0)
		{
			return &_libraries[map[jdx]];
		}
	}
	
	// not found, look into any architecture map
	ArrayMap<String, int> const &map = _includeMap[0];
	int jdx = map.Find(include);
	if(jdx >= 0)
		return &_libraries[map[jdx]];
	
	// nope, return an empty library
	return NULL;
}

// find a group of libraries by a group of includes
Vector<Ptr<LibraryInfo>> LibraryPoolClass::FindByIncludes(Vector<String> const &includes, String const &architecture)
{
	Index<LibraryInfo *> res;
	for(int i = 0; i < includes.GetCount(); i++)
	{
		Ptr<LibraryInfo> info = FindByInclude(includes[i], architecture);
		if(info)
			res.FindAdd(info);
	}
	Vector<Ptr<LibraryInfo>> vres;
	for(int i = 0; i < res.GetCount(); i++)
		vres.Add(res[i]);
	return vres;
}


// find a library by name and architecture name
LibraryInfo *LibraryPoolClass::FindByName(String const &name, String const &arch)
{
	// first search by architecture
	int idx = _nameMap.Find(arch);
	if(idx >= 0)
	{
		ArrayMap<String, int> const &map = _nameMap[idx];
		int jdx = map.Find(name);
		if(jdx >= 0)
			return &_libraries[map[jdx]];
	}
	
	// not found, look into any architecture map
	if(!_nameMap.IsEmpty())
	{
		ArrayMap<String, int> const &map = _nameMap[0];
		int jdx = map.Find(name);
		if(jdx >= 0)
			return &_libraries[map[jdx]];
	}
	
	// nope, return an empty library
	return NULL;
}

// find a group of libraries by names and architecture name
Vector<Ptr<LibraryInfo>> LibraryPoolClass::FindByNames(Vector<String> const &names, String const &architecture)
{
	Index<LibraryInfo *> res;
	for(int i = 0; i < names.GetCount(); i++)
	{
		Ptr<LibraryInfo> info = FindByName(names[i], architecture);
		if(info)
			res.FindAdd(info);
	}
	Vector<Ptr<LibraryInfo>> vres;
	for(int i = 0; i < res.GetCount(); i++)
		vres.Add(res[i]);
	return vres;
}

// get library names
Vector<String> LibraryPoolClass::GetNames(LibraryInfo::Kind kind) const
{
	Vector<String> res;
	for(int i = 0; i < _libraries.GetCount(); i++)
	{
		LibraryInfo const &lib = _libraries[i];
		if(lib.GetKind() == kind)
			res.Add(lib.GetName());
	}
	return res;
}

// get library names and descriptions
VectorMap<String, String> LibraryPoolClass::GetNamesDescs(LibraryInfo::Kind kind) const
{
	VectorMap<String, String> res;
	for(int i = 0; i < _libraries.GetCount(); i++)
	{
		LibraryInfo const &lib = _libraries[i];
		if(lib.GetKind() == kind)
			res.Add(lib.GetName(), lib.GetParagraph());
	}
	return res;
}

LibraryPoolClass &__GetLibraryPool(void)
{
	static LibraryPoolClass pool;
	return pool;
}
