#include <string>
#include <set>
#include <utility>

#include <symbols/minecraft.h>
#include <libreborn/libreborn.h>

#include <mods/text-input-box/TextInputScreen.h>
#include <mods/touch/touch.h>
#include <mods/misc/misc.h>
#include <mods/game-mode/game-mode.h>
#include "game-mode-internal.h"

// Strings
#define GAME_MODE_STR(mode) ("Game Mode: " mode)
#define SURVIVAL_STR GAME_MODE_STR("Survival")
#define CREATIVE_STR GAME_MODE_STR("Creative")

// Structure
EXTEND_STRUCT(CreateWorldScreen, Screen, struct {
    TextInputScreen text_input;
    TextInputBox *name;
    TextInputBox *seed;
    Button *game_mode;
    Button *create;
    Button *back;
});
static void create_world(Minecraft *minecraft, std::string name, bool is_creative, std::string seed_str);
CUSTOM_VTABLE(create_world_screen, Screen) {
    TextInputScreen::setup<CreateWorldScreen>(vtable);
    // Constants
    static int line_height = 8;
    static int bottom_padding = 4;
    static int inner_padding = 4;
    static int description_padding = 4;
    static int title_padding = 8;
    static int button_height = 24;
    static int content_y_offset_top = (title_padding * 2) + line_height;
    static int content_y_offset_bottom = button_height + (bottom_padding * 2);
    // Init
    static Screen_init_t original_init = vtable->init;
    vtable->init = [](Screen *super) {
        original_init(super);
        CreateWorldScreen *self = (CreateWorldScreen *) super;
        // Name
        self->data.name = new TextInputBox("World Name", "Unnamed world");
        self->data.text_input.m_textInputs->push_back(self->data.name);
        self->data.name->init(super->font);
        self->data.name->setFocused(true);
        // Seed
        self->data.seed = new TextInputBox("Seed");
        self->data.text_input.m_textInputs->push_back(self->data.seed);
        self->data.seed->init(super->font);
        self->data.seed->setFocused(false);
        // Game Mode
        self->data.game_mode = touch_create_button(1, CREATIVE_STR);
        super->rendered_buttons.push_back(self->data.game_mode);
        super->selectable_buttons.push_back(self->data.game_mode);
        // Create
        self->data.create = touch_create_button(2, "Create");
        super->rendered_buttons.push_back(self->data.create);
        super->selectable_buttons.push_back(self->data.create);
        // Back
        self->data.back = touch_create_button(3, "Back");
        super->rendered_buttons.push_back(self->data.back);
        super->selectable_buttons.push_back(self->data.back);
    };
    // Removal
    static Screen_removed_t original_removed = vtable->removed;
    vtable->removed = [](Screen *super) {
        original_removed(super);
        CreateWorldScreen *self = (CreateWorldScreen *) super;
        delete self->data.name;
        delete self->data.seed;
        self->data.game_mode->destructor_deleting();
        self->data.back->destructor_deleting();
        self->data.create->destructor_deleting();
    };
    // Rendering
    static Screen_render_t original_render = vtable->render;
    vtable->render = [](Screen *super, const int x, const int y, const float param_1) {
        // Background
        misc_render_background(80, super->minecraft, 0, 0, super->width, super->height);
        misc_render_background(32, super->minecraft, 0, content_y_offset_top, super->width, super->height - content_y_offset_top - content_y_offset_bottom);
        // Call Original Method
        original_render(super, x, y, param_1);
        // Title
        std::string title = "Create world";
        super->drawCenteredString(super->font, title, super->width / 2, title_padding, 0xffffffff);
        // Game Mode Description
        CreateWorldScreen *self = (CreateWorldScreen *) super;
        const bool is_creative = self->data.game_mode->text == CREATIVE_STR;
        std::string description = is_creative ? Strings::creative_mode_description : Strings::survival_mode_description;
        super->drawString(super->font, description, self->data.game_mode->x, self->data.game_mode->y + self->data.game_mode->height + description_padding, 0xa0a0a0);
    };
    // Positioning
    static Screen_setupPositions_t original_setupPositions = vtable->setupPositions;
    vtable->setupPositions = [](Screen *super) {
        original_setupPositions(super);
        CreateWorldScreen *self = (CreateWorldScreen *) super;
        // Height/Width
        constexpr int width = 120;
        const int height = button_height;
        self->data.create->width = self->data.back->width = self->data.game_mode->width = width;
        int seed_width = self->data.game_mode->width;
        int name_width = width * 1.5f;
        self->data.create->height = self->data.back->height = self->data.game_mode->height = height;
        int text_box_height = self->data.game_mode->height;
        // Find Center Y
        const int top = content_y_offset_top;
        const int bottom = super->height - content_y_offset_bottom;
        int center_y = ((bottom - top) / 2) + top;
        center_y -= (description_padding + line_height) / 2;
        // X/Y
        self->data.create->y = self->data.back->y = super->height - bottom_padding - height;
        self->data.create->x = self->data.game_mode->x = (super->width / 2) - inner_padding - width;
        self->data.back->x = (super->width / 2) + inner_padding;
        const int seed_x = self->data.back->x;
        const int name_x = (super->width / 2) - (name_width / 2);
        const int name_y = center_y - inner_padding - height;
        self->data.game_mode->y = center_y + inner_padding;
        const int seed_y = self->data.game_mode->y;
        // Update Text Boxes
        self->data.name->setSize(name_x, name_y, name_width, text_box_height);
        self->data.seed->setSize(seed_x, seed_y, seed_width, text_box_height);
    };
    // ESC
    vtable->handleBackEvent = [](Screen *super, const bool do_nothing) {
        if (!do_nothing) {
            super->minecraft->screen_chooser.setScreen(5);
        }
        return true;
    };
    // Button Click
    vtable->buttonClicked = [](Screen *super, Button *button) {
        CreateWorldScreen *self = (CreateWorldScreen *) super;
        const bool is_creative = self->data.game_mode->text == CREATIVE_STR;
        if (button == self->data.game_mode) {
            // Toggle Game Mode
            self->data.game_mode->text = is_creative ? SURVIVAL_STR : CREATIVE_STR;
        } else if (button == self->data.back) {
            // Back
            super->handleBackEvent(false);
        } else if (button == self->data.create) {
            // Create
            create_world(super->minecraft, self->data.name->getText(), is_creative, self->data.seed->getText());
        }
    };
}
static Screen *create_create_world_screen() {
    // Construct
    CreateWorldScreen *screen = new CreateWorldScreen;
    ALLOC_CHECK(screen);
    screen->super()->constructor();

    // Set VTable
    screen->super()->vtable = get_create_world_screen_vtable();

    // Return
    return (Screen *) screen;
}

