#include "io/LayoutJson.h"
#include "io/CatalogLoader.h"
#include "model/TrackPiece.h"
#include <sstream>
#include <iomanip>
#include <cmath>

static std::string esc(const std::string& s) {
    std::string out;
    for (char c : s) {
        if (c == '"')  out += "\\\"";
        else if (c == '\\') out += "\\\\";
        else if (c == '\n') out += "\\n";
        else out += c;
    }
    return out;
}

static std::string d(double v, int prec = 3) {
    std::ostringstream ss;
    ss << std::fixed << std::setprecision(prec) << v;
    return ss.str();
}

static const char* pieceTypeName(MC::PieceType t) {
    switch (t) {
        case MC::PieceType::Straight:        return "straight";
        case MC::PieceType::Curve:           return "curve";
        case MC::PieceType::TurnoutLeft:     return "turnoutLeft";
        case MC::PieceType::TurnoutRight:    return "turnoutRight";
        case MC::PieceType::TurnoutY:        return "turnoutY";
        case MC::PieceType::TurnoutCurved:   return "turnoutCurved";
        case MC::PieceType::DoubleCrossover: return "doubleCrossover";
        case MC::PieceType::Crossing:        return "crossing";
        case MC::PieceType::Bumper:          return "bumper";
        case MC::PieceType::Uncoupler:       return "uncoupler";
        case MC::PieceType::Turntable:       return "turntable";
        default:                             return "straight";
    }
}

static const char* seriesName(MC::TrackSeries s) {
    switch (s) {
        case MC::TrackSeries::MarlinC:  return "MarlinC";
        case MC::TrackSeries::MarlinK:  return "MarlinK";
        case MC::TrackSeries::MarlinZ:  return "MarlinZ";
        case MC::TrackSeries::TrixC:    return "TrixC";
        default:                        return "MarlinC";
    }
}

// ── Track widths per series (mm) ─────────────────────────────────────────────
static double trackWidth(MC::TrackSeries s) {
    return (s == MC::TrackSeries::MarlinZ) ? 12.0 : 37.4;
}

// ── Rail gauge per series (mm, inside of rails) ──────────────────────────────
static double railGauge(MC::TrackSeries s) {
    return (s == MC::TrackSeries::MarlinZ) ? 6.5 : 16.5;
}

std::string LayoutJson::Export(const MC::CanvasState& state) {
    auto& catalog = MC::GetCatalog();
    std::ostringstream j;
    j << std::fixed << std::setprecision(4);

    j << "{\n";

    // ── Baseboard ─────────────────────────────────────────────────────────
    j << "  \"baseboard\": [";
    bool first = true;
    for (const auto& v : state.baseboard) {
        if (!first) j << ",";
        j << "[" << d(v.x) << "," << d(v.y) << "]";
        first = false;
    }
    j << "],\n";

    // ── Pieces ────────────────────────────────────────────────────────────
    j << "  \"pieces\": [\n";
    first = true;
    for (const auto& p : state.pieces) {
        const MC::TrackPiece* def = catalog.Find(p.catalogNumber);
        if (!def) continue;

        if (!first) j << ",\n";
        first = false;

        j << "    {\n";
        j << "      \"id\": "           << p.id << ",\n";
        j << "      \"cat\": \""        << esc(p.catalogNumber) << "\",\n";
        j << "      \"type\": \""       << pieceTypeName(def->type) << "\",\n";
        j << "      \"series\": \""     << seriesName(def->series) << "\",\n";
        j << "      \"x\": "            << d(p.transform.pos.x) << ",\n";
        j << "      \"y\": "            << d(p.transform.pos.y) << ",\n";
        j << "      \"heading\": "      << d(p.transform.heading) << ",\n";
        j << "      \"z_mm\": "         << d(p.z_mm) << ",\n";
        j << "      \"gradeDeg\": "     << d(p.gradeDeg) << ",\n";
        j << "      \"lengthMm\": "     << d(def->lengthMm) << ",\n";
        j << "      \"radiusMm\": "     << d(def->radiusMm) << ",\n";
        j << "      \"angleDeg\": "     << d(def->angleDeg) << ",\n";
        j << "      \"trackWidth\": "   << d(trackWidth(def->series)) << ",\n";
        j << "      \"railGauge\": "    << d(railGauge(def->series)) << ",\n";

        // Color
        if (p.hasColor)
            j << "      \"color\": \""
              << std::hex << std::setfill('0')
              << std::setw(2) << (int)p.colorR
              << std::setw(2) << (int)p.colorG
              << std::setw(2) << (int)p.colorB
              << std::dec << "\",\n";
        else
            j << "      \"color\": null,\n";

        // Connection points
        j << "      \"connections\": [";
        bool cf = true;
        for (const auto& cp : def->connections) {
            if (!cf) j << ",";
            j << "[" << d(cp.pos.x) << "," << d(cp.pos.y) << ","
              << d(cp.heading) << "]";
            cf = false;
        }
        j << "]\n";

        j << "    }";
    }
    j << "\n  ],\n";

    // ── Scenery ───────────────────────────────────────────────────────────
    j << "  \"scenery\": [\n";
    first = true;
    for (const auto& s : state.scenery) {
        if (!first) j << ",\n";
        first = false;
        j << "    {\"id\":" << s.id
          << ",\"item\":\"" << esc(s.itemId) << "\""
          << ",\"x\":" << d(s.transform.pos.x)
          << ",\"y\":" << d(s.transform.pos.y)
          << ",\"heading\":" << d(s.transform.heading)
          << ",\"scaleX\":" << d(s.scaleX)
          << ",\"scaleY\":" << d(s.scaleY) << "}";
    }
    j << "\n  ],\n";

    // ── Labels ────────────────────────────────────────────────────────────
    j << "  \"labels\": [\n";
    first = true;
    for (const auto& l : state.labels) {
        if (!first) j << ",\n";
        first = false;
        j << "    {\"id\":" << l.id
          << ",\"text\":\"" << esc(l.text) << "\""
          << ",\"x\":" << d(l.pos.x)
          << ",\"y\":" << d(l.pos.y) << "}";
    }
    j << "\n  ]\n";

    j << "}\n";
    return j.str();
}
