#include "rooms.h"
#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>
#include <map>
#include "images.h"

namespace fs = std::filesystem;

namespace rooms
{
    int num_total_rooms = 0;
    std::string current_room = "";
    std::map<std::string, room_t> rooms;

    static const char *paletteMap[] = {
        "black",    "bright_black",     "blue",     "bright_blue",
        "red",      "bright_red",       "magenta",  "bright_magenta",
        "green",    "bright_green",     "cyan",     "bright_cyan",
        "yellow",   "bright_yellow",    "white",    "bright_white"
    };
    const uint8_t colorToNum(std::string value)
    {
        for (int i=0; i<16; ++i) if (value == paletteMap[i]) return i;
        return 0;
    }

    // Trim from start (in-place)
    inline void ltrim(std::string& s) {
        s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            [](unsigned char ch) { return !std::isspace(ch); }));
    }

    // Trim from end (in-place)
    inline void rtrim(std::string& s) {
        s.erase(std::find_if(s.rbegin(), s.rend(),
            [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
    }

    // Trim both ends (in-place)
    inline void trim(std::string& s) {
        ltrim(s);
        rtrim(s);
    }

    void processKeyValue(const std::string& room_name, const std::string& section, const std::string& key, const std::string& value)
    {
        room_t& room = rooms[room_name];

        if (section == "global") {
            if (key == "name") {
                room.name = value;
            } else if (key == "bgColor") {
                room.bgColor = colorToNum(value);
            } else if (key == "border") {
                room.border = colorToNum(value);
            //} else if (key == "tileMapFile") {
            //    room.tileMapFile = value;
            } else if (key == "tileSetFile") {
                room.tileSetFile = images::getImage("tilesets/"+value);
            } else if (key == "roomUp") {
                room.roomUp = value;
            } else if (key == "roomDown") {
                room.roomDown = value;
            } else if (key == "roomLeft") {
                room.roomLeft = value;
            } else if (key == "roomRight") {
                room.roomRight = value;
            } else if (key == "itemColor1") {
                room.itemColor1 = colorToNum(value);
            } else if (key == "itemColor2") {
                room.itemColor2 = colorToNum(value);
            }
        } else if (section == "enemy") {
            if (key == "animation") {
                room.enemies[room.enemies.size()-1].animation = value;
            } else if (key == "x") {
                room.enemies[room.enemies.size()-1].x = std::stoi(value);
            } else if (key == "y") {
                room.enemies[room.enemies.size()-1].y = std::stoi(value);
            } else if (key == "vx") {
                room.enemies[room.enemies.size()-1].vx = std::stof(value);
            } else if (key == "vy") {
                room.enemies[room.enemies.size()-1].vy = std::stof(value);
            } else if (key == "x1") {
                room.enemies[room.enemies.size()-1].x1 = std::stoi(value);
            } else if (key == "y1") {
                room.enemies[room.enemies.size()-1].y1 = std::stoi(value);
            } else if (key == "x2") {
                room.enemies[room.enemies.size()-1].x2 = std::stoi(value);
            } else if (key == "y2") {
                room.enemies[room.enemies.size()-1].y2 = std::stoi(value);
            } else if (key == "color") {
                room.enemies[room.enemies.size()-1].color = colorToNum(value);
            } else if (key == "flip") {
                room.enemies[room.enemies.size()-1].flip = value=="true" ? true : false;
            } else if (key == "mirror") {
                room.enemies[room.enemies.size()-1].mirror = value=="true" ? true : false;
            }
        } else if (section == "item") {
            if (key == "tile") {
                room.items[room.items.size()-1].tile = std::stoi(value);
            } else if (key == "x") {
                room.items[room.items.size()-1].x = std::stoi(value);
            } else if (key == "y") {
                room.items[room.items.size()-1].y = std::stoi(value);
            } else if (key == "counter") {
                room.items[room.items.size()-1].counter = std::stof(value);
            }
        }
    }

    void loadRoom(fs::path filename)
    {
        std::string room_name = filename.stem().string();

        std::ifstream file(filename);
        if (!file) {
            std::cerr << "Failed to open file.\n";
            return;
        }

        std::string current_section = "global";

        std::string line;
        while (std::getline(file, line))
        {
            //std::cout << "'" << line << "'\n";
            // Trim and clean line, ignore empty lines and comments
            trim(line);
            if (line.empty() || line[0] == '#') continue;

            // Remove inline comments
            std::size_t comment_pos = line.find('#');
            if (comment_pos != std::string::npos) {
                line = line.substr(0, comment_pos);
                trim(line);
            }

            // Check if entering or exiting a section
            if (line[0] == '[')
            {
                std::size_t closing_pos = line.find(']');
                if (closing_pos != std::string::npos && closing_pos > 1)
                {
                    std::string section = line.substr(1,closing_pos-1);
                    if (section[0] == '/') {
                        current_section = "global";
                    } else {
                        current_section = section;
                        if (section == "enemy") rooms[room_name].enemies.emplace_back();
                        else if (section == "item") rooms[room_name] .items.emplace_back();
                    }
                }
            }

            // Get and process key/value
            std::size_t eq_pos = line.find('=');
            if (eq_pos != std::string::npos) {
                std::string key = line.substr(0, eq_pos);
                std::string value = line.substr(eq_pos + 1);
                trim(key);
                trim(value);
                processKeyValue(room_name, current_section, key, value);
            }
        }
    }

    void loadTiles(fs::path filename)
    {
        filename.replace_extension(".tmx");
        std::string room_name = filename.stem().string();
        std::string line;
        bool inData = false;
        int row = 0, col = 0;

        std::ifstream file(filename);
        if (!file) {
            std::cerr << "Failed to open file.\n";
            return;
        }

        room_t& room = rooms[room_name];

        while (std::getline(file, line)) {
            if (!inData) {
                if (line.find("<data encoding=\"csv\">") != std::string::npos) inData = true;
                continue;
            }

            if (line.find("</data>") != std::string::npos) break;

            std::stringstream ss(line);
            std::string value;
            while (std::getline(ss, value, ',')) {
                if (!value.empty()) {
                    room.tiles[col][row] = std::stoi(value);
                    ++col;
                    if (col == 32) {
                        col = 0;
                        ++row;
                    }
                }
            }
        }
    }

    void load()
    {
        // Get all room files
        fs::path target_dir = "./data/room";
        std::vector<fs::path> room_files;

        try {
            for (const auto& entry : fs::directory_iterator(target_dir)) {
                if (entry.is_regular_file() && entry.path().extension() == ".room") {
                    room_files.push_back(entry.path());
                }
            }

            std::sort(room_files.begin(), room_files.end());

            for (const auto& path : room_files) {
                std::cout << "Processing " << path.filename() << '\n';
                loadRoom(path);
                loadTiles(path);
            }

        } catch (const fs::filesystem_error& e) {
            std::cerr << "Filesystem error: " << e.what() << '\n';
        }

        /*for (auto &item : rooms)
        {
            room_t &room = item.second;
            std::cout << room.name << std::endl;

            for (int y=0; y<16; ++y) {
                for (int x=0; x<32; ++x)
                    std::cout << std::to_string(room.tiles[x][y]) << ",";
                std::cout << std::endl;
            }
        }*/
    }

    void draw()
    {
        room_t &room = rooms["02"];
        draw::setSource(room.tileSetFile);

        for (int y=0; y<16; ++y) {
            for (int x=0; x<32; ++x) {
                uint16_t tile = room.tiles[x][y];
                if (tile>0) {
                    tile--;
                    draw::draw(x*8, y*8, 8, 8, (tile%24)*8, (tile/24)*8);
                }
            }
        }
    }
}
