///////////////////////////////////////////////////////////////////////////////
//
// wxFormBuilder - A Visual Dialog Editor for wxWidgets.
// Copyright (C) 2005 José Antonio Hurtado
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Written by
//   José Antonio Hurtado - joseantonio.hurtado@gmail.com
//   Juan Antonio Ortega  - jortegalalmolda@gmail.com
// Modified by
//   Michal Bliznak
//
///////////////////////////////////////////////////////////////////////////////

#include "xrcpanel.h"

#include <wx/fdrepdlg.h>

#include "codegen/codewriter.h"
#include "codegen/xrccg.h"
#include "model/objectbase.h"
#include "rad/appdata.h"
#include "rad/codeeditor/codeeditor.h"
#include "rad/wxfbevent.h"
#include "utils/typeconv.h"
#include "utils/wxfbexception.h"


BEGIN_EVENT_TABLE(XrcPanel, wxPanel)
EVT_FB_CODE_GENERATION(XrcPanel::OnCodeGeneration)
EVT_FB_PROJECT_REFRESH(XrcPanel::OnProjectRefresh)
EVT_FB_PROPERTY_MODIFIED(XrcPanel::OnPropertyModified)
EVT_FB_OBJECT_CREATED(XrcPanel::OnObjectChange)
EVT_FB_OBJECT_REMOVED(XrcPanel::OnObjectChange)
EVT_FB_OBJECT_SELECTED(XrcPanel::OnObjectChange)

EVT_FIND(wxID_ANY, XrcPanel::OnFind)
EVT_FIND_NEXT(wxID_ANY, XrcPanel::OnFind)
END_EVENT_TABLE()


XrcPanel::XrcPanel(wxWindow* parent, int id) : wxPanel(parent, id)
{
    auto* topSizer = new wxBoxSizer(wxVERTICAL);

    m_xrcPanel = new CodeEditor(this, wxID_ANY);
    InitStyledTextCtrl(m_xrcPanel->GetTextCtrl());

    topSizer->Add(m_xrcPanel, 1, wxEXPAND, 0);

    SetSizer(topSizer);

    m_cw = std::make_shared<TCCodeWriter>(m_xrcPanel->GetTextCtrl());

    AppData()->AddHandler(this->GetEventHandler());
}

XrcPanel::~XrcPanel()
{
    AppData()->RemoveHandler(this->GetEventHandler());
}

void XrcPanel::InitStyledTextCtrl(wxStyledTextCtrl* stc)
{
    stc->SetLexer(wxSTC_LEX_XML);
#ifdef __WXGTK__
    // Debe haber un bug en wxGTK ya que la familia wxMODERN no es de ancho fijo.
    wxFont font(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
    font.SetFaceName(wxT("Monospace"));
#else
    wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
#endif
    stc->StyleSetForeground(wxSTC_STYLE_DEFAULT, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    stc->StyleSetBackground(wxSTC_STYLE_DEFAULT, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    stc->StyleSetFont(wxSTC_STYLE_DEFAULT, font);
    stc->StyleClearAll();
    if (!AppData()->IsDarkMode()) {
        stc->StyleSetForeground(wxSTC_H_DOUBLESTRING, *wxRED);
        stc->StyleSetForeground(wxSTC_H_TAG, wxColour(0, 0, 128));
        stc->StyleSetForeground(wxSTC_H_ATTRIBUTE, wxColour(128, 0, 128));
    } else {
        stc->StyleSetForeground(wxSTC_H_DOUBLESTRING, wxColour(23, 198, 163));
        stc->StyleSetForeground(wxSTC_H_TAG, wxColour(18, 144, 195));
        stc->StyleSetForeground(wxSTC_H_ATTRIBUTE, wxColour(221, 40, 103));
    }
    stc->SetUseTabs(false);
    stc->SetTabWidth(4);
    stc->SetTabIndents(true);
    stc->SetBackSpaceUnIndents(true);
    stc->SetIndent(4);
    stc->SetSelBackground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
    stc->SetSelForeground(true, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));

    stc->SetCaretForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    stc->SetCaretWidth(2);
    stc->SetReadOnly(true);
}

void XrcPanel::OnFind(wxFindDialogEvent& event)
{
    m_xrcPanel->GetEventHandler()->ProcessEvent(event);
}

void XrcPanel::OnPropertyModified(wxFBPropertyEvent& event)
{
    // Generate code to the panel only
    event.SetId(1);
    OnCodeGeneration(event);
}

void XrcPanel::OnProjectRefresh(wxFBEvent& event)
{
    // Generate code to the panel only
    event.SetId(1);
    OnCodeGeneration(event);
}

void XrcPanel::OnObjectChange(wxFBObjectEvent& event)
{
    // Generate code to the panel only
    event.SetId(1);
    OnCodeGeneration(event);
}

void XrcPanel::OnCodeGeneration(wxFBEvent& event)
{
    PObjectBase project;

    // Using the previously unused Id field in the event to carry a boolean
    bool panelOnly = (event.GetId() != 0);

    // Generate code in the panel if the panel is active
    bool doPanel = IsShown();

    // Only generate to panel + panel is not shown = do nothing
    if (panelOnly && !doPanel) {
        return;
    }

    // For code preview generate only code relevant to selected form,
    //  otherwise generate full project code.
    if (panelOnly) {
        project = AppData()->GetSelectedForm();
    }

    if (!panelOnly || !project) {
        project = AppData()->GetProjectData();
    }
    // PObjectBase project = AppData()->GetProjectData();

    if (!project)
        return;

    // Generate code in the panel if the panel is active
    if (IsShown()) {
        Freeze();
        wxStyledTextCtrl* editor = m_xrcPanel->GetTextCtrl();
        editor->SetReadOnly(false);
        int line = editor->GetFirstVisibleLine() + editor->LinesOnScreen() - 1;
        int xOffset = editor->GetXOffset();

        XrcCodeGenerator codegen;
        codegen.SetWriter(m_cw);
        codegen.GenerateCode(project);
        editor->SetReadOnly(true);
        editor->GotoLine(line);
        editor->SetXOffset(xOffset);
        editor->SetAnchor(0);
        editor->SetCurrentPos(0);
        Thaw();
    }

    if (panelOnly) {
        return;
    }

    PProperty pCodeGen = project->GetProperty(wxT("code_generation"));
    if (pCodeGen) {
        if (!TypeConv::FlagSet(wxT("XRC"), pCodeGen->GetValue())) {
            return;
        }
    }

    // And now in the file.
    {
        XrcCodeGenerator codegen;

        wxString file, pathEntry;

        wxString path;

        try {
            // Get the output path
            path = AppData()->GetOutputPath();

            PProperty pfile = project->GetProperty(wxT("file"));

            if (pfile)
                file = pfile->GetValue();

            if (file.empty()) {
                file = wxT("noname");
            }

            wxString filePath;
            filePath << path << file << wxT(".xrc");

            auto cw= std::make_shared<FileCodeWriter>(filePath);

            codegen.SetWriter(cw);
            codegen.GenerateCode(project);
            wxLogStatus(wxT("Code generated on \'%s\'."), path);
        } catch (wxFBException& ex) {
            wxLogError(ex.what());
        }
    }
}
