#include "FileTools.h"

#include <Archive/Archive.h>
#include "Dialogs.h"

Vector<String> FileTools::ScanFolder(String const &path, String const &pattern, bool folders)
{
	Vector<String> res;
	if(!DirectoryExists(path))
		return res;

	FindFile ff(AppendFileName(path, pattern));
	while(ff)
	{
		if((ff.IsFolder() && !folders) || (!ff.IsFolder() && folders) || (ff.GetName() == ".") || (ff.GetName() == ".."))
		{
			ff.Next();
			continue;
		}
		res << ff.GetName();
		ff.Next();
	}
	Sort(res);
	return res;
}

// scan a folder for files
Vector<String> FileTools::ScanFolder(String const &path, Vector<String> const &patterns)
{
	Vector<String> res;
	for(int iPat = 0; iPat < patterns.GetCount(); iPat++)
		res.Append(ScanFolder(path, patterns[iPat], false));
	return res;
}

// recurse scan a folder for files
Vector<String>FileTools::RecurseScan(String const &path, String const &pattern, String const &sub)
{
	Vector<String> res;
	
	// scan first for subfolders recursively
	Vector<String> subs = ScanFolder(path, "*", true);
	for(int iSub = 0; iSub < subs.GetCount(); iSub++)
	{
		String name = subs[iSub];
		res.Append(RecurseScan(AppendFileName(path, name), pattern, name));
	}

	// then scan for files
	res.Append(ScanFolder(path, pattern, false));
	for(int iRes = 0; iRes < res.GetCount(); iRes++)
		res[iRes] = AppendFileName(sub, res[iRes]);
	return res;
}

// recurse scan a folder for files
Vector<String>FileTools::RecurseScan(String const &path, Vector<String> const &patterns)
{
	Vector<String> res;
	for(int iPat = 0; iPat < patterns.GetCount(); iPat++)
		res.Append(RecurseScan(path, patterns[iPat]));
	return res;
}

// check if file is writeable
// a semi-hack..
bool FileTools::IsReadOnly(String const &path)
{
	FindFile ff(path);
	if(!ff)
		return true;
	return ff.IsReadOnly();
}

// unpacks an archive on a destination folder
// a-la-arduino :
// 1) archive MUST contain a single folder
// 2) root folder name is replaced by given last folder in dest path
// 3) upper folder from destination one must contain AT MOST the destination folder
bool FileTools::UnpackArduinoArchive(String const &archivePath, String const &destPath, Event<int, int>progress)
{
	// first, we shall remove destination path and its contents
	DeleteFolderDeep(destPath);
	
	Archive archive(archivePath);
	archive.Progress = progress;
	bool res = archive.Decompress(destPath, 1);
// DUMP(archive.GetError());
	return res;
}

// same with progress popup
bool FileTools::UnpackArduinoArchive(String const &prompt, String const &archivePath, String const &destPath)
{
	ProgressPopup progress(prompt);
	progress.Show();
	bool res = UnpackArduinoArchive(archivePath, destPath, progress.GetEventCb());
	progress.Hide();
	if(!res)
	{
		Exclamation(Format(t_("Error unpacking archive '%s'"), DeQtf(archivePath)));
		return false;
	}
	return true;
}

bool FileTools::DeleteFolderDeep(const char *dir)
{
	FindFile ff(AppendFileName(dir, "*.*"));
	while(ff)
	{
		String name = ff.GetName();
		String p = AppendFileName(dir, name);
		if(ff.IsFile() || ff.IsSymLink())
			FileDelete(p);
		else
		if(ff.IsFolder())
			DeleteFolderDeep(p);
		ff.Next();
	}
	return DirectoryDelete(dir);
}

// read a value file into a map of key=value pairs
VectorMap<String, String> FileTools::ReadValueFile(String const &path)
{
	VectorMap<String, String> res;
	
	String data = LoadFile(path);
	if(data.IsEmpty())
		return res;

	StringStream ss(data);
	while(!ss.IsEof())
	{
		String line = TrimBoth(ss.GetLine());
		
		// skip comments and empty lines
		if(line.IsEmpty() || line.StartsWith("#"))
			continue;

		// locate the first '=' character
		int i = 0;
		while(line[i] && line[i] != '=')
			i++;
		if(!line[i])
			continue;
		
		// extract name and value
		String name = TrimBoth(line.Mid(0, i));
		String value = TrimLeft(line.Mid(i + 1));
		
		if(name.IsEmpty())
			continue;
		
		res.Add(name, value);
	}
	return res;
}

// copy a directory to a new destination
// both source and dest MUST be folder paths
bool FileTools::DirectoryCopy(String const &sourcePath, String const &destPath)
{
	if(!DirectoryExists(sourcePath))
		return false;
	Vector<String> files = RecurseScan(sourcePath, "*");
	for(int i = 0; i < files.GetCount(); i++)
	{
		String src = AppendFileName(sourcePath, files[i]);
		String dst = AppendFileName(destPath, files[i]);
		RealizePath(dst);
		if(!FileCopy(src, dst))
			return false;
	}
	return true;
}
