#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <assert.h>
#include "filesystem/stream.h"
#include "filesystem/filemanager.h"

void RecursiveSearch(const std::string& path, std::vector<std::string>& files);
void osResolvePath(char* path);

FileManager* g_fileManager;

FileManager::FileManager()
{
	static char currentDirectory[kMaxPathLength];
	GetCurrentDirectoryA(kMaxPathLength, currentDirectory);
	strcat(currentDirectory, "/");
	
	osResolvePath(currentDirectory);
	
	m_defaultPath = currentDirectory;
}

FileManager::~FileManager()
{

}

void FileManager::SetDefaultPath(const char* path)
{
	m_defaultPath = path;
}

bool FileManager::FileExist(const char* filename)
{
#if defined(WIN32) || defined(_WIN32)
	char pathBuffer[kMaxPathLength];
	if (!strstr(filename, m_defaultPath))
		sprintf(pathBuffer, "%s%s", m_defaultPath, filename);
	else
		strcpy(pathBuffer, filename);

	osResolvePath(pathBuffer);

	DWORD dwAttrib = GetFileAttributes(pathBuffer);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
#else
	FILE* file;

	if ((file = fopen(filename, "r")) != NULL)
	{
		fclose(file);
		return true;
}
	else
		return false;
#endif
}

File* FileManager::OpenFile(const char* path, FileAccess access)
{
	char pathBuffer[kMaxPathLength];
	if (!strstr(path, m_defaultPath))
		sprintf(pathBuffer, "%s%s", m_defaultPath, path);
	else
		strcpy(pathBuffer, path);

	osResolvePath(pathBuffer);

	return new File(pathBuffer, access);
}

void FileManager::CloseFile(File*& file)
{
	if (file)
	{
		delete file;
		file = nullptr;
	}
}

StreamBase* FileManager::OpenStream(const char* fname, FileAccess access)
{
	char pathBuffer[kMaxPathLength];
	if (!strstr(fname, m_defaultPath))
		sprintf(pathBuffer, "%s%s", m_defaultPath, fname);
	else
		strcpy(pathBuffer, fname);

	osResolvePath(pathBuffer);

	if (!g_fileManager->FileExist( pathBuffer ))
		return nullptr;

	File* file = new File(pathBuffer, access);
	return createFileStream(file);
}

#if _WIN32
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <time.h>

void GetFileListFromFolder(const char* path, const char* findingPattern, std::vector<std::string>& filesList)
{
	struct _finddata_t c_file;
	intptr_t hFile;

	char pathfind[kMaxPathLength];
	sprintf(pathfind, "%s/%s", path, findingPattern);
	osResolvePath(pathfind);

	if ((hFile = _findfirst(pathfind, &c_file)) != -1L) {
		do {
			char buffer[kMaxPathLength];
			sprintf(buffer, "%s/%s", path, c_file.name);
			filesList.push_back(buffer);
		} while (_findnext(hFile, &c_file) == 0);
		_findclose(hFile);
	}
}

void osResolvePath(char* path)
{
	assert(path);

	size_t length = strlen(path);

	for (int i = 0; i < (int)length; i++) {
		if (path[i] == '/')
			path[i] = '\\';
	}
}

bool osDirectoryIsExist(const char* path)
{
	DWORD ftyp = GetFileAttributesA(path);
	if (ftyp == INVALID_FILE_ATTRIBUTES)
		return false;

	if (ftyp & FILE_ATTRIBUTE_DIRECTORY)
		return true;

	return false;
}

void osCreateDirectory(const char* path)
{
	CreateDirectoryA(path, NULL);
}

