#include <iostream>
#include <iomanip>

#include "TileMap.hpp"

// Creates a new tile map.
// rows and cols are how many different tiles there are in the sprite sheet
//
// _TileWidth and _TileHeight are the size of each individual
// tile in the sprite sheet.
// (Typically this works best if they are square for this implementation.)
//
// _mapX, and _mapY are the size of the tilemap. This is the actual
// number of tiles in the game that the player sees, not how many tiles
// are in the actual sprite sheet file loaded.
TileMapComponent::TileMapComponent(std::string tileSheetFileName, int rows, int cols, int _TileWidth, int _TileHeight,
                                   int _mapX, int _mapY, SDL_Renderer *ren, std::vector<GameObject *>* gameobjects)
{
    m_gameobjects = gameobjects;
    if (nullptr == ren)
    {
        SDL_Log("No valid renderer found");
    }

    // Setup variables
    m_Rows = rows;
    m_Cols = cols;
    m_TileWidth = _TileWidth;
    m_TileHeight = _TileHeight;
    m_MapX = _mapX;
    m_MapY = _mapY;
    // Load the TileMapComponent Image
    // This is the image that will get
    // sliced into smaller subsections of individual tiles.
    m_TileSpriteSheet = SDL_LoadBMP(tileSheetFileName.c_str());

    if (nullptr == m_TileSpriteSheet)
    {
        SDL_Log("Failed to allocate surface");
    }
    else
    {
        // Create a texture from our surface
        // Textures run faster and take advantage of
        //  hardware acceleration
        m_Texture = SDL_CreateTextureFromSurface(ren, m_TileSpriteSheet);
    }

    // Setup the TileMapComponent array
    // This sets each tile to '0'
    m_Tiles = new int[m_MapX * m_MapY];
    for (int i = 0; i < m_MapX * m_MapY; i++)
    {
        m_Tiles[i] = -1; // Default value is no tile.
    }
}

// Destructor
TileMapComponent::~TileMapComponent()
{
    SDL_DestroyTexture(m_Texture);
    // Remove our TileMapComponent
    delete[] m_Tiles;
}

// Helper function to gegenerate a simlpe map
void TileMapComponent::GenerateSimpleMap()
{
    for (int y = 0; y < m_MapY; y++)
    {
        for (int x = 0; x < m_MapX; x++)
        {

            bool waterMid = x > 3 && x <= m_MapX - 6;
            bool farMid = x > 1 && x <= m_MapX - 4;
            bool center = x > 6 && x <= m_MapX - 9;

            if (y == 5 && center)
            {
                SetTile(x, y, 4);
            }

            if (y == 3 && farMid && !waterMid)
            {
                SetTile(x, y, 4);
            }

            if (y == 9)
            {
                SetTile(x, y, waterMid ? 2 : 0);
            }

            if (y > 9)
            {
                SetTile(x, y, waterMid ? 3 : 1);
            }

            // if (y == 0) {
            //     SetTile(x, y, 12);
            // }
            // if (y == m_MapY - 1) {
            //     SetTile(x, y, 0);
            // }
        }
    }
}

// Helper function to print out the tile map
// to the console
void TileMapComponent::PrintMap()
{
    for (int y = 0; y < m_MapY; y++)
    {
        for (int x = 0; x < m_MapX; x++)
        {
            std::cout << std::setw(3) << GetTileType(x, y);
        }
        std::cout << "\n";
    }
}

// Sets a tile a certain type
void TileMapComponent::SetTile(int x, int y, int type)
{
    m_Tiles[y * m_MapX + x] = type;
    std::string pendingName = std::to_string(x) + std::to_string(x);
    bool exists = false;
    auto it = m_gameobjects->begin();
    for (it; it != m_gameobjects->end(); it++) {
        if((*it)->GetName() == pendingName) {
            exists = true;
        }
    }

    if(type >= 0 && !exists) { //if it isn't the sky, create a rigidbody on that tile
        std::string pendingName = std::to_string(x * m_TileWidth) + std::to_string(y * m_TileHeight);
        GameObject* tileObject = new GameObject(pendingName);
        Component* hitbox = new RigidbodyComponent(new TransformComponent(x * m_TileWidth,y * m_TileHeight), Vec2D(64,64), Vec2D(0,0), 1000.0f, false, false);
        tileObject->AddComponent(hitbox);
        m_gameobjects->push_back(tileObject);
    }
    else if (type < 0 & exists) { // delete the body
        m_gameobjects->erase(it);
    }
}

// Returns what the tile is at a specific position.
int TileMapComponent::GetTileType(int x, int y)
{
    return m_Tiles[y * m_MapX + x];
}

// render TileMapComponent
void TileMapComponent::Render(SDL_Renderer *ren)
{
    if (nullptr == ren)
    {
        SDL_Log("No valid renderer found");
    }

    SDL_Rect Src, Dest;
    for (int y = 0; y < m_MapY; y++)
    {
        for (int x = 0; x < m_MapX; x++)
        {
            // Select our Tile
            int currentTile = GetTileType(x, y);
            if (currentTile > -1)
            {
                // Reverse lookup, given the tile type
                // and then figuring out how to select it
                // from the texture atlas.
                Src.x = (currentTile % m_Cols) * m_TileWidth;
                Src.y = (currentTile / m_Rows) * m_TileHeight;
                Src.w = m_TileWidth;
                Src.h = m_TileHeight;
                // Render our Tile at this location
                Dest.x = x * m_TileWidth;
                Dest.y = y * m_TileHeight;
                Dest.w = m_TileWidth;
                Dest.h = m_TileHeight;
                SDL_RenderCopy(ren, m_Texture, &Src, &Dest);
            }
        }
    }
}

void TileMapComponent::Update(SDL_Event e, int frame, std::vector<std::string> broadcastList)
{
    ParseAndUpdateBroadcast(broadcastList);
}

void TileMapComponent::ShowComponentEditor(bool *showEditor)
{
    ImGuiWindowFlags window_flags = 0;
    window_flags |= ImGuiWindowFlags_MenuBar;
    window_flags |= ImGuiWindowFlags_NoCollapse;

    if (ImGui::Begin("Editing TileMap Component", showEditor, window_flags))
    {
        // TODO: Show visuals of each tile type
        if (ImGui::BeginTabBar("Editing"))
        {
            if (ImGui::BeginTabItem("Map"))
            {
                static int selectedX;
                static int selectedY;
                static int selectedType;

                ImGui::Text("Select a tile to edit");
                ImGui::InputInt("X", &selectedX);
                ImGui::InputInt("Y", &selectedY);
                ImGui::Text("Set the tile's type");
                ImGui::InputInt("Type", &selectedType);
                if (ImGui::Button("Set Tile"))
                {
                    SetTile(selectedX, selectedY, selectedType);
                }
                ImGui::EndTabItem();
            }
            if (ImGui::BeginTabItem("File"))
            {
                ImGui::InputInt("Rows", &m_Rows);
                ImGui::InputInt("Columns", &m_Cols);
                ImGui::InputInt("Tile Width", &m_TileWidth);
                ImGui::InputInt("Tile Height", &m_TileHeight);
                ImGui::EndTabItem();
            }
            ImGui::EndTabBar();
        }
        ImGui::End();
    }
}
