#pragma once
#include <memory>
#include "../types/Vec2.h"
#include "Texture.h"

struct TextureAtlas
{
    std::shared_ptr<Texture> texture;
    int frameWidth  = 32;
    int frameHeight = 32;

    TextureAtlas() = default;

    TextureAtlas(std::shared_ptr<Texture> tex, int texelW, int texelH)
        : texture(std::move(tex))
    {
        SetTexelSize(texelW, texelH);
    }

    void SetTexelSize(int texelWidth, int texelHeight)
    {
        frameWidth  = texelWidth;
        frameHeight = texelHeight;
    }

    core::types::Vec2 GetTexelSize() const
    {
        return core::types::Vec2(frameWidth, frameHeight);
    }

    bool IsValid() const
    {
        return texture && frameWidth > 0 && frameHeight > 0;
    }

    // how many columns (x-cells) the atlas has
    int GetAtlasColumns() const
    {
        if (!IsValid()) return 0;
        return texture->GetSize().x / frameWidth;
    }

    // how many rows (y-cells) the atlas has
    int GetAtlasRows() const
    {
        if (!IsValid()) return 0;
        return texture->GetSize().y / frameHeight;
    }

    // total frames (cols * rows)
    int GetTotalFrames() const
    {
        return GetAtlasColumns() * GetAtlasRows();
    }

    // (u,v) of top-left corner of frame, with V flipped so that y=0 is top
    core::types::Vec2 GetFrameUV(int index) const
    {
        if (!IsValid()) return {0.0f, 0.0f};

        int cols = GetAtlasColumns();
        int x    = index % cols;
        int y    = index / cols;

        float u = (x * frameWidth)  / float(texture->GetSize().x);
        float v = 1.0f - ((y+1) * frameHeight) / float(texture->GetSize().y);
        return core::types::Vec2(u, v);
    }

    // normalized width/height of one frame
    core::types::Vec2 GetFrameSizeUV() const
    {
        if (!IsValid()) return {0.0f, 0.0f};
        return core::types::Vec2(
            float(frameWidth)  / texture->GetSize().x,
            float(frameHeight) / texture->GetSize().y
        );
    }

    // pixel-space rectangle of a frame: { x, y, w, h }
    struct FrameRect { int x, y, w, h; };
    FrameRect GetFrameRect(int index) const
    {
        int cols = GetAtlasColumns();
        if (cols == 0) return {0,0, frameWidth, frameHeight};

        int fx = index % cols;
        int fy = index / cols;
        return { fx * frameWidth,
                 fy * frameHeight,
                 frameWidth,
                 frameHeight };
    }

    // UV rectangle of a frame: { min, max }
    struct UVRect { core::types::Vec2 min, max; };
    UVRect GetFrameUVRect(int index) const
    {
        auto uvMin  = GetFrameUV(index);
        auto uvSize = GetFrameSizeUV();
        return { uvMin, uvMin + uvSize };
    }
};
