///////////////////////////////////////////////////////////////////////////////
//  CFindFile.cpp

#ifdef WIN32
#include <windows.h>
#endif
#include <fstream>
#include <iostream>
#include <algorithm>
#include "CFile.h"
#include "CFindFile.h"

namespace filesystem {

#ifdef WIN32
// pimpl
class CFindFile::CImpl {
	HANDLE				m_hFindFile;
	WIN32_FIND_DATA		m_FindData;

public:
	CImpl() : m_hFindFile(INVALID_HANDLE_VALUE) {}
	virtual ~CImpl()
	{
		Close();
	}

	bool	Close()
	{
		if (m_hFindFile != INVALID_HANDLE_VALUE) {
			if (!FindClose(m_hFindFile)) return false;
			m_hFindFile = INVALID_HANDLE_VALUE;
		}

		return true;
	}

	bool	GetFirst(const std::string& szSearch, std::string& szFound)
	{
		if (!Close()) return false;

		m_hFindFile = FindFirstFile(szSearch.c_str(), &m_FindData);
		if (m_hFindFile == INVALID_HANDLE_VALUE) return false;

		szFound = m_FindData.cFileName;

		return true;
	}

	bool	GetFirst(const std::string& szSearch, std::string& szFound, FILETIME& CreationTime)
	{
		if (!Close()) return false;

		m_hFindFile = FindFirstFile(szSearch.c_str(), &m_FindData);
		if (m_hFindFile == INVALID_HANDLE_VALUE) return false;

		szFound = m_FindData.cFileName;
		CreationTime = m_FindData.ftCreationTime;

		return true;
	}

	bool	GetNext(std::string& szFound)
	{
		if (m_hFindFile == INVALID_HANDLE_VALUE) return false;

		if (!FindNextFile(m_hFindFile, &m_FindData)) return false;

		szFound = m_FindData.cFileName;

		return true;
	}

	bool	GetNext(std::string& szFound, FILETIME& CreationTime)
	{
		if (m_hFindFile == INVALID_HANDLE_VALUE) return false;

		if (!FindNextFile(m_hFindFile, &m_FindData)) return false;

		szFound = m_FindData.cFileName;
		CreationTime = m_FindData.ftCreationTime;

		return true;
	}
};
#endif

//
CFindFile::CFindFile(void)
#ifdef WIN32
	: m_Impl(new CImpl())
#endif
{
}

CFindFile::~CFindFile()
{
	Close();
}

bool CFindFile::GetFirst(const std::string& szSearch, std::string& szFound)
{
#ifdef WIN32
	return m_Impl->GetFirst(szSearch, szFound);
#else
	return false;
#endif
}

bool CFindFile::GetNext(std::string& szFound)
{
#ifdef WIN32
	return m_Impl->GetNext(szFound);
#else
	return false;
#endif
}

bool CFindFile::IsExistFile(const std::string& szFileName)
{
#ifdef WIN32
	const std::string RealFileName = CFile::TransRealFileName(szFileName);

	//
	WIN32_FIND_DATA	FindData;

	HANDLE hFindFile = FindFirstFile(RealFileName.c_str(), &FindData);
	if (hFindFile == INVALID_HANDLE_VALUE) return false;

	FindClose(hFindFile);

	if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) return false;

	return true;
#elif defined(__EMSCRIPTEN__)
	CFile File;
	return File.Open(szFileName);
#else
    
    std::ifstream ifs(szFileName);
    return ifs.is_open();
    
#endif
}

bool CFindFile::IsExistDir(const std::string& szFileName)
{
#ifdef WIN32
	const std::string RealFolderName = CFile::TransRealFileName(szFileName);

	//
	WIN32_FIND_DATA	FindData;

	HANDLE hFindFile = FindFirstFile(RealFolderName.c_str(), &FindData);
	if (hFindFile == INVALID_HANDLE_VALUE) return false;

	FindClose(hFindFile);

	return (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
	return false;
#endif
}

bool CFindFile::IsHidden(const std::string& FileName)
{
#ifdef WIN32
	WIN32_FIND_DATA	FindData;

	HANDLE hFindFile = FindFirstFile(FileName.c_str(), &FindData);
	if (hFindFile == INVALID_HANDLE_VALUE) return false;

	FindClose(hFindFile);

	return (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
#else
	return false;
#endif
}

bool CFindFile::IsSystem(const std::string& FileName)
{
#ifdef WIN32
	WIN32_FIND_DATA	FindData;

	HANDLE hFindFile = FindFirstFile(FileName.c_str(), &FindData);
	if (hFindFile == INVALID_HANDLE_VALUE) return false;

	FindClose(hFindFile);

	return (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0;
#else
	return false;
#endif
}

bool CFindFile::Close(void)
{
#ifdef WIN32
	return m_Impl->Close();
#else
	return true;
#endif
}

void CFindFile::GetFileList(const std::string& FolderName, std::vector<std::string>& vecszList)
{
	CFindFile FindFile;
	std::string szFound;

	const std::string RealFolderName = CFile::TransRealFileName(FolderName);

	const std::string SearchName = RealFolderName + "\\*";

	if (!FindFile.GetFirst(SearchName.c_str(), szFound)) return;

	for (;;) {
		if (szFound != "." && szFound != "..") {
			const std::string FileName = RealFolderName + "\\" + szFound;

			if (IsExistFile(FileName) &&
				!IsHidden(FileName) &&
				!IsSystem(FileName))
			{
				vecszList.push_back(szFound);
			}
		}

		if (!FindFile.GetNext(szFound)) break;
	}

	std::sort(vecszList.begin(), vecszList.end());
}

void CFindFile::GetFileListWithinSubFolder(const std::string& FolderName, std::vector<std::string>& vecszList)
{
	// ルートのファイルリスト
	std::vector<std::string> FileList;
	GetFileList(FolderName, FileList);

	// フォルダ名を連結する
	{
		std::vector<std::string>::iterator it = FileList.begin();
		for (; it != FileList.end(); ++it) {
			*it = FolderName + "\\" + *it;
		}
	}

	vecszList.insert(vecszList.end(), FileList.begin(), FileList.end());

	// ルートのフォルダリスト
	std::vector<std::string> FolderList;
	GetFolderList(FolderName, FolderList);

	{
		std::vector<std::string>::iterator it = FolderList.begin();
		for (; it != FolderList.end(); ++it) {
			GetFileListWithinSubFolder(FolderName + "\\" + *it, vecszList);
		}
	}
}

void CFindFile::GetFolderList(const std::string& FolderName, std::vector<std::string>& vecszList)
{
	CFindFile FindFile;
	std::string szFound;

	const std::string RealFolderName = CFile::TransRealFileName(FolderName);

	const std::string SearchName = RealFolderName + "\\*";

	if (!FindFile.GetFirst(SearchName.c_str(), szFound)) return;

	for (;;) {
		if (szFound != "." && szFound != "..") {
			if (IsExistDir(RealFolderName + "\\" + szFound)) {
				vecszList.push_back(szFound);
			}
		}

		if (!FindFile.GetNext(szFound)) break;
	}

	std::sort(vecszList.begin(), vecszList.end());
}

} // end of namespace filesystem

