#pragma once



#include <string>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <ctime>
#include <filesystem>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <fstream>


// here so i only need to include once.
#include "Profiler.h"
#include "Logging.h"
#include "FileDialog.h"
#include "EngineConfig.h"

#include "AssetManager.h"

#include <cstdio>

#if defined(_WIN32)
    #include <io.h>
    #define ISATTY _isatty
    #define FILENO _fileno
#else
    #include <unistd.h>
    #define ISATTY isatty
    #define FILENO fileno
#endif

static bool HasTerminal()
{
    return ISATTY(FILENO(stdout)) != 0;
}


struct ImageCacheEntry {
    unsigned int textureID;
    glm::vec2 size;
};

extern std::unordered_map<std::string, ImageCacheEntry> textureCache;



// Returns filename from a full path (e.g., "C:/foo/bar.png" → "bar.png")
std::string GetFilenameFromPath(const std::string& path);



GLuint LoadTextureIfNeeded(const std::string& path);
ImageCacheEntry LoadImageCache(const std::string& path);

GLuint EngineLoadTextureIfNeeded(const std::string &path);


bool DeleteLatestLogFile();

bool IsWindowVisibleTab(const char* name);

std::string HashString(const std::string& data);
std::time_t GetFileLastWrite(const std::string &path);

std::string GetFileHash(const std::string &path);


// HSV → RGB helper
inline glm::vec3 HSVtoRGB(float h, float s, float v)
{
    float r, g, b;

    int i = int(h * 6);
    float f = h * 6 - i;
    float p = v * (1 - s);
    float q = v * (1 - f * s);
    float t = v * (1 - (1 - f) * s);

    switch (i % 6)
    {
        case 0: r = v; g = t; b = p; break;
        case 1: r = q; g = v; b = p; break;
        case 2: r = p; g = v; b = t; break;
        case 3: r = p; g = q; b = v; break;
        case 4: r = t; g = p; b = v; break;
        case 5: r = v; g = p; b = q; break;
    }

    return glm::vec3(r, g, b);
}

inline std::string GetFileExtension(const std::string &path)
{
    size_t dot = path.find_last_of('.');
    if (dot == std::string::npos)
        return "";
    return path.substr(dot + 1);
}


inline std::string GetFileExtension_Name(const std::string &path)
{
    size_t dot = path.find_last_of('.');
    if (dot == std::string::npos || dot == path.length() - 1)
        return "";

    std::string ext = path.substr(dot + 1);

    // Convert to lowercase
    std::transform(ext.begin(), ext.end(), ext.begin(),
                   [](unsigned char c) { return std::tolower(c); });

    return ext;
}


inline std::string GetFilenameNoExtension(const std::string& path)
{
    std::filesystem::path p(path);
    return p.stem().string();
}


inline std::string TruncateWithEllipsis(const std::string& str, size_t maxLength)
{
    if (maxLength <= 3)
        return std::string(maxLength, '.');  // handle very small limits

    if (str.size() <= maxLength)
        return str;

    return str.substr(0, maxLength - 3) + "...";
}


inline bool FileExists(const std::string& path)
{
    return std::filesystem::exists(path) && std::filesystem::is_regular_file(path);
}



inline bool ReadTextFile(const std::string& path, std::string& out)
{
    std::ifstream file(path);
    if (!file.is_open())
        return false;

    std::stringstream buffer;
    buffer << file.rdbuf();
    out = buffer.str();
    return true;
}
