#ifndef BREEZE_MAP_H
#define BREEZE_MAP_H

#include "tileset.h"
#include "../util/string.h"

#define BZ_MAP_MAX_LAYERS 8
#define BZ_MAP_MAX_TILESETS 8

typedef struct BzTileLayer BzTileLayer;
typedef struct BzTileObject BzTileObject;
typedef struct BzTileObjectGroup BzTileObjectGroup;
typedef struct BzTileMap BzTileMap;

typedef void (*BzTileLayerRenderFunc)(BzTileMap *map, BzTileLayer *layer);
typedef void (*BzTileObjectGroupRenderFunc)(BzTileMap *map, BzTileObjectGroup *objectGroup);

extern BzTileLayerRenderFunc BZ_TILE_LAYER_SKIP_RENDER;
extern BzTileObjectGroupRenderFunc BZ_TILE_OBJECTS_SKIP_RENDER;

typedef struct BzTileLayerDesc {
    const char *name; // Matches map layer names
    BzTileLayerRenderFunc renderer;
    bool applyColliders;
} BzTileLayerDesc;
typedef struct BzTileObjectsDesc {
    const char *name; // Matches map layer names
    BzTileObjectGroupRenderFunc renderer;
    BzStringHashFunc hashFunc;
} BzTileObjectsDesc;

typedef struct BzTileMapDesc {
    const char *path;
    bool generateCollisionMap;
    BzTileset tilesets[BZ_MAP_MAX_TILESETS];

    BzTileLayerDesc layers[BZ_MAP_MAX_LAYERS];
    BzTileObjectsDesc objectGroups[BZ_MAP_MAX_LAYERS];
} BzTileMapDesc;

typedef struct BzTileLayer {
    i32 id;

    BzTile *data;
    i32 dataCount;
    BzTile minData;
    BzTile maxData;

    i32 width;
    i32 height;

    f32 offsetX;
    f32 offsetY;
    f32 opacity;

    i32 tilesetIdx;

    BzTileLayerDesc desc;
    bool hasOwnership;
} BzTileLayer;

typedef struct BzTileObject {
    u32 id;
    BzTile gid;
    BzTileShape shape;
} BzTileObject;

typedef struct BzTileObjectGroup {
    BzTileObject *objects;
    i32 objectCount;
    BzTile minGID;
    BzTile maxGID;
    i32 tilesetIdx;

    BzTileObjectsDesc desc;
    bool hasOwnership;
} BzTileObjectGroup;


typedef struct BzTileMap {
    Color backgroundColor;

    i32 width;
    i32 height;
    i32 tileWidth;
    i32 tileHeight;

    bool *collisionMap;

    BzTileLayer layers[BZ_MAP_MAX_LAYERS];
    i32 layerCount;

    BzTileObjectGroup objectGroups[BZ_MAP_MAX_LAYERS];
    i32 objectGroupCount;

    BzTileset tilesets[BZ_MAP_MAX_TILESETS];
    i32 tilesetCount;

    bool isValid;
} BzTileMap;

extern BzTileMap BZ_TILEMAP_INVALID;

// Return true, if you want to override data pointer (you are responsible for cleanup, if you override)
typedef bool (*BzTileLayerFunc)(BzTileMap *map, BzTileLayer *layer);
typedef bool (*BzTileObjectsFunc)(BzTileMap *map, BzTileObjectGroup *objectGroup);

extern BzTileLayerFunc BZ_TILE_LAYER_CLEAR;
extern BzTileObjectsFunc BZ_TILE_OBJECTS_CLEAR;

BzTile bzTileLayerGetTile(BzTileLayer *layer, i32 x, i32 y);
void bzTileLayerSetTile(BzTileLayer *layer, BzTile tile, i32 x, i32 y, i32 w, i32 h);
BzTileset *bzTileLayerGetTileset(BzTileMap *map, BzTileLayer *layer);
BzTileset *bzTileObjectGroupGetTileset(BzTileMap *map, BzTileObjectGroup *objectGroup);

BzTileMap bzTileMapCreate(const BzTileMapDesc *desc);
void bzTileMapDestroy(BzTileMap *map);

void bzTileMapPosToTile(BzTileMap *map, Vector2 pos, BzTile *x, BzTile *y);

void bzTileMapOverrideLayer(BzTileMap *map, i32 slotID, BzTileLayerFunc func);
void bzTileMapOverrideObjectGroup(BzTileMap *map, i32 slotID, BzTileObjectsFunc func);

BzTileLayer *bzTileMapGetLayer(BzTileMap *map, i32 slotID);
BzTileObjectGroup *bzTileMapGetObjects(BzTileMap *map, i32 slotID);

f32 bzTileMapRayCast(BzTileMap *map, Vector2 from, Vector2 to, f32 maxDst, Vector2 *outIntersection);
bool bzTileMapCanRayCastLine(BzTileMap *map, Vector2 from, Vector2 to);

void bzTileMapDraw(BzTileMap *map);
void bzTileMapDrawCollisions(BzTileMap *map);
bool bzTileMapHasCollision(BzTileMap *map, i32 x, i32 y);
void bzTileMapUpdateCollisions(BzTileMap *map, i32 x, i32 y, i32 sizeX, i32 sizeY);



#endif //BREEZE_MAP_H
