#pragma once
#include "common.hpp"
#include <map>


typedef struct {
    typedef enum { TILE_NONE=0, TILE_FLOOR=1, TILE_WATER=2, TILE_BOX=3, TILE_ROCK=4, TILE_DOOR=5, TILE_GOAL=6, TILE_PLAYER=7, TILE_NUM=8} tex_t;
    typedef enum { TILE_MODEL_FLAT=0, TILE_MODEL_SPIKE_LOW=1, TILE_MODEL_CUBE_LOW=2, TILE_MODEL_SPIKE=3, TILE_MODEL_CUBE=4 } model_t;
    typedef enum { TILE_ACTION_NONE=0, TILE_ACTION_FINISH, TILE_ACTION_GOTO, TILE_ACTION_SWITCH, TILE_ACTION_GOAL } action_t;
    tex_t tile;
    unsigned tile_border_type;
    bool clip;
    bool start;
    bool movable;
    model_t model;
    action_t action;
    union {
        uint32_t action_arg1;
        uint16_t action_arg2[2];
    };
} tile_t;
extern const tile_t tile_defaults[tile_t::TILE_NUM];


class Layout {
    private:
        unsigned* const border_buf;
        static unsigned id_cnt;

    protected:
        tile_t* const buf;
        tile_t* const bottom_buf; // will be initialized with TILE_NONE
        void find_borders(); // generate "shadows"

    public:
        const unsigned w, h;
        const unsigned id;

        Layout(unsigned, unsigned);
        virtual ~Layout();

        tile_t& at(unsigned x, unsigned y) { assert(x<w && y<h); return buf[(y*w)+x]; }
        tile_t& at(unsigned i) { assert(i<w*h); return buf[i]; }
        tile_t& below(unsigned x, unsigned y) { assert(x<w && y<h); return bottom_buf[(y*w)+x]; }
        tile_t& below(unsigned i) { assert(i<w*h); return bottom_buf[i]; }
        const tile_t& at(unsigned x, unsigned y) const { assert(x<w && y<h); return buf[(y*w)+x]; }
        const tile_t& at(unsigned i) const { assert(i<w*h); return buf[i]; }
        unsigned borders(unsigned x, unsigned y) const { assert(x<w && y<h); return border_buf[(y*w)+x]; }

        virtual bool is_done() const { return true; }; // all layouts must be done for great success
        virtual bool autoroute() const { return false; } // allow shortest path routes?
};


class World {
    private:
        unsigned player_x, player_y;
        Layout* const world;

    public:
        const unsigned w, h;
        const unsigned &x, &y;

        World(Layout*);
        ~World();

        bool player_step(int, int);
        bool player_push(int, int);
        bool player_goto(unsigned, unsigned);
        const Layout* world_get() const;
        const tile_t& at(unsigned, unsigned, unsigned&, unsigned&) const;
};


class Worlds {
    private:
        std::map<unsigned, World*> worlds;
        World* get(unsigned);

    public:
        Worlds();
        Worlds(unsigned, World*);
        ~Worlds();

        World* world;

        void push(unsigned, World*);
        bool set(unsigned);
        bool all_done() const;
};