#pragma once

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

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

namespace TSE
{
    enum SortingOrder
    {
        TopRight,
        TopLeft,
        BottomRight,
        BottomLeft,
    };

    struct TileMapChunk
    {
    private:
        SortingOrder order;
        int chunksize;
        std::unordered_map<Vector2, int> sprites;
    public:
        Vector2 nextLine;
        Vector2 pos;
        TileMapChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight);

        void SetTile(const Vector2& p, const Vector2& Spriteindex, TileSet* set);
        void RemoveTile(Vector2 p);
        void SetOrdering(SortingOrder _order);
        void GetOrderedPositions(Vector2* array);
        void GetOrderedSpriteIds(int* array);
        int GetChunksize();
        int GetSpriteCount();

    };

    class TileMap : public BehaviourScript
    {
    private:
        Rect bounds = Rect(0,0,0,0);
        Vector2 nextLine = Vector2(-0.5f, 1.25f);
    public:
        int chunkSize = 16;
        SortingOrder order = TopRight;
        Vector2 SpriteScale = Vector2(1,1);
        TileSet* set;
        std::unordered_map<Vector2, TileMapChunk> chunks;

        void RemoveTile(Vector2 p);
        void SetTile(Vector2 p, Vector2 Spriteindex);
        TileMapChunk* GetChunk(const Vector2& pos);
        void GetChunkPositionsInOrder(Vector2* arr);
        int GetChunkCount();
        TileSet* GetTileSet();
        const Rect& GetBounds() const { return bounds; }
        void SetNextLineOffset(const Vector2& offset);
        Vector2 GetNextLineOffset();

        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
