#include <Widget/TabList/TabList.hpp>
#include <WorldAvatar/WorldAvatar.hpp>
#include <filesystem>
#include <string>
#include <Display/WidgetDisplay/WorldCreation/WorldCreation.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/Center/Center.hpp>
#include <Widget/Layout/Row/Row.hpp>
#include <Widget/Button/Button.hpp>
#include <Log/Log.hpp>
#include <Display/WidgetDisplay/WorldDisplay/WorldDisplay.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Display/WidgetDisplay/MainMenuDisplay/MainMenuDisplay.hpp>
#include <Widget/TextField/TextField.hpp>
#include <Utilities/Utilities.hpp>
#include <Widget/Builder/Builder.hpp>
#include <World/LocalWorld/SinglePlayerWorld/SinglePlayerWorld.hpp>
#include <Widget/Layout/Column/HorizontalSeparator/HorizontalSeparator.hpp>
#include <Widget/Layout/CompoundWidget/Form/Form.hpp>
#include <WorldTypeRegistry/WorldTypeRegistry.hpp>
#include <Widget/Layout/CompoundWidget/FormField/FormField.hpp>
#include <WorldIndex/WorldIndex.hpp>
#include <Widget/Switch/Switch.hpp>
#include <Widget/Layout/CompoundWidget/FormSection/FormSection.hpp>
#include <Widget/Layout/CompoundWidget/WorldAvatarPicker/WorldAvatarPicker.hpp>
#include <Display/WidgetDisplay/MessageDisplay/MessageDisplay.hpp>
#include <ChunkWindow/ChunkWindow.hpp>
#include <Widget/EnumSelector/EnumSelector.hpp>
#include <Widget/Button/BackButton/BackButton.hpp>

bool WorldCreation::isAcceptable()
{
    if (!getWorldNameError(worldName).empty())
    {
        return false;
    }

    if (worldTypeCreationForm == nullptr)
    {
        return false;
    }

    if (!worldTypeCreationForm->isAcceptable())
    {
        return false;
    }

    return true;
}

WorldCreation::WorldCreation() :
    worldName(""),
    worldTypeID("main:survival"),
    worldTypeCreationForm(nullptr),
    enableCheats(false)
{
    avatar = pickRandom(worldAvatarList);

    auto onCreateWorld = [this]()
    {
        if (!isAcceptable())
        {
            return;
        }
    
        std::string worldPath = formatString("worlds/$+.voxelverse", worldName);

        try
        {
            std::filesystem::create_directories("worlds");
        }
        catch (const std::exception& e)
        {
            Display::setCurrent<MessageDisplay>(Material::alert, "Error", "Failed to create the worlds folder!");
            return;
        }

        // Create the index.
        {
            WorldIndex index = WorldIndex::create(enableCheats, avatar);
            std::shared_ptr<WorldTypeSettings> settings = worldTypeCreationForm->makeSettings();
            index.setSettings(worldTypeID, *settings);

            AtomicDataStore store;
            store.create(worldPath);
            const std::string &storeError = store.getError();

            if (!storeError.empty())
            {
                Display::setCurrent<MessageDisplay>(Material::alert, "Error", "Failed to open the world file after creation!");
                return;
            }

            AtomicDataStore::Plan plan;
            serializeInto(plan["windex.dat"], index);

            store.update(plan, 0);
        }

        // Now try to play the world.
        std::shared_ptr<SinglePlayerWorld> world = std::make_shared<SinglePlayerWorld>(worldPath);
        Display::setCurrent<WorldDisplay>(world, world->getThePlayer());
    };

    static std::vector<EnumSelector<std::string>::Option> worldTypeOptions;
    if (worldTypeOptions.empty())
    {
        for (const auto &[id, worldType] : worldTypeRegistry.worldTypes)
        {
            worldTypeOptions.push_back({id, worldType->getName()});
        }
    }

    setWidget(
        new Center(
            new Form(
                "Create world",

                new TabList(
                    new TabList::Page(
                        Material::worldBasics,
                        "Basic",
                        new Column(
                            new Panel(glm::ivec2(150, 0)),

                            new FormField(
                                "World name:",
                                worldName,
                                (Widget::focused = new TextField(worldName, onCreateWorld)),
                                [this]() { return getWorldNameError(worldName); }
                            ),

                            new FormField(
                                "Cheats:",
                                enableCheats,
                                new Row(new Switch(enableCheats))
                            ),

                            new FormField(
                                "World avatar:",
                                avatar,
                                new WorldAvatarPicker(avatar)
                            )
                        )
                    ),

                    new TabList::Page(
                        Material::worldAdvanced,
                        "Advanced",
                        new Column(
                            new Panel(glm::ivec2(150, 0)),

                            new FormField(
                                "World type:",
                                worldTypeID,
                                new EnumSelector(worldTypeID, worldTypeOptions)
                            ),

                            Layout::WithProportion<1>(),
                            new Builder<std::string>(
                                worldTypeID,
                                [this, onCreateWorld](std::string worldTypeID)
                                {
                                    worldTypeCreationForm = worldTypeRegistry.getByID(worldTypeID)->makeCreationForm(onCreateWorld);
                                    return worldTypeCreationForm;
                                }
                            )
                        )
                    )
                ),

                Layout::WithProportion<1>(),
                (new Button("Create", onCreateWorld))
                    ->withIcon(Material::create)
                    ->enabledIf([this]() { return isAcceptable(); }),

                Layout::WithProportion<1>(),
                new BackButton("Cancel")
            )
        )
    );
}