#pragma once

#define TILE_MAP typeid(TSE::TileMap).name()

#include <unordered_map>
#include "elements/BehaviourScript.hpp"
#include "Vector2.hpp"
#include "Vector2i.hpp"
#include "elements/Sprite.hpp"
#include "elements/TileSet.hpp"
#include "enums/SortingOrder.hpp"

namespace TSE
{
    struct TileMapChunk
    {
    private:
        std::vector<Vector2> orderedPositions;
        std::vector<Vector2i> orderedSpriteIDs;
        SortingOrder order;
        int chunksize;
        std::unordered_map<Vector2, Vector2i> sprites;
    public:
        bool dirtyPositions = true;
        bool dirtySpriteIds = true;
        Vector2 nextLine;
        Vector2 pos;
        TileMapChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight);

        void SetTile(const Vector2& p, const Vector2& Spriteindex, const Vector2& Normalindex, TileSet* set);
        void RemoveTile(Vector2 p);
        void SetOrdering(SortingOrder _order);
        const std::vector<Vector2>* GetOrderedPositions();
        const std::vector<Vector2i>* GetOrderedSpriteIds();
        int GetChunksize();
        int GetSpriteCount();

    };

    class TileMap : public BehaviourScript
    {
    private:
        bool dirty = true;
        std::vector<Vector2> orderedChunks;
        Rect bounds = Rect(0,0,0,0);
        Vector2 nextLine = Vector2(0.5f, 0.5f);
    public:
        int chunkSize = 16;
        SortingOrder order = BottomRight;
        Vector2 SpriteScale = Vector2(1,1);
        TileSet* set;
        std::unordered_map<Vector2, TileMapChunk> chunks;

        void RemoveTile(Vector2 p);
        void SetTile(Vector2 p, Vector2 Spriteindex, Vector2 Normalindex = {-1,-1});
        TileMapChunk* GetChunk(const Vector2& pos);
        const std::vector<Vector2>* GetChunkPositionsInOrder();
        int GetChunkCount();
        TileSet* GetTileSet();
        const Rect& GetBounds() const { return bounds; }
        void SetNextLineOffset(const Vector2& offset);
        Vector2 GetNextLineOffset();
        Vector2 RealPosToTileMapPos(const Vector2& v);
        Vector2 TileMapToRealPos(const Vector2& v);
        void DirtyAll();

        inline const char* GetName() override
        {
            return "Tile Map";
        }
    private:
        void CheckBounds(Vector2 pos);
        Vector2 LocalToChunkPos(const Vector2& v);
        Vector2 ChunkToLocalPos(const Vector2& v, const TileMapChunk& chunk);
    };
} // namespace TSE
