#include "asset_locator.h"

#include <cstdlib>

using std::filesystem::path;

static path get_env_path(const char *name)
{
    const char *v = std::getenv(name);
    if (!v || !*v) return {};
    path p = v;
    if (std::filesystem::exists(p)) return std::filesystem::canonical(p);
    return {};
}

static path find_upwards_containing(path start, const std::string &subdir, int maxDepth = 6)
{
    path cur = std::filesystem::weakly_canonical(start);
    for (int i = 0; i <= maxDepth; i++)
    {
        path candidate = cur / subdir;
        if (std::filesystem::exists(candidate)) return cur;
        if (!cur.has_parent_path()) break;
        cur = cur.parent_path();
    }
    return {};
}

AssetPaths AssetPaths::detect(const path &startDir)
{
    AssetPaths out{};

    if (auto root = get_env_path("VKG_ASSET_ROOT"); !root.empty())
    {
        out.root = root;
        if (std::filesystem::exists(root / "assets")) out.assets = root / "assets";
        if (std::filesystem::exists(root / "shaders")) out.shaders = root / "shaders";
        return out;
    }

    if (auto aroot = find_upwards_containing(startDir, "assets"); !aroot.empty())
    {
        out.assets = aroot / "assets";
        out.root = aroot;
    }
    if (auto sroot = find_upwards_containing(startDir, "shaders"); !sroot.empty())
    {
        out.shaders = sroot / "shaders";
        if (out.root.empty()) out.root = sroot;
    }

    if (out.assets.empty())
    {
        path p1 = startDir / "assets";
        path p2 = startDir / ".." / "assets";
        if (std::filesystem::exists(p1)) out.assets = p1;
        else if (std::filesystem::exists(p2)) out.assets = std::filesystem::weakly_canonical(p2);
    }
    if (out.shaders.empty())
    {
        path p1 = startDir / "shaders";
        path p2 = startDir / ".." / "shaders";
        if (std::filesystem::exists(p1)) out.shaders = p1;
        else if (std::filesystem::exists(p2)) out.shaders = std::filesystem::weakly_canonical(p2);
    }

    return out;
}

void AssetLocator::init()
{
    _paths = AssetPaths::detect();
}

bool AssetLocator::file_exists(const path &p)
{
    std::error_code ec;
    return !p.empty() && std::filesystem::exists(p, ec) && std::filesystem::is_regular_file(p, ec);
}

std::string AssetLocator::resolve_in(const path &base, std::string_view name)
{
    if (name.empty()) return {};
    path in = base / std::string(name);
    if (file_exists(in)) return in.string();
    return {};
}

std::string AssetLocator::shaderPath(std::string_view name) const
{
    if (name.empty()) return {};
    path np = std::string(name);

    if (np.is_absolute() && file_exists(np)) return np.string();
    if (file_exists(np)) return np.string();

    if (!_paths.shaders.empty())
    {
        if (auto r = resolve_in(_paths.shaders, name); !r.empty()) return r;
    }

    if (auto r = resolve_in(std::filesystem::current_path() / "shaders", name); !r.empty()) return r;
    if (auto r = resolve_in(std::filesystem::current_path() / ".." / "shaders", name); !r.empty()) return r;

    return np.string();
}

std::string AssetLocator::assetPath(std::string_view name) const
{
    if (name.empty()) return {};
    path np = std::string(name);
    if (np.is_absolute() && file_exists(np)) return np.string();
    if (file_exists(np)) return np.string();

    if (!_paths.assets.empty())
    {
        if (auto r = resolve_in(_paths.assets, name); !r.empty()) return r;
    }

    if (auto r = resolve_in(std::filesystem::current_path() / "assets", name); !r.empty()) return r;
    if (auto r = resolve_in(std::filesystem::current_path() / ".." / "assets", name); !r.empty()) return r;

    return np.string();
}