void RecursiveSearch(const std::string& path, std::vector<std::string>& files)
{
	struct _finddata_t c_file;
	intptr_t hFile;

	std::string bufferPath;
	bufferPath += path;
	bufferPath += "/";
	bufferPath += "*.*";

	if ((hFile = _findfirst(bufferPath.c_str(), &c_file)) != -1L)
	{
		do {
			if (strcmp(c_file.name, ".") == 0 ||
				strcmp(c_file.name, "..") == 0 ||
				strcmp(c_file.name, ".git") == 0 ||
				strcmp(c_file.name, ".gitignore") == 0 ||
				strcmp(c_file.name, ".gitignore") == 0 ||
				strcmp(c_file.name, ".vs") == 0)
				continue;

			if (c_file.attrib & _A_SUBDIR)
			{
				std::string nextPath;
				nextPath += path;
				nextPath += "/";
				nextPath += c_file.name;

				RecursiveSearch(nextPath, files);
			}
			else
			{
				std::string filepath;
				filepath += path;
				filepath += "/";
				filepath += c_file.name;
				files.push_back(filepath);
			}

		} while (_findnext(hFile, &c_file) == 0);
		_findclose(hFile);
	}
}

#else
#include <dirent.h>

bool match(const char* pattern, const char* str, int p = 0, int c = 0)
{
	if (pattern[p] == '\0')
	{
		return str[c] == '\0';
	}
	else if (pattern[p] == '*')
	{
		for (; str[c] != '\0'; c++)
		{
			if (match(pattern, str, p + 1, c))
				return true;
		}
		return match(pattern, str, p + 1, c);
	}
	else if (pattern[p] != '?' && pattern[p] != str[c])
	{
		return false;
	}
	else
	{
		return match(pattern, str, p + 1, c + 1);
	}
}

void GetFileListFromFolder(const char* path, const char* findingPattern, std::vector<std::string>& filesList)
{
	DIR* dir;
	dirent* ent;

	if ((dir = opendir(path)) != NULL)
	{
		while ((ent = readdir(dir)) != NULL)
		{
			if (match(findingPattern, ent->d_name))
			{
				char buffer[kMaxPathLength];
				snprintf(buffer, kMaxPathLength, "%s/%s", path, ent->d_name);
				filesList.push_back(buffer);
			}
		}

		closedir(dir);
	}
}
#endif

namespace fs
{
	std::string getFileExtension(const std::string& filename)
	{
		size_t whereIsDot = filename.find_last_of('.');
		if (whereIsDot != std::string::npos) {
			return filename.substr(whereIsDot);
		}

		return std::string();
	}

	std::string getFilePath(const std::string& filename)
	{
		size_t lastindex = filename.find_last_of("/");
		if (lastindex == std::string::npos) {
			lastindex = filename.find_last_of("\\");
		}

		if (lastindex != std::string::npos) {

			return filename.substr(0, lastindex);
		}

		return std::string();
	}

	std::string getFileNameWithoutExtension(const std::string& filename)
	{
		size_t lastindex = filename.find_last_of(".");
		if (lastindex != std::string::npos) {
			return filename.substr(0, lastindex);
		}

		return filename;
	}

	std::string getFilenameWithoutPath(const std::string& filename)
	{
		size_t whereIsSlash = filename.find_last_of('/');
		if (whereIsSlash == std::string::npos) {
			whereIsSlash = filename.find_last_of('\\');
		}

		if (whereIsSlash == std::string::npos) {
			return filename;
		}

		std::string string = filename.substr(whereIsSlash + 1);
		return string;
	}

	std::string getFilenameWithoutPathAndExtension(const std::string& filename)
	{
		size_t whereIsDot = filename.find_last_of('.');
		size_t whereIsSlash = filename.find_last_of('/');
		if (whereIsSlash == std::string::npos) {
			whereIsSlash = filename.find_last_of('\\');
		}

		if (whereIsDot == std::string::npos && whereIsSlash == std::string::npos) {
			return filename;
		}

		std::string string = filename.substr(whereIsSlash + 1);
		whereIsDot = string.find_last_of('.');
		string = string.substr(0, whereIsDot);

		return string;
	}
}