#include <random>
#include <sstream>

#include <WorldType/SurvivalWorldType/SurvivalWorldType.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/CompoundWidget/WorldSeedField/WorldSeedField.hpp>
#include <Widget/Layout/CompoundWidget/FormField/FormField.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <WorldGenerator/SurvivalWorldGenerator/SurvivalWorldGenerator.hpp>

void SurvivalWorldType::Settings::serialize(std::vector<uint8_t> &output)
{
    serializeInto(output, seed);
}

std::unique_ptr<WorldGenerator> SurvivalWorldType::Settings::makeWorldGenerator(LocalWorld &world)
{
    Log::info(formatString("Initializing survival world generator with seed: $+", seed));
    return std::make_unique<SurvivalWorldGenerator>(world, seed);
}

std::shared_ptr<SurvivalWorldType::Settings> SurvivalWorldType::Settings::deserialize(const uint8_t *data, size_t size)
{
    std::shared_ptr<Settings> settings = std::make_shared<Settings>();
    ::deserialize(data, size, settings->seed);
    return settings;
}

SurvivalWorldType::CreationForm::CreationForm(std::function<void()> onAccept) : WorldTypeCreationForm(
    new Column(
        new FormField(
            "World seed:",
            seedString,
            new WorldSeedField(seedString, onAccept),
            [this]()
            {
                try
                {
                    std::stoull(seedString);
                }
                catch (const std::exception& e)
                {
                    return "The seed must be a number.";
                }

                return "";
            }
        )
    )
)
{
    std::random_device rd;
    std::mt19937_64 gen(rd());
    std::uniform_int_distribution<uint64_t> dis;

    std::stringstream ss;
    ss << dis(gen);
    seedString = ss.str();
}

std::shared_ptr<WorldTypeSettings> SurvivalWorldType::CreationForm::makeSettings()
{
    uint64_t seed;
    try
    {
        seed = std::stoull(seedString);
    }
    catch (const std::exception& e)
    {
        return nullptr;
    }

    std::shared_ptr<Settings> settings = std::make_shared<Settings>();
    settings->seed = seed;
    return settings;
}

bool SurvivalWorldType::CreationForm::isAcceptable()
{
    try
    {
        std::stoull(seedString);
    }
    catch (const std::exception& e)
    {
        return false;
    }

    return true;
}

std::shared_ptr<WorldTypeSettings> SurvivalWorldType::createSettingsWithSeed(std::optional<uint64_t> seed)
{
    uint64_t actualSeed;
    if (seed.has_value())
    {
        actualSeed = *seed;
    }
    else
    {
        std::random_device rd;
        std::mt19937_64 gen(rd());
        std::uniform_int_distribution<uint64_t> dis;

        actualSeed = dis(gen);
    }

    std::shared_ptr<Settings> settings = std::make_shared<Settings>();
    settings->seed = actualSeed;
    return settings;
}

std::string_view SurvivalWorldType::getName()
{
    return "Survival";
}

WorldTypeCreationForm* SurvivalWorldType::makeCreationForm(std::function<void()> onAccept)
{
    return new CreationForm(onAccept);
}

std::shared_ptr<WorldTypeSettings> SurvivalWorldType::deserializeSettings(const uint8_t *data, size_t size)
{
    return Settings::deserialize(data, size);
}

std::shared_ptr<WorldTypeSettings> SurvivalWorldType::parseSettingsFromCmdArgs(const std::string *args, size_t numArgs)
{
    if (numArgs > 1)
    {
        Log::error("Syntax: mkworld <filename> main:survival [seed]");
        return nullptr;
    }

    std::optional<uint64_t> seed;
    if (numArgs >= 1)
    {
        try
        {
            seed = std::stoull(args[0]);
        }
        catch (const std::exception &e)
        {
            Log::error("Invalid seed.");
            return nullptr;
        }
    }

    return createSettingsWithSeed(seed);
}