#include "LayoutShapeDialog.h"
#include "canvas/CanvasState.h"
#include <wx/statline.h>
#include <wx/spinctrl.h>
#include <sstream>
#include <algorithm>
#include <cmath>

static const char* kShapeNames[] = {
    "Rectangle",
    "L-Shape",
    "U-Shape",
    "T-Shape",
    "Custom polygon (vertices)",
    "Free-form (draw on canvas)",
    "Clear (no baseboard)",
};

static double ToMm(double val, int u)
{
    if (u == 1) return val * 10.0;
    if (u == 2) return val * 25.4;
    return val;
}
static double FromMm(double mm, int u)
{
    if (u == 1) return mm / 10.0;
    if (u == 2) return mm / 25.4;
    return mm;
}
static int DefaultVal(int mm, int u)
{
    return (int)std::round(FromMm(mm, u));
}
static const char* UnitLabel(int u) {
    if (u == 1) return "cm";
    if (u == 2) return "in";
    return "mm";
}

LayoutShapeDialog::LayoutShapeDialog(wxWindow* parent,
                                     const std::vector<MC::Vec2>& current,
                                     int units,
                                     MC::CanvasState* state)
    : wxDialog(parent, wxID_ANY, "Layout Baseboard",
               wxDefaultPosition, wxDefaultSize,
               wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    , m_poly(current)
    , m_units(units)
    , m_state(state)
{
    wxBoxSizer* top = new wxBoxSizer(wxVERTICAL);

    // Shape
    wxBoxSizer* sRow = new wxBoxSizer(wxHORIZONTAL);
    sRow->Add(new wxStaticText(this, wxID_ANY, "Shape:"),
              0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8);
    m_shapeChoice = new wxChoice(this, wxID_ANY);
    for (auto& n : kShapeNames) m_shapeChoice->Append(n);
    // Restore the last shape choice if we have memory of it; otherwise
    // default to rectangle.
    int initSel = SH_RECT;
    if (m_state && m_state->lastShape.shapeId >= 0 &&
        m_state->lastShape.shapeId <= SH_CLEAR)
        initSel = m_state->lastShape.shapeId;
    m_shapeChoice->SetSelection(initSel);
    sRow->Add(m_shapeChoice, 1, wxALIGN_CENTER_VERTICAL);
    top->Add(sRow, 0, wxEXPAND | wxALL, 10);

    top->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, 8);

    // Fields
    m_fieldsPanel = new wxPanel(this, wxID_ANY);
    m_fieldsSizer = new wxBoxSizer(wxVERTICAL);
    m_fieldsPanel->SetSizer(m_fieldsSizer);
    top->Add(m_fieldsPanel, 1, wxEXPAND | wxALL, 10);

    const char* ul = UnitLabel(m_units);
    wxString noteStr = wxString::Format("All dimensions in %s.", ul);
    top->Add(new wxStaticText(this, wxID_ANY, noteStr), 0, wxLEFT | wxBOTTOM, 10);

    top->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, 8);

    // Buttons
    wxBoxSizer* btns = new wxBoxSizer(wxHORIZONTAL);
    btns->AddStretchSpacer();
    wxButton* ok     = new wxButton(this, wxID_OK, "Apply");
    wxButton* cancel = new wxButton(this, wxID_CANCEL, "Cancel");
    btns->Add(ok, 0, wxRIGHT, 6);
    btns->Add(cancel, 0);
    top->Add(btns, 0, wxALL | wxEXPAND, 10);

    SetSizerAndFit(top);
    SetSize(wxSize(420, -1));
    Centre();

    ok->Bind(wxEVT_BUTTON, &LayoutShapeDialog::OnOK, this);
    m_shapeChoice->Bind(wxEVT_CHOICE, &LayoutShapeDialog::OnShapeChoice, this);

    wxCommandEvent dummy;
    OnShapeChoice(dummy);
}

