#include "stdafx.h"

using namespace System::Text::RegularExpressions;

void descendpath(const tstring root, std::wstring relativepath, std::queue<tstring> &pathlist)
{
	std::queue<std::wstring> searchpath;
	WIN32_FIND_DATAW fd;
	HANDLE h = FindFirstFileExW((root + relativepath + L"*").c_str(),
		findex_flag,
		&fd,
		FindExSearchNameMatch, NULL, 0);
	if (h == INVALID_HANDLE_VALUE) {
		return;
	}
	do {
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			if ((wcscmp(fd.cFileName, L".") == 0) ||
				(wcscmp(fd.cFileName, L"..") == 0)) {
				continue;
			}
			std::wstring dirname = fd.cFileName;
			dirname = relativepath + dirname + L'\\';
			searchpath.push(dirname);
			pathlist.push(dirname);
			continue;
		}
	} while (FindNextFileW(h, &fd));
	FindClose(h);
	while (!searchpath.empty()){
		descendpath(root, searchpath.front(), pathlist);
		searchpath.pop();
	}
}

std::vector<fileentry> SearchSrcfiles(std::wstring basepath, std::wstring pattern, param::paramdata &param)
{
	std::wstring srcpath = basepath;
	std::wstring matchpattern = pattern;

	std::queue<std::wstring> searchpath;
	searchpath.push(L"");
	if (param.local.recursion){
		descendpath(srcpath, L"", searchpath);
	}

	std::vector<fileentry> targetfiles;

	while (!searchpath.empty()){
		tstring relativepath = searchpath.front();
		searchpath.pop();

		WIN32_FIND_DATAW fd;
		HANDLE h = FindFirstFileExW((srcpath + relativepath + matchpattern).c_str(),
			findex_flag,
			&fd,
			FindExSearchNameMatch, NULL, 0);
		if (h == INVALID_HANDLE_VALUE) {
			continue;
		}
		do {
			std::wstring filename = fd.cFileName;
			fileentry aFile;
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				if ((wcscmp(fd.cFileName, L".") == 0) ||
					(wcscmp(fd.cFileName, L"..") == 0)) {
					continue;
				}
				filename += L"\\";
				aFile.directory = true;
			}
			else {
				aFile.directory = false;
			}
			LARGE_INTEGER filesize;
			FILETIME LastWriteTime = fd.ftLastWriteTime;
			filesize.HighPart = fd.nFileSizeHigh;
			filesize.LowPart = fd.nFileSizeLow;
			aFile.absolutepath = srcpath + relativepath + filename;
			aFile.name = filename;
			aFile.relativepath = relativepath + filename;
			aFile.filesize = filesize.QuadPart;
			aFile.LastWriteTime = LastWriteTime;
			targetfiles.push_back(aFile);
		} while (FindNextFileW(h, &fd));
		FindClose(h);
	}

	return targetfiles;
}

std::vector<fileentry> localsearch(param::paramdata& param)
{
	std::vector<fileentry> result;

	bool regular = param.local.regular;

	System::String^ strtarget = gcnew System::String(param.local.targetstr.c_str());
	bool recursion = Regex::IsMatch(strtarget, ".*[*?]+.*\\\\");
	bool forcerecursion = false;
	std::wstring base;
	std::wstring target;
	if (!param.local.basepath.empty()){
		base = param.local.basepath;
		target = param.local.targetstr;
	}
	else if (recursion){
		base = SystemStringToStdWString(Regex::Replace(strtarget, "(([^\\\\]*\\\\)*\\\\)[^\\\\]*[*?]+[^\\\\]*\\\\?.*", "$1"));
		target = SystemStringToStdWString(Regex::Replace(strtarget, "([^\\\\]*\\\\)*([^\\\\]*[*?]+[^\\\\]*\\\\?.*)", "$2"));
		forcerecursion = true;
	}
	else {
		base = SystemStringToStdWString(Regex::Replace(strtarget, "(([^\\\\]*\\\\)*)[^\\\\]*", "$1"));
		target = SystemStringToStdWString(Regex::Replace(strtarget, "([^\\\\]*\\\\)*([^\\\\]*)", "$2"));
		target = (target.empty()) ? L"*" : target;
	}

	param.local.recursion = param.local.recursion || recursion;

	std::wcerr << "target " << target << "\n";
	std::wcerr << "recursion " << param.local.recursion << "\n";
	std::wcerr << "...loding " << base << "\n";


	auto list = SearchSrcfiles(base, (regular || forcerecursion)? L"*": target, param);
	for (auto it = list.begin(); it != list.end(); ++it){
		auto pathname = it->relativepath;
		if (CheckFilePathMatch(target, pathname, regular)){
			if (param.usedatefilter){
				const FILETIME zerovalue = { 0 };
				if ((CompareFileTime(&zerovalue, &param.startdate) != 0) && (CompareFileTime(&it->LastWriteTime, &param.startdate) < 0)){
					continue;
				}
				if ((CompareFileTime(&zerovalue, &param.enddate) != 0) && (CompareFileTime(&it->LastWriteTime, &param.enddate) > 0)){
					continue;
				}
			}
			if (param.usesizefilter){
				if ((param.minsize > 0) && (param.minsize > it->filesize)){
					continue;
				}
				if ((param.maxsize > 0) && (param.maxsize < it->filesize)){
					continue;
				}
			}
			if (param.local.notree){
				if (pathname.find_last_of('\\') != pathname.length() - 1)
					it->display = it->name;
			}
			else {
				it->display = pathname;
			}
			result.push_back(*it);
		}
	}
	std::wcerr << "local match " << result.size() << "\n";
	return result;
}

std::vector<std::wstring> findlocal(param::paramdata& param)
{
	std::vector<std::wstring> ret;
	auto list = localsearch(param);
	std::for_each(list.begin(), list.end(), [&](const fileentry &it){ ret.push_back(it.display); });
	std::sort(ret.begin(), ret.end());
	return ret;
}

int listLocal(param::paramdata& param)
{
	auto result = findlocal(param);

	std::copy(result.begin(), result.end(), std::ostream_iterator<std::wstring, wchar_t>(std::wcout, L"\n"));
	return 0;
}
