/******************************************************************************
*	File		-	tile_map.h
*	Author		-	Joey Pollack
*	Date		-	2022/02/15 (y/m/d)
*	Mod Date	-	2022/02/15 (y/m/d)
*	Description	-	Contains a single tile map
******************************************************************************/

#include "tile_map.h"
#include "tile_set.h"
#include <core/core.h>
#include <graphics/graphics.h>
#include <editor/editor.h>
#include <utils/logger.h>

namespace lunarium { namespace editor
{
    
    TileMap::TileMap(Sizei tile_size)
        : mpMap(nullptr), mTileSize(tile_size), mIsDirty(false)
    {

    }

    OpRes TileMap::AddTileSet(int id, TileSet* set)
    {
        if (mTileSets.find(id) != mTileSets.end())
        {
            return OpRes::Fail("Cannot add tile set to Tile Map - Tile Set with id: %d already exists", id);
        }

        if (set->GetTileSize() != mTileSize)
        {
            return OpRes::Fail("Cannot add tile set to Tile Map - Tile size does not match");
        }

        mTileSets[id] = set;
        return OpRes::OK();
    }

    // NOTE: This could probably be replaced with code that marks it clean on save
    //          since this class will have to implement saving to a file (because it's an asset).
    void TileMap::MarkClean()
    {
        mIsDirty = false;
    }

    bool TileMap::IsDirty() const
    {
        return mIsDirty;
    }

    void TileMap::ClearMap()
    {
        for(int i = 0; i < mSizeInTiles.Width; i++)
        {
            for (int j = 0; j < mSizeInTiles.Height; j++)
            {
                mpMap[i][j] = { -1, {-1, -1} };
            }
        }

        mIsDirty = true;
    }


    void TileMap::SetTile(TileRef tile, Vec2i location)
    {
        if (location.X < 0 || location.X >= mSizeInTiles.Width || location.Y < 0 || location.Y >= mSizeInTiles.Height)
        {
            return;
        }

        mpMap[location.X][location.Y] = tile;
        mIsDirty = true;
    }

    TileRef TileMap::GetTile(Vec2i location)
    {
        if (location.X < 0 || location.X >= mSizeInTiles.Width || location.Y < 0 || location.Y >= mSizeInTiles.Height)
        {
            return {-1, {-1, -1 }};
        }

       TileRef tile = mpMap[location.X][location.Y];

        // Make sure the tileset exists
        if (mTileSets.find(tile.TileSetID) == mTileSets.end())
        {
            return {-1, {-1, -1 }};
        }

        return tile;
    }
    
    Sizei TileMap::GetSizeInTiles()
    {
        return mSizeInTiles;
    }

    Sizei TileMap::GetSizeInPixels()
    {
        if (!mpMap)
            return { 0, 0 };

        return { mTileSize.Width * mSizeInTiles.Width, mTileSize.Height * mSizeInTiles.Height };
    }

    Sizei TileMap::GetTileSize()
    {
        return mTileSize;
    }
    
    void TileMap::ResizeMap(Sizei size)
    {
        TileRef** new_map = new TileRef*[size.Width];
        for(int i = 0; i < size.Width; i++) 
        {
            new_map[i] = new TileRef[size.Height];
        }

        int smaller_width = mSizeInTiles.Width < size.Width ? mSizeInTiles.Width : size.Width;
        int smaller_height = mSizeInTiles.Height < size.Height ? mSizeInTiles.Height : size.Height;

        for(int i = 0; i < smaller_width; i++)
        {
            for (int j = 0; j < smaller_height; j++)
            {
                new_map[i][j] = mpMap[i][j];
            }
        }

        for(int i = 0; i < mSizeInTiles.Width; i++)
        {
            delete[] mpMap[i];
        }

        delete[] mpMap;
        mSizeInTiles = size;
        mpMap = new_map;
        mIsDirty = true;
    }

    
    void TileMap::Render(lunarium::IGraphics* g)
    {
        if (!mpMap)
            return;

        Sizei map_size_pixels = { mTileSize.Width * mSizeInTiles.Width, mTileSize.Height * mSizeInTiles.Height };

        // Draw Map
        for (int i = 0; i < mSizeInTiles.Width; i++)
        {
            for (int j = 0; j < mSizeInTiles.Height; j++)
            {
                // If there is no tile here skip this spot
                if (mpMap[i][j].TileIndex.X == -1 || mpMap[i][j].TileIndex.Y == -1)
                    continue;   

                int id = mpMap[i][j].TileSetID;

                // -1 means an intentionally empty tile
                if (-1 == id)
                {
                    continue;
                }

                // Make sure the tileset exists
                if (mTileSets.find(id) == mTileSets.end())
                {
                    Logger::Warn(Editor::LogCat, "Tile Map is missing tile set. No Tile Set with id: %d", id);
                    continue;
                }
                
                TileSet* set = mTileSets[id];
                Rectangle dest = Rectangle::MakeFromTopLeft(i * mTileSize.Width, j * mTileSize.Height, mTileSize.Width, mTileSize.Height);
                Rectangle src = set->GetTileRect(mpMap[i][j].TileIndex);

                g->DrawImage(*set->GetImage(), src, dest, Color::White());
                //g->DrawImage(*set->GetImage(), glm::vec2(dest.left(), dest.top()), Color::White());
                //g->DrawImage(*set->GetImage(), Rectangle::MakeFromTopLeft(0, 0, 1024, 1024), dest, Color::White());
                //g->DrawImage(*set->GetImage(), Rectangle::MakeFromTopLeft(16, 0, 16, 16), Rectangle::MakeFromTopLeft(0, 0, 500, 500), Color::White());
            }
        }

        // Draw grid
        for (int i = 0; i < mSizeInTiles.Width; i++)
        {
            g->DrawLine(glm::vec2(i * mTileSize.Width, 0), glm::vec2(i * mTileSize.Width, map_size_pixels.Height), Color::Black(), 1.0f);
        }
        g->DrawLine(glm::vec2(map_size_pixels.Width, 0), glm::vec2(map_size_pixels.Width, map_size_pixels.Height), Color::Black(), 1.0f);

        for (int j = 0; j < mSizeInTiles.Height; j++)
        {
            g->DrawLine(glm::vec2(0, j * mTileSize.Height), glm::vec2(map_size_pixels.Width, j * mTileSize.Height), Color::Black(), 1.0f);
        }
        g->DrawLine(glm::vec2(0, map_size_pixels.Height), glm::vec2(map_size_pixels.Width, map_size_pixels.Height), Color::Black(), 1.0f);
    }
}}
