/******************************************************************************
*	File		-	helpers.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/08/30 (y/m/d)
*	Mod Date	-	2021/08/30 (y/m/d)
*	Description	-	Defines some helper methods for use through out the engine.
******************************************************************************/

#include "helpers.h"
#include "logger.h"
#include <nfd.hpp>

#include <algorithm>

#ifdef WIN32
    #include <windows.h>
#else  
    #include <X11/Xlib.h>
#endif  // WIN32

namespace lunarium
{
    Sizei System::GetScreenResolution()
    {
        #ifdef WIN32
            return Sizei { GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN) };
        #else
            Display* disp = XOpenDisplay(NULL);
            Screen*  scrn = DefaultScreenOfDisplay(disp);
            return Sizei { scrn->width, scrn->height };
        #endif  // WIN32
    }

	    
    std::string System::GetGLSLVersionString()
    {
        std::string glsl_version = "#version ";
        glsl_version += (OPENGL_MAJOR_VERSION + 48);	// Need to convert to ascii
        glsl_version += (OPENGL_MINOR_VERSION) + 48;
        glsl_version += "0";
        return glsl_version;
    }

	////////////////////////////////////////////////////////////
	//				FILE SYSTEM FUNCTIONS
	////////////////////////////////////////////////////////////
	std::vector<std::string> FileSystem::GetFilesInDirectory(std::string path)
	{
		std::vector<std::string> files;
		for (auto &p : std::filesystem::recursive_directory_iterator(path))
		{
			files.push_back(p.path().filename().string());
		}

		return files;
	}
	
    bool FileSystem::MakeDir(std::string path)
	{
		if (std::filesystem::exists(path))
			return false;

		return std::filesystem::create_directory(path);
	}

	FileSystem::DialogResult FileSystem::SaveFileDialog(std::filesystem::path& outPath, const FilterItem* filters, 
								u32 num_filters, const u8* default_path, const u8* default_name)
	{
		
		NFD::Guard nfdGuard;
		NFD::UniquePath outPath_intern;
		DialogResult result = (DialogResult) NFD::SaveDialog(outPath_intern, (const nfdu8filteritem_t*) filters, (nfdfiltersize_t)num_filters, (const nfdu8char_t*)default_path, (const nfdu8char_t*)default_name);
		
		outPath = "";
		if (result == DialogResult::OK)
		{
			outPath = outPath_intern.get();
		}
		return result;
	}

	FileSystem::DialogResult FileSystem::OpenFileDialog(std::filesystem::path& outPath, const FilterItem* filters, 
								u32 num_filters, const u8* default_path)
	{
		NFD::Guard nfdGuard;
		NFD::UniquePath outPath_intern;
		DialogResult result = (DialogResult) NFD::OpenDialog(outPath_intern, (const nfdu8filteritem_t*) filters, 
									(nfdfiltersize_t)num_filters, (const nfdu8char_t*)default_path);
									
		outPath = "";
		if (result == DialogResult::OK)
		{
			outPath = outPath_intern.get();
		}
		return result;
	}

	FileSystem::DialogResult FileSystem::PickDirectory(std::filesystem::path& outPath, const u8* default_path)
	{
		
		NFD::Guard nfdGuard;
		NFD::UniquePath outPath_intern;
		DialogResult result = (FileSystem::DialogResult) NFD::PickFolder(outPath_intern, (const nfdu8char_t*)default_path);
		outPath = "";
		if (result == DialogResult::OK)
		{
			outPath = outPath_intern.get();
		}
		return result;
	}

	std::string FileSystem::GetError()
	{
		return std::string(NFD::GetError());
	}

	////////////////////////////////////////////////////////////
	//				MATH FUNCTIONS
	////////////////////////////////////////////////////////////
	int Math::ClampI(int value, int min, int max)
	{
		if (value < min)
		{
			return min;
		}

		if (value > max)
		{
			return max;
		}

		return value;		
	}

	float Math::ClampF(float value, float min, float max)
	{
		if (value < min)
		{
			return min;
		}

		if (value > max)
		{
			return max;
		}

		return value;		
	}

	////////////////////////////////////////////////////////////
	//				STRING MANIPULATION
	////////////////////////////////////////////////////////////
    std::vector<std::string> String::Split(std::string str, char delim, int maxSplits)
	{
		std::vector<std::string> splits;

		int startPos = 0;
		for (int i = 0; i < (signed)str.length(); i++)
		{
			if (str[i] == delim)
			{
				if (startPos == i)
					continue;

				std::string s = str.substr(startPos, i - startPos);
				splits.push_back(s);
				startPos = i + 1;

				if (maxSplits > 0 && (signed)splits.size() >= (maxSplits - 1))
				{
					break;
				}
			}
		}

		std::string s = str.substr(startPos);
		splits.push_back(s);
		return splits;
	}

	std::string String::Trim(std::string str, std::string delims)
	{
		str = TrimStart(str, delims);
		str = TrimEnd(str, delims);
		return str;
	}


	std::string String::TrimStart(std::string str, std::string delims)
	{
		bool isDelim = true;
		while (isDelim && str.length() > 0)
		{
			isDelim = false;
			for (int i = 0; i < (signed)delims.length(); i++)
			{
				if (str[0] == delims[i])
				{
					isDelim = true;
					break;
				}
			}

			if (isDelim)
				str.erase(str.begin());
		}

		return str;
	}


	std::string String::TrimEnd(std::string str, std::string delims)
	{
		bool isDelim = true;
		while (isDelim && str.length() > 0)
		{
			isDelim = false;
			for (int i = 0; i < (signed)delims.length(); i++)
			{
				if (*(str.rbegin()) == delims[i])
				{
					isDelim = true;
					break;
				}
			}

			if (isDelim)
				str.erase((str.rbegin() + 1).base());
		}

		return str;
	}

    //template <typename T>
    std::string String::AsString(int value)
    {
        std::string str = "";
        str += value;
        return str;
    }

    std::string String::AsString(bool value)
    {
        std::string str = "";
        str += value;
        return str;
    }

    std::string String::AsString(float value)
    {
        std::string str = "";
        str += value;
        return str;
    }

    
    std::string String::StringToUpper(std::string str)
    {
        for (int i = 0; i < str.size(); i++)
        {
            if (str[i] >= 97 && str[i] <= 122)
            {
                str[i] -= 32;
            }
        }

        return str;
    }

    std::string String::StringToLower(std::string str)
    {
        for (int i = 0; i < str.size(); i++)
        {
            if (str[i] >= 65 && str[i] <= 90)
            {
                str[i] += 32;
            }
        }

        return str;
    }

	////////////////////////////////////////////////////////////
	//				PATH MANIPULATION
	////////////////////////////////////////////////////////////
	std::string String::GetFileNameFromPath(std::string path, bool includeExt)
	{
		path.erase(path.begin(), std::find_if(path.rbegin(), path.rend(), [](int ch) { return ch == '\\' || ch == '/'; }).base());

		if (!includeExt)
		{
			path.erase(std::find_if(path.rbegin(), path.rend(), [](int ch) { return ch == '.'; }).base(), path.end());
			path.erase(path.begin() + path.size() - 1);
		}

		return path;
	}

	std::string String::TrimFileNameFromPath(std::string path)
	{
		path.erase(std::find_if(path.rbegin(), path.rend(), [](int ch) { return ch == '\\' || ch == '/'; }).base(), path.end());
		return path;
	}

	std::string String::GetFileExtension(std::string filename)
	{
		filename.erase(filename.begin(), std::find_if(filename.begin(), filename.end(), [](int ch) { return ch == '.'; }));
		filename.erase(filename.begin());
		return filename;
	}
}