// Unique Level Name (https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/client/gui/screens/CreateWorldScreen.cpp#L65-L83)
static std::string getUniqueLevelName(LevelStorageSource *source, const std::string &in) {
    std::set<std::string> maps;
    std::vector<LevelSummary> vls;
    source->getLevelList(vls);
    for (int i = 0; i < int(vls.size()); i++) {
        const LevelSummary &ls = vls[i];
        maps.insert(ls.folder);
    }
    std::string out = in;
    while (maps.contains(out)) {
        out += "-";
    }
    return out;
}

// Create World
int get_seed_from_string(std::string str) {
    int seed;
    str = Util::stringTrim(str);
    if (!str.empty()) {
        int num;
        if (sscanf(str.c_str(), "%d", &num) > 0) {
            seed = num;
        } else {
            seed = Util::hashCode(str);
        }
    } else {
        seed = Common::getEpochTimeS();
    }
    return seed;
}
static void create_world(Minecraft *minecraft, std::string name, const bool is_creative, std::string seed_str) {
    // Get Seed
    const int seed = get_seed_from_string(std::move(seed_str));

    // Get Folder Name
    name = Util::stringTrim(name);
    std::string folder = "";
    for (const char c : name) {
        if (
            c >= ' ' && c <= '~' &&
            c != '/' &&
            c != '\\' &&
            c != '`' &&
            c != '?' &&
            c != '*' &&
            c != '<' &&
            c != '>' &&
            c != '|' &&
            c != '"' &&
            c != ':'
        ) {
            folder += c;
        }
    }
    if (folder.empty()) {
        folder = "World";
    }
    folder = getUniqueLevelName(minecraft->getLevelSource(), folder);

    // Settings
    LevelSettings settings;
    settings.game_type = is_creative;
    settings.seed = seed;

    // Create World
    minecraft->selectLevel(folder, name, settings);

    // Multiplayer
    minecraft->hostMultiplayer(19132);

    // Open ProgressScreen
    ProgressScreen *screen = ProgressScreen::allocate();
    ALLOC_CHECK(screen);
    screen = screen->constructor();
    minecraft->setScreen((Screen *) screen);
}

// Redirect Create World Button
#define create_SelectWorldScreen_tick_injection(prefix) \
    static void prefix##SelectWorldScreen_tick_injection(prefix##SelectWorldScreen_tick_t original, prefix##SelectWorldScreen *screen) { \
        if (screen->should_create_world) { \
            /* Open Screen */ \
            screen->minecraft->setScreen(create_create_world_screen()); \
            /* Finish */ \
            screen->should_create_world = false; \
        } else { \
            /* Call Original Method */ \
            original(screen); \
        } \
    }
create_SelectWorldScreen_tick_injection()
create_SelectWorldScreen_tick_injection(Touch_)

// Init
void _init_game_mode_ui() {
    // Hijack Create World Button
    overwrite_calls(SelectWorldScreen_tick, SelectWorldScreen_tick_injection);
    overwrite_calls(Touch_SelectWorldScreen_tick, Touch_SelectWorldScreen_tick_injection);
}
