#include "ShipLayoutBlueprintSerializer.h"

#include <sstream>
#include <stdexcept>

#include "toml.hpp"

#include "Rotation.h"

namespace
{

std::string rotationToString(Rotation r)
{
    switch (r)
    {
    case Rotation::North: return "north";
    case Rotation::East:  return "east";
    case Rotation::South: return "south";
    case Rotation::West:  return "west";
    }
    return "north";
}

Rotation parseRotation(const std::string& s)
{
    if (s == "east")  { return Rotation::East; }
    if (s == "south") { return Rotation::South; }
    if (s == "west")  { return Rotation::West; }
    return Rotation::North;
}

} // namespace

namespace ShipLayoutBlueprintSerializer
{

std::string serialize(const std::vector<ShipLayoutBlueprint>& blueprints)
{
    toml::array bpArr;
    for (const ShipLayoutBlueprint& bp : blueprints)
    {
        toml::array modArr;
        for (const PlacedModule& pm : bp.modules)
        {
            toml::table modTbl;
            modTbl.insert("type",     pm.moduleId);
            modTbl.insert("x",        static_cast<int64_t>(pm.position.x()));
            modTbl.insert("y",        static_cast<int64_t>(pm.position.y()));
            modTbl.insert("rotation", rotationToString(pm.rotation));
            modArr.push_back(std::move(modTbl));
        }

        toml::table bpTbl;
        bpTbl.insert("name",      bp.name.toStdString());
        bpTbl.insert("ship_type", bp.shipType);
        bpTbl.insert("modules",   std::move(modArr));
        bpArr.push_back(std::move(bpTbl));
    }

    toml::table root;
    root.insert("blueprint", std::move(bpArr));

    std::ostringstream oss;
    oss << root;
    return oss.str();
}

std::vector<ShipLayoutBlueprint> deserialize(const std::string& tomlContent)
{
    toml::table root;
    try
    {
        root = toml::parse(tomlContent);
    }
    catch (const toml::parse_error& e)
    {
        std::ostringstream msg;
        msg << "TOML parse error: " << e.description() << " at " << e.source().begin;
        throw std::runtime_error(msg.str());
    }

    std::vector<ShipLayoutBlueprint> result;

    const toml::array* bpArr = root["blueprint"].as_array();
    if (!bpArr) { return result; }

    for (std::size_t i = 0; i < bpArr->size(); ++i)
    {
        const toml::table* bpTbl = (*bpArr)[i].as_table();
        if (!bpTbl) { continue; }

        const std::optional<std::string> name     = (*bpTbl)["name"].value<std::string>();
        const std::optional<std::string> shipType = (*bpTbl)["ship_type"].value<std::string>();
        if (!name || name->empty() || !shipType || shipType->empty()) { continue; }

        ShipLayoutBlueprint bp;
        bp.name     = QString::fromStdString(*name);
        bp.shipType = *shipType;

        const toml::array* modArr = (*bpTbl)["modules"].as_array();
        if (modArr)
        {
            for (std::size_t j = 0; j < modArr->size(); ++j)
            {
                const toml::table* modTbl = (*modArr)[j].as_table();
                if (!modTbl) { continue; }

                const std::optional<std::string> type   = (*modTbl)["type"].value<std::string>();
                const std::optional<int64_t>     x      = (*modTbl)["x"].value<int64_t>();
                const std::optional<int64_t>     y      = (*modTbl)["y"].value<int64_t>();
                const std::optional<std::string> rotStr = (*modTbl)["rotation"].value<std::string>();
                if (!type || !x || !y || !rotStr) { continue; }

                PlacedModule pm;
                pm.moduleId  = *type;
                pm.position  = QPoint(static_cast<int>(*x), static_cast<int>(*y));
                pm.rotation  = parseRotation(*rotStr);
                bp.modules.push_back(std::move(pm));
            }
        }

        result.push_back(std::move(bp));
    }

    return result;
}

} // namespace ShipLayoutBlueprintSerializer