static wxSpinCtrl* AddDimRow(wxWindow* parent, wxSizer* sizer,
                              const wxString& label, int val,
                              const char* unitStr)
{
    wxBoxSizer* row = new wxBoxSizer(wxHORIZONTAL);
    wxStaticText* lbl = new wxStaticText(parent, wxID_ANY, label);
    lbl->SetMinSize(wxSize(160, -1));
    row->Add(lbl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6);
    wxSpinCtrl* sp = new wxSpinCtrl(parent, wxID_ANY, "",
        wxDefaultPosition, wxSize(90,-1), wxSP_ARROW_KEYS, 1, 99000, val);
    row->Add(sp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
    row->Add(new wxStaticText(parent, wxID_ANY, unitStr),
             0, wxALIGN_CENTER_VERTICAL);
    sizer->Add(row, 0, wxBOTTOM, 6);
    return sp;
}

void LayoutShapeDialog::OnShapeChoice(wxCommandEvent&)
{
    m_fieldsSizer->Clear(true);
    m_spW = m_spH = m_spW2 = m_spH2 = nullptr;
    m_customText = nullptr;

    int sh = m_shapeChoice->GetSelection();
    int u  = m_units;
    const char* ul = UnitLabel(u);

    // If the last-saved shape matches the currently selected shape, seed
    // the dimension defaults from it; otherwise fall back to the built-in
    // defaults.
    bool useLast = (m_state && m_state->lastShape.shapeId == sh);
    auto Dseed = [&](double fallbackMm, double lastMm) -> int {
        double mm = useLast ? lastMm : fallbackMm;
        return (int)std::round(FromMm(mm, u));
    };
    auto D = [&](int mm) { return DefaultVal(mm, u); };

    switch (sh) {
    case SH_RECT:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "Simple rectangular baseboard:"), 0, wxBOTTOM, 8);
        m_spW = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Width (left to right):",
                          Dseed(2400, m_state ? m_state->lastShape.w_mm : 2400), ul);
        m_spH = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Depth (top to bottom):",
                          Dseed(1200, m_state ? m_state->lastShape.h_mm : 1200), ul);
        break;

    case SH_L:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "L-Shape: full rectangle minus bottom-right corner.\n"
            "W1/H1 = full size,  W2/H2 = cutout (bottom-right)."),
            0, wxBOTTOM, 8);
        m_spW  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Full width  W1:",
                           Dseed(2400, m_state ? m_state->lastShape.w_mm : 2400), ul);
        m_spH  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Full depth  H1:",
                           Dseed(2400, m_state ? m_state->lastShape.h_mm : 2400), ul);
        m_spW2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Cutout width  W2:",
                           Dseed(1200, m_state ? m_state->lastShape.w2_mm : 1200), ul);
        m_spH2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Cutout depth  H2:",
                           Dseed(1200, m_state ? m_state->lastShape.h2_mm : 1200), ul);
        break;

    case SH_U:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "U-Shape: rectangle with notch cut from bottom centre.\n"
            "W/H = outer size,  W2/H2 = notch (centred on bottom)."),
            0, wxBOTTOM, 8);
        m_spW  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Outer width  W:",
                           Dseed(3600, m_state ? m_state->lastShape.w_mm : 3600), ul);
        m_spH  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Outer depth  H:",
                           Dseed(2400, m_state ? m_state->lastShape.h_mm : 2400), ul);
        m_spW2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Notch width  W2:",
                           Dseed(1200, m_state ? m_state->lastShape.w2_mm : 1200), ul);
        m_spH2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Notch depth  H2:",
                           Dseed(1200, m_state ? m_state->lastShape.h2_mm : 1200), ul);
        break;

    case SH_T:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "T-Shape: wide top bar plus narrower stem below.\n"
            "W/H1 = top bar,  W2/H2 = stem (centred)."),
            0, wxBOTTOM, 8);
        m_spW  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Top width  W:",
                           Dseed(3600, m_state ? m_state->lastShape.w_mm : 3600), ul);
        m_spH  = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Top depth  H1:",
                           Dseed(1200, m_state ? m_state->lastShape.h_mm : 1200), ul);
        m_spW2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Stem width  W2:",
                           Dseed(1200, m_state ? m_state->lastShape.w2_mm : 1200), ul);
        m_spH2 = AddDimRow(m_fieldsPanel, m_fieldsSizer, "Stem depth  H2:",
                           Dseed(1200, m_state ? m_state->lastShape.h2_mm : 1200), ul);
        break;

    case SH_CUSTOM: {
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "Enter polygon vertices as  x,y  pairs, one per line.\n"
            "Values are in the unit selected above.\n"
            "Example (mm):  0,0  /  2400,0  /  2400,1200  /  0,1200"),
            0, wxBOTTOM, 8);
        wxString defText;
        if (!m_poly.empty()) {
            for (auto& v : m_poly)
                defText += wxString::Format("%.2f,%.2f\n",
                    FromMm(v.x, m_units), FromMm(v.y, m_units));
        } else {
            defText = wxString::Format("0,0\n%.0f,0\n%.0f,%.0f\n0,%.0f\n",
                FromMm(2400,m_units), FromMm(2400,m_units),
                FromMm(1200,m_units), FromMm(1200,m_units));
        }
        m_customText = new wxTextCtrl(m_fieldsPanel, wxID_ANY, defText,
            wxDefaultPosition, wxSize(300, 120),
            wxTE_MULTILINE | wxTE_DONTWRAP);
        m_fieldsSizer->Add(m_customText, 0, wxEXPAND | wxBOTTOM, 4);
        break;
    }
    case SH_FREEFORM:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "Click OK and then click vertices on the canvas to outline the "
            "baseboard.\nClose the shape by clicking near the first vertex "
            "or pressing Enter.\nBackspace removes the last vertex; Escape "
            "cancels."),
            0, wxBOTTOM, 8);
        break;
    case SH_CLEAR:
        m_fieldsSizer->Add(new wxStaticText(m_fieldsPanel, wxID_ANY,
            "Remove the baseboard outline from the canvas."),
            0, wxBOTTOM, 8);
        break;
    }

    m_fieldsPanel->Layout();
    Fit();
}

