#ifndef __MAP_H__
#define __MAP_H__
#include <vector>
#include <string>
#include <utility>

#include "output.h"
#include "position.h"
#include "rng.h"
#include "fraction.h"
#include "player.h"
#include "characters.h"

struct level_data;

class game_map final {
private:
        static const int MAX_ROOM_CNT      = 20;
        static const int MIN_ROOM_WIDTH    = 3;
        static const int MIN_ROOM_HEIGHT   = 3;
        static const int MAX_ROOM_WIDTH    = 20;
        static const int MAX_ROOM_HEIGHT   = 5;
        // setup safezones
        static const int ACTUAL_MAP_WIDTH  = 73;
        static const int ACTUAL_MAP_HEIGHT = 20;
        static const int MAP_PADDING       = 3;
        static const int MIN_ROOM_SPACING  = 3;
        static const int WIDTH_RESERVED    = 6;
        static const int HEIGHT_RESERVED   = 3;

        const feature enabled_features;
        std::vector<char> map;
        position up_stairs;
        position down_stairs;
        std::vector<position_list> rooms_tile_list;
public:
        struct room {
                int top;
                int left;
                int width;
                int height;
                position ldoor;
                position rdoor;
                position tdoor;
                position bdoor;
                bool operator!=(const room &r) {
                        return !(*this == r);
                }
                bool operator==(const room &r) {
                        return top == r.top &&
                               left == r.left &&
                               width == r.width &&
                               height == r.height;
                }
        };
        // randomly generate a map
        game_map(character *player, RNG *rng, const feature enabled_features);
        // read map from a string
        game_map(const level_data &lvl, character *player, RNG *rng,
                 const feature enabled_features);

        bool is_available(const position &pos) const;
        position_list get_available_around(const position &pos) const;
        int which_room(const position &pos) const;

        position_list get_room_list(int idx) const;
        std::vector<position_list> get_room_list() const;
        // IMPORTANT: always print a map before anything else
        void print(output *out) const;

        position get_up_stairs() const;
        position get_down_stairs() const;

        int get_up_stairs_room() const;
        int get_down_stairs_room() const;

        int get_room_cnt() const;
private:
        position get_available_spot(const level_data &lvl, int room, RNG *rng);
        std::vector<room> room_data;
        position remap_index(const int idx) const;
        int remap_position(const position &pos) const;

        position random_size(int min_width, int min_height,
                             int max_width, int max_height,
                             RNG *rng);

        std::vector<std::pair<position, int>> gen_room_dims(RNG *rng);
        std::vector<room> distr_rooms(RNG *rng,
                                      std::vector<std::pair<position, int>> &room_dims,
                                      std::vector<std::pair<position, int>> &layer_data);

        bool overlap_x(room &room1,
                       room &room2);
        bool overlap_y(room &room1,
                       room &room2);

        void jitter(RNG *rng, std::vector<room> &rooms);

        void fill_room(const room &r, const int num);
        void fill_outline();

        void gen_path(std::vector<std::pair<position, int>> &layer_data, RNG *rng);
        // IMPORTANT: assumes room1 is to the left of room 2
        void connect_x(room &room1, room &room2, RNG *rng);
        // IMPORTANT: assumes room1 is under room2
        void connect_y(room &room1, room &room2, RNG *rng);

        void connect(position s, position t);
        room hit_room(const position &a);
        bool hit_room(const position &a, const room &r);

        void gen_stairs(const level_data &lvl, int player_room, RNG *rng);
        room get_room(std::size_t idx) const;
};

const std::string DEFAULT_MAP =
        "|-----------------------------------------------------------------------------|\
|                                                                             |\
| |--------------------------|        |-----------------------|               |\
| |11111111111111111111111111|        |33333333333333333333333|               |\
| |11111111111111111111111111+########+33333333333333333333333|-------|       |\
| |11111111111111111111111111|   #    |3333333333333333333333333333333|--|    |\
| |11111111111111111111111111|   #    |3333333333333333333333333333333333|--| |\
| |----------+---------------|   #    |----+----------------|333333333333333| |\
|            #                 #############                |333333333333333| |\
|            #                 #     |-----+------|         |333333333333333| |\
|            #                 #     |444444444444|         |333333333333333| |\
|            ###################     |444444444444|   ######+333333333333333| |\
|            #                 #     |444444444444|   #     |333333333333333| |\
|            #                 #     |-----+------|   #     |--------+------| |\
|  |---------+-----------|     #           #          #              #        |\
|  |222222222222222222222|     #           #          #         |----+------| |\
|  |222222222222222222222|     ########################         |00000000000| |\
|  |222222222222222222222|     #           #                    |00000000000| |\
|  |222222222222222222222|     #    |------+--------------------|00000000000| |\
|  |222222222222222222222|     #    |000000000000000000000000000000000000000| |\
|  |222222222222222222222+##########+000000000000000000000000000000000000000| |\
|  |222222222222222222222|          |000000000000000000000000000000000000000| |\
|  |---------------------|          |---------------------------------------| |\
|                                                                             |\
|-----------------------------------------------------------------------------|";

#endif
