#include "TilemapComponent.h"
#include "../Renderer.h"
#include <iostream>

TilemapComponent::TilemapComponent(Object* owner)
    : Component(owner)
{
    renderType = RenderType::Unlit;
    hasSelection = false;
}

uint64_t TilemapComponent::PackCoord(int x, int y)
{
    return (static_cast<uint64_t>(static_cast<uint32_t>(x)) << 32) |
           static_cast<uint32_t>(y);
}

void TilemapComponent::UnpackCoord(uint64_t key, int& x, int& y)
{
    x = static_cast<int32_t>(key >> 32);
    y = static_cast<int32_t>(key & 0xFFFFFFFF);
}


bool TilemapComponent::HasSelection()
{
    return hasSelection;

}


void TilemapComponent::SetTextureAtlas(uint64_t uaid, int texelWidth, int texelHeight)
{
    textureUAID = uaid;

    const auto* asset = AssetManager::GetAssetByID(uaid);
    if (!asset || !asset->loaded)
    {
        Logger::LogError("TilemapComponent: Asset not found or not loaded (UAID: %llu)", uaid);
        return;
    }

    const auto* imageAsset = dynamic_cast<const ImageAssetInfo*>(asset);
    if (!imageAsset)
    {
        Logger::LogError("TilemapComponent: Asset with UAID %llu is not an image", uaid);
        return;
    }

    texture = std::make_shared<Texture>(imageAsset->uaid);

    atlas.texture = texture;
    atlas.SetTexelSize(texelWidth, texelHeight);

    int texWidth = static_cast<int>(imageAsset->size.x);
    int texHeight = static_cast<int>(imageAsset->size.y);

    atlasColumns = texWidth / texelWidth;
    atlasRows = texHeight / texelHeight;
}


void TilemapComponent::SetTile(int x, int y, int index)
{
    uint64_t key = PackCoord(x, y);
    if (index >= 0)
        tiles[key] = index;
    else
        tiles.erase(key);
}

int TilemapComponent::GetTile(int x, int y) const
{
    uint64_t key = PackCoord(x, y);
    auto it = tiles.find(key);
    return (it != tiles.end()) ? it->second : -1;
}



void TilemapComponent::PlaceSelection(int gx, int gy)
{
    if (!hasSelection) return;

    int x0 = std::min(selStartX, selEndX);
    int y0 = std::min(selStartY, selEndY);
    int x1 = std::max(selStartX, selEndX);
    int y1 = std::max(selStartY, selEndY);

    int width = x1 - x0 + 1;
    int height = y1 - y0 + 1;

    for (int y = 0; y < height; ++y)
    {
        for (int x = 0; x < width; ++x)
        {
            int srcX = x0 + x;
            int srcY = y0 + y;
            int index = srcY * GetAtlasColumns() + srcX;

            SetTile(gx + x, gy + y, index);
        }
    }
}



// Accessors
uint64_t TilemapComponent::GetTextureUAID() const { return textureUAID; }
std::shared_ptr<Texture> TilemapComponent::GetTexture() const { return texture; }
TextureAtlas* TilemapComponent::GetAtlas() {
    return &atlas;
}

const TextureAtlas* TilemapComponent::GetAtlas() const {
    return &atlas;
}
const core::types::Vec2& TilemapComponent::GetTileSize() const { return tileSize; }
void TilemapComponent::SetTileSize(const core::types::Vec2& size) { tileSize = size; }
int TilemapComponent::GetAtlasColumns() const { return atlasColumns; }
int TilemapComponent::GetAtlasRows() const { return atlasRows; }
const std::unordered_map<uint64_t, int>& TilemapComponent::GetTileData() const { return tiles; }

// Save to YAML
void TilemapComponent::Save(YAML::Emitter& out) const
{
    out << YAML::BeginMap;
    out << YAML::Key << "type" << YAML::Value << GetName();
    out << YAML::Key << "TextureUAID" << YAML::Value << textureUAID;
    out << YAML::Key << "TexelWidth" << YAML::Value << atlas.GetTexelSize().x;
    out << YAML::Key << "TexelHeight" << YAML::Value << atlas.GetTexelSize().y;

    out << YAML::Key << "TileSize" << YAML::Value << YAML::BeginMap;
    out << YAML::Key << "x" << YAML::Value << tileSize.x;
    out << YAML::Key << "y" << YAML::Value << tileSize.y;
    out << YAML::EndMap;

    out << YAML::Key << "Tiles" << YAML::Value << YAML::BeginSeq;
    for (const auto& [key, index] : tiles)
    {
        int x, y;
        UnpackCoord(key, x, y);
        out << YAML::Flow << YAML::BeginSeq << x << y << index << YAML::EndSeq;
    }
    out << YAML::EndSeq;
    out << YAML::EndMap;
}



void TilemapComponent::Load(const YAML::Node& node)
{
    textureUAID = node["TextureUAID"].as<uint64_t>();
    int texelWidth = node["TexelWidth"].as<int>();
    int texelHeight = node["TexelHeight"].as<int>();
    SetTextureAtlas(textureUAID, texelWidth, texelHeight);

    if (node["TileSize"])
    {
        tileSize.x = node["TileSize"]["x"].as<float>();
        tileSize.y = node["TileSize"]["y"].as<float>();
    }

    tiles.clear();
    if (node["Tiles"])
    {
        for (const auto& tile : node["Tiles"])
        {
            if (!tile.IsSequence() || tile.size() != 3) continue;
            int x = tile[0].as<int>();
            int y = tile[1].as<int>();
            int index = tile[2].as<int>();
            SetTile(x, y, index);
        }
    }
}