std::vector<MC::Vec2> LayoutShapeDialog::BuildFromFields() const
{
    std::vector<MC::Vec2> poly;
    int sh = m_shapeChoice->GetSelection();
    int u  = m_units;
    auto MM = [&](int v) { return ToMm(v, u); };

    switch (sh) {
    case SH_RECT: {
        double W = MM(m_spW->GetValue()), H = MM(m_spH->GetValue());
        poly = {{0,0},{W,0},{W,H},{0,H}};
        break;
    }
    case SH_L: {
        double W=MM(m_spW->GetValue()),  H=MM(m_spH->GetValue());
        double w=MM(m_spW2->GetValue()), h=MM(m_spH2->GetValue());
        w=std::min(w,W-1); h=std::min(h,H-1);
        poly={{0,0},{W,0},{W,H-h},{W-w,H-h},{W-w,H},{0,H}};
        break;
    }
    case SH_U: {
        double W=MM(m_spW->GetValue()),  H=MM(m_spH->GetValue());
        double w=MM(m_spW2->GetValue()), h=MM(m_spH2->GetValue());
        w=std::min(w,W-2); h=std::min(h,H-1);
        double x0=(W-w)/2, x1=(W+w)/2;
        poly={{0,0},{W,0},{W,H},{x1,H},{x1,H-h},{x0,H-h},{x0,H},{0,H}};
        break;
    }
    case SH_T: {
        double W=MM(m_spW->GetValue()),  H=MM(m_spH->GetValue());
        double w=MM(m_spW2->GetValue()), h=MM(m_spH2->GetValue());
        w=std::min(w,W); h=std::max(h,1.0);
        double x0=(W-w)/2, x1=(W+w)/2;
        poly={{0,0},{W,0},{W,H},{x1,H},{x1,H+h},{x0,H+h},{x0,H},{0,H}};
        break;
    }
    case SH_CUSTOM: {
        if (!m_customText) break;
        std::istringstream ss(m_customText->GetValue().ToStdString());
        std::string line;
        while (std::getline(ss, line)) {
            line.erase(std::remove(line.begin(),line.end(),' '),line.end());
            if (line.empty()) continue;
            auto comma = line.find(',');
            if (comma == std::string::npos) continue;
            try {
                double x = ToMm(std::stod(line.substr(0, comma)), u);
                double y = ToMm(std::stod(line.substr(comma+1)),  u);
                poly.push_back({x, y});
            } catch (...) {}
        }
        break;
    }
    case SH_CLEAR: break;
    }
    return poly;
}

void LayoutShapeDialog::OnOK(wxCommandEvent&)
{
    int sh = m_shapeChoice->GetSelection();

    // Free-form: don't build geometry here; signal the caller to enter
    // baseboard-draw mode after the dialog dismisses. m_poly stays at the
    // current baseboard (so if the user later cancels the draw the existing
    // shape is preserved by MainFrame).
    if (sh == SH_FREEFORM) {
        m_drawAfterClose = true;
        if (m_state) m_state->lastShape.shapeId = sh;
        EndModal(wxID_OK);
        return;
    }

    m_poly = BuildFromFields();
    if (sh != SH_CUSTOM && sh != SH_CLEAR && m_poly.size() < 3) {
        wxMessageBox("Please enter valid dimensions.", "Layout Shape",
                     wxOK | wxICON_WARNING, this);
        return;
    }

    // Remember the chosen shape and dimensions so reopening the dialog
    // pre-fills with what the user just used.
    if (m_state) {
        m_state->lastShape.shapeId = sh;
        if (m_spW)  m_state->lastShape.w_mm  = ToMm(m_spW ->GetValue(), m_units);
        if (m_spH)  m_state->lastShape.h_mm  = ToMm(m_spH ->GetValue(), m_units);
        if (m_spW2) m_state->lastShape.w2_mm = ToMm(m_spW2->GetValue(), m_units);
        if (m_spH2) m_state->lastShape.h2_mm = ToMm(m_spH2->GetValue(), m_units);
    }

    EndModal(wxID_OK);
}
