#include "view3d/View3DWeb.h"
#include "io/LayoutJson.h"
#include <wx/filename.h>
#include <wx/stdpaths.h>

wxBEGIN_EVENT_TABLE(View3DWebFrame, wxFrame)
    EVT_CLOSE(View3DWebFrame::OnClose)
wxEND_EVENT_TABLE()

View3DWebFrame::View3DWebFrame(wxWindow* parent, const MC::CanvasState& state)
    : wxFrame(parent, wxID_ANY, "Track Designer - 3D View",
              wxDefaultPosition, wxSize(1024, 768),
              wxDEFAULT_FRAME_STYLE)
    , m_state(&state)
{
    if (!wxWebView::IsBackendAvailable(wxWebViewBackendDefault)) {
        wxMessageBox("WebView not available.\nPlease install Microsoft Edge WebView2.",
                     "3D View Error", wxOK | wxICON_ERROR, this);
        Close();
        return;
    }

    m_webView = wxWebView::New(this, wxID_ANY);
    if (!m_webView) {
        wxMessageBox("Failed to create WebView.", "3D View Error",
                     wxOK | wxICON_ERROR, this);
        Close();
        return;
    }

    // Listen for script messages — JS calls window.chrome.webview.postMessage("ready")
    // but wxWebView uses a different mechanism; we use a script message handler
    m_webView->Bind(wxEVT_WEBVIEW_LOADED,  &View3DWebFrame::OnWebViewLoaded,  this);
    m_webView->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED,
                    &View3DWebFrame::OnScriptMessage, this);
    m_webView->AddScriptMessageHandler("tdBridge");

    wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
    wxString htmlPath = exePath.GetPath() + wxFILE_SEP_PATH
                      + "view3d" + wxFILE_SEP_PATH + "viewer3d.html";
    if (!wxFileExists(htmlPath))
        htmlPath = exePath.GetPath() + wxFILE_SEP_PATH + "viewer3d.html";

    if (wxFileExists(htmlPath))
        m_webView->LoadURL("file:///" + htmlPath);
    else
        wxMessageBox("viewer3d.html not found:\n" + htmlPath,
                     "3D View Error", wxOK | wxICON_ERROR, this);

    m_pendingJson = wxString::FromUTF8(LayoutJson::Export(state));

    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(m_webView, 1, wxEXPAND);
    SetSizer(sizer);
    Show();
}

void View3DWebFrame::OnWebViewLoaded(wxWebViewEvent&) {
    // Page DOM loaded but JS may not be ready yet.
    // We inject a tiny poll that calls tdBridge.postMessage("ready") once
    // loadLayout is defined.
    m_webView->RunScript(
        "var _td_poll = setInterval(function(){"
        "  if(typeof loadLayout==='function'){"
        "    clearInterval(_td_poll);"
        "    window.tdBridge.postMessage('ready');"
        "  }"
        "}, 50);");
}

void View3DWebFrame::OnScriptMessage(wxWebViewEvent& e) {
    if (e.GetString() == "ready") {
        m_pageReady = true;
        if (!m_pendingJson.IsEmpty())
            InjectLayout();
    }
}

void View3DWebFrame::RefreshLayout(const MC::CanvasState& state) {
    m_state = &state;
    m_pendingJson = wxString::FromUTF8(LayoutJson::Export(state));
    if (m_pageReady)
        InjectLayout();
}

void View3DWebFrame::InjectLayout() {
    if (!m_webView || m_pendingJson.IsEmpty()) return;
    wxString json = m_pendingJson;
    json.Replace("\\", "\\\\");
    json.Replace("`",  "\\`");
    json.Replace("$",  "\\$");
    m_webView->RunScript(wxString::Format("loadLayout(`%s`);", json));
    m_pendingJson.Clear();
}

void View3DWebFrame::OnClose(wxCloseEvent& e) {
    Hide();
    e.Veto();
}
