/////////////////////////////////////////////////////////////////////////////
// Name:        proppanel_buildsystem.cpp
// Purpose:     wxPackagePropertiesPanel
//              (stuff for handling the "buildsystem" page)
// Author:      Francesco Montorsi
// Modified by:
// Created:     28/06/2006 19:22:47
// RCS-ID:      $Id: proppanel_buildsystem.cpp,v 1.8 2007/01/28 14:35:29 frm Exp $
// Copyright:   (c) 2006 Julian Smart, Francesco Montorsi
// Licence:     wxWidgets license
/////////////////////////////////////////////////////////////////////////////


// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "guipkg/proppanel.h"
#include "guipkg/sourceeditctrl.h"
#include "guipkg/opteditdlg.h"
#include "guipkg/cmdeditdlg.h"
#include "guipkg/optlistctrl.h"
#include "guipkg/optpresetchooserdlg.h"
#include "guipkg/cmdpresetchooserdlg.h"

#include "guicmn/commandlistbox.h"

#include "wxp/package.h"
#include "wxp/wxp.h"
#include "wxp/packagewxp.h"

#include "wx/miscutils.h"



// ----------------------------------------------------------------------------
// Global helpers
// ----------------------------------------------------------------------------

// some globals wxPackageCommandOption helpers

wxPackageCommandOption &wxGetPackageCommandOptionFromListbox(wxListBox *lb, unsigned int idx)
{ return *((wxPackageCommandOption *)lb->GetClientData(idx)); }

void wxUpdatePackageCommandOptionInListbox(wxListBox *lb, unsigned int idx,
                                           const wxPackageCommandOption &opt)
{ wxGetPackageCommandOptionFromListbox(lb, idx) = opt; }



// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - build system stuff - I/O
// ----------------------------------------------------------------------------

void wxPackagePropertiesPanel::InitBuildSystemPage()
{
    m_pBuildSystemType->Append(wxArrayString(wxPBST_MAX, wxPackageBuildSystemTypeName));
    m_pBuildSystemStage->Append(wxArrayString(wxPBSS_MAX, wxPackageBuildSystemStageName));
    m_pBuildSystemType->Select(0);
    m_pBuildSystemStage->Select(0);

    // fake event to initialize the formats choice control
    wxCommandEvent fake;
    OnBuildSystemType(fake);        // this will update all filters

    UpdateOptions();
    UpdateCommands();
}

void wxPackagePropertiesPanel::GetBuildSystemPackageInfo(wxPackageInfo &ret) const
{
    // BUILD SYSTEM info
    ret.SetBuildSysHref(m_strBuildSystemHref);
    ret.SetBuildSystemType(GetSelBuildSystemType());
    ret.SetBuildSysOptions(m_pBuildSysOptions->GetOptions());

    // NB: we cannot use wxCheckListBox2Flags as m_pFormats contains only those
    //     formats supported by selected build system and not all wxPackageCompilerFormat
    ret.SetSupportedFormats(GetCompilerFormats());

    for (size_t i=0; i<wxPBSS_MAX; i++)
        ret.SetBuildSysCommands(wxIdx2PackageBuildSystemStage(i), m_commands[i]);
}

void wxPackagePropertiesPanel::DoSetBuildSystemPackageInfo(const wxPackageInfo &p)
{
    // BUILD SYSTEM info
    m_strBuildSystemHref = p.GetBuildSystemHref();

    if (p.GetBuildSystemType() != wxPBST_INVALID)
        m_pBuildSystemType->SetSelection(
                wxPackageBuildSystemType2Idx(p.GetBuildSystemType()));
    else
        m_pBuildSystemType->SetSelection(0);

    // false events to force m_formats & m_pFormats update
    wxCommandEvent ev;
    OnBuildSystemType(ev);

    // NB: we cannot use wxFlags2CheckListBox to update m_pFormats
    //     as it contains only the formats filtered for current build system type !
    wxCheckAll(m_pFormats, false);
    for (size_t i=0; i<m_pFormats->GetCount(); i++)
        if ((p.GetSupportedBuildSysFormats() & m_formats[i]) != 0)
            m_pFormats->Check(i);

    // update filters
    m_pBuildSysOptions->SetCompilerFormats(p.GetSupportedBuildSysFormats());
    m_pBuildSysCommands->SetCompilerFormats(p.GetSupportedBuildSysFormats());

    // update options
    m_pBuildSysOptions->SetOptions(p.GetBuildSysOptions());
    UpdateOptions();

    for (size_t i=0; i<wxPBSS_MAX; i++)
    {
        m_commands[i] = p.GetCmdForStage(wxIdx2PackageBuildSystemStage(i));
        m_targets[i] = m_commands[i].GetTargetsStr();
    }

    int idx = m_pBuildSystemStage->GetSelection();
    m_pTargets->SetValue(m_targets[idx]);

    // update commands
    m_pBuildSysCommands->SetCommands(m_commands[idx]);
    UpdateCommands();

    // reset modified flag
    m_bModified=false;

    // NOTE: this function won't call UpdateXML(); the SetPackageInfo()
    //       overloads will do that
}

bool wxPackagePropertiesPanel::IsBuildSystemPageOk() const
{
    bool isok = true;

    // check formats
    if (wxCheckListBox2Flags(m_pFormats) == 0)
    {
        wxLogWarning(wxT("Please check at least one compiler format as supported"));
        isok = false;
    }

    // check options
    if (m_pBuildSysOptions->GetOptions().GetCount() == 0)
    {
        wxLogWarning(wxT("No build options available for this package ?"));
        isok = false;
    }

    for (size_t i=0; i<wxPBST_MAX; i++)
    {
        wxString stagename = 
            wxPackageBuildSystemStage2String(wxIdx2PackageBuildSystemStage(i));

        if (m_commands[i].GetCount() == 0)
        {
            wxLogWarning(wxT("No commands for the '%s' build system stage ?"),
                         stagename.c_str());
            isok = false;
        }

        if (m_targets[i].IsEmpty())
        {
            wxLogWarning(wxT("No targets for the '%s' build system stage ?"),
                         stagename.c_str());
            isok = false;
        }
    }

    return isok;
}


// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - build system stuff 
// ----------------------------------------------------------------------------

bool wxPackagePropertiesPanel::GetBuildSysOption(wxPackageCommandOption *ret,
                                                 const wxPackageCommandOption &initial)
{
    wxPackageOptionEditDlg dlg(this);
    dlg.SetOption(initial);
    if (dlg.ShowModal() == wxID_OK)
    {
        wxPackageCommandOption opt(dlg.GetOption());
        wxString presetname;

        if (initial.GetName() != opt.GetName())
        {
            // check the new name
            if (IsPresetOption(opt.GetName(), &presetname))
            {
                // this name conflicts with the name of an option in one of the
                // presets that are in use !
                wxLogError(wxT("Cannot use the '%s' name.\nIt's the name of an option contained in the '%s' preset, which is in use."),
                           opt.GetName().c_str(), presetname.c_str());
                return false;
            }
            else if (IsUserOption(opt.GetName()))
            {
                wxLogError(wxT("Cannot use the '%s' name.\nIt's the name of another option."),
                           opt.GetName().c_str());
                return false;
            }
        }

        // no conflicts
        if (ret) *ret = opt;
        return true;
    }

    return false;
}

bool wxPackagePropertiesPanel::GetBuildSysCommand(wxPackageCommand *ret,
                                                  wxPackageBuildSystemStage stage,
                                                  const wxPackageCommand &initial)
{
    wxPackageCommandEditDlg dlg(this);
    dlg.SetCommand(initial, stage);
    if (dlg.ShowModal() == wxID_OK)
    {
        wxPackageCommand cmd(dlg.GetCommand());
        if (ret) *ret = cmd;
        return true;
    }

    return false;
}

void wxPackagePropertiesPanel::UpdateOptions()
{
    const wxPackageCommandOptionArray &arr = m_pBuildSysOptions->GetOptions();

    // update the "used presets" label:
    wxString str;
    wxPackageCommandOptionArrayHashMap::iterator it;
    for( it = wxPresetXMLDescriptor::s_hashmapOptions.begin();
         it != wxPresetXMLDescriptor::s_hashmapOptions.end(); ++it )
    {
        wxString name = it->first;
        wxPackageCommandOptionArray &value = it->second;

        if (arr.Contains(value))
            str += name + wxT(", ");
    }

    if (str.IsEmpty())
        str = wxT("none");
    else
        str = str.RemoveLast().RemoveLast();
    m_pOptionPresets->SetLabel(str);
}

void wxPackagePropertiesPanel::UpdateCommands()
{
    // update the "used presets" label:
    wxString str;
    wxStringHashMap h;
    wxPackageCommandSetHashMap::iterator it;
    for( it = wxPresetXMLDescriptor::s_hashmapCommands.begin();
         it != wxPresetXMLDescriptor::s_hashmapCommands.end(); ++it )
    {
        wxString name = it->first;
        wxPackageCommandSet &set = it->second;

        if (set.IsUsedBy(m_commands, h))
            str += name + wxT(", ");
    }

    if (str.IsEmpty())
        str = wxT("none");
    else
        str = str.RemoveLast().RemoveLast();
    m_pCommandPresets->SetLabel(str);
}

bool wxPackagePropertiesPanel::IsPresetOption(const wxString &optname,
                                              wxString *presetname) const
{
    wxArrayString presets = wxStringTokenize(m_pOptionPresets->GetLabel(), wxT(","));

    for (size_t i=0; i<presets.GetCount(); i++)
    {
        wxPackageCommandOptionArrayHashMap &h = wxPresetXMLDescriptor::s_hashmapOptions;

        // this check is important to avoid to call h[wxT("some-dummy-string")]
        // which would generate a new option array!
        if (h.count(presets[i]) > 0)
        {
            if (h[presets[i]].IndexByName(optname) != wxNOT_FOUND)
            {
                if (presetname) *presetname = presets[i];
                return true;
            }
        }
    }

    return false;
}

bool wxPackagePropertiesPanel::IsUserOption(const wxString &optname) const
{ 
    if (m_pBuildSysOptions->GetOptions().ContainsItemNamed(optname))
        return true;
    if (IsPresetOption(optname, NULL))
        return true;

    return false;
}

long wxPackagePropertiesPanel::GetCompilerFormats() const
{
    long fmt = 0;
    for (size_t i=0; i<m_pFormats->GetCount(); i++)
        if (m_pFormats->IsChecked(i))
            fmt |= m_formats[i];

    return fmt;
}


// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - event handlers
// ----------------------------------------------------------------------------

void wxPackagePropertiesPanel::OnBuildSystemType(wxCommandEvent &)
{
    wxPackageBuildSystemType t =
            wxIdx2PackageBuildSystemType(m_pBuildSystemType->GetSelection());
    wxASSERT(t != wxPBST_INVALID);

    // update the format wxCheckListBox control to show only the formats available
    // for the currently selected build system
    m_pFormats->Clear();
    for (size_t i=0; i<wxPCF_MAX; i++)
    {
        wxPackageCompilerFormat curr = wxIdx2PackageCompilerFormat(i);
        if (wxIsCompilerFormatSupportedBy(curr, t))
        {
            int last = m_pFormats->Append(wxPackageCompilerFormat2String(curr));

            // unfortunately we cannot use client data with a wxCheckListBox
            m_formats[last] = curr;

            // make all formats checked at the beginning
            m_pFormats->Check(last);
        }
    }

    // then, select the first of those formats
    m_pFormats->Select(0);

    // update filters
    m_pBuildSysOptions->SetBuildSystemType(t);
    m_pBuildSysCommands->SetBuildSystemType(t);

    // fake event
    wxCommandEvent fake;
    OnBuildSystemFormatCheck(fake);

    OnUserChange();
}

void wxPackagePropertiesPanel::OnBuildSystemFormatCheck(wxCommandEvent &)
{
    // get the wxPackageCompilerFormat flag
    long fmt = GetCompilerFormats();

    // update filters
    m_pBuildSysOptions->SetCompilerFormats(fmt);
    m_pBuildSysCommands->SetCompilerFormats(fmt);

    OnUserChange();
}

void wxPackagePropertiesPanel::OnBuildSystemStage(wxCommandEvent &)
{
    int idx = m_pBuildSystemStage->GetSelection();

    // save targets & commands for old stage
    TransferDataFromWindow();

    // update targets & commands for this stage
    m_pTargets->SetValue(m_targets[idx]);
    m_pBuildSysCommands->SetCommands(m_commands[idx]);

    m_nLastStage=idx;

    OnUserChange();
}

void wxPackagePropertiesPanel::OnBuildSystemButton(wxCommandEvent &ev)
{
    OnUserChange();

    switch (ev.GetId())
    {
    case ID_PROP_NEW_OPTION:
        {
            wxPackageCommandOption opt;

            // start with all conditions checked
            opt.GetCondition().SetAlwaysTrue();

            if (GetBuildSysOption(&opt, opt))
            {
                m_pBuildSysOptions->GetOptions().Add(opt);
                m_pBuildSysOptions->TransferDataToWindow();
                UpdateOptions();
            }
        }
        break;

    case ID_PROP_NEW_COMMAND:
        {
            wxPackageCommand cmd;
            int idx = m_pBuildSystemStage->GetSelection();
            wxPackageBuildSystemStage stage = wxIdx2PackageBuildSystemStage(idx);

            // start with all conditions checked except for the stage
            cmd.GetCondition().SetAlwaysTrue();
            cmd.GetCondition().SetStages(stage);

            if (GetBuildSysCommand(&cmd, stage, cmd))
            {
                m_pBuildSysCommands->Append(cmd);
                UpdateCommands();
            }
        }
        break;

    case ID_PROP_DELETE_OPTION:
        {
            wxString optname(m_pBuildSysOptions->GetSelectedOptName()), presetname;
            if (optname.IsEmpty())
            {
                // the selected item is an option group, not an option!
                // ignore this command
                return;
            }
            else if (IsPresetOption(optname, &presetname))
            {
                int reply = wxMessageBox(
                    wxString::Format(
                        wxT("Are you sure that you want to delete an option which belongs to the '%s' preset?\n\nIf you do, this package won't use that preset anymore and all its other options\nwill be copied in the WXP file making it longer (and hard to maintain/review)..."),
                        presetname.c_str()),
                    wxT("Question"), wxYES_NO|wxICON_QUESTION, this);
                if (reply == wxID_YES)
                {
                    m_pBuildSysOptions->DeleteSelected();
                    UpdateOptions();
                }
            }
            else
            {
                m_pBuildSysOptions->DeleteSelected();
                UpdateOptions();
            }
        }
        break;

    case ID_PROP_DELETE_COMMAND:
        m_pBuildSysCommands->DeleteSelected();
        UpdateCommands();
        break;

    case ID_PROP_RESET_COMMANDS:
        {
            if (wxPresetXMLDescriptor::GetCommandPresetNamesFor(GetSelBuildSystemType()).GetCount() == 0)
            {
                wxLogMessage(wxT("No presets for the currently selected build system type"));
                return;
            }

            wxPackageCommandPresetChooserDlg dlg(this, wxID_ANY, GetSelBuildSystemType());
            if (dlg.ShowModal() == wxID_OK)
            {
                const wxPackageCommandSet &arr = dlg.GetSelectedPreset();

                wxStringHashMap vars;
                for (size_t i=0; i < arr.GetVars().GetCount(); i++)
                {
                    wxString curr = arr.GetVars().Item(i);

                    wxString msg;
                    msg.Printf(wxT("Please provide the value of the '%s' variable\n")
                               wxT("for the '%s' command preset."),
                               curr.c_str(), dlg.GetSelectedPresetName().c_str());
                    wxString txt = wxGetTextFromUser(msg, wxT("Input variable value"),
                                                     wxEmptyString, this);
                    if (txt.IsEmpty())
                        return;
                    vars[curr] = txt;
                }

                // set selected cmd preset
                for (size_t i=0; i<wxPBSS_MAX; i++)
                {
                    m_commands[i].Clear();

                    wxPackageBuildSystemStage stage = wxIdx2PackageBuildSystemStage(i);
                    for (size_t j=0; j<arr.GetCount(); j++)
                    {
                        if (arr[j].GetCondition().GetStages() & stage)
                        {
                            m_commands[i].Add(arr[j]);
                            m_commands[i].AddKeywords(vars);
                        }
                    }
                }

                // update the control, too
                int idx = m_pBuildSystemStage->GetSelection();
                m_pBuildSysCommands->SetCommands(m_commands[idx]);

                // update the "presets used" label
                UpdateCommands();
            }
        }
        break;

    case ID_PROP_ADD_OPTPRESET:
        {
            if (wxPresetXMLDescriptor::GetOptionPresetNamesFor(GetSelBuildSystemType()).GetCount() == 0)
            {
                wxLogMessage(wxT("No presets for the currently selected build system type"));
                return;
            }

            wxPackageOptionPresetChooserDlg dlg(this, wxID_ANY, GetSelBuildSystemType());
            if (dlg.ShowModal() == wxID_OK)
            {
                // add selected option preset
                wxPackageCommandOptionArray arr = m_pBuildSysOptions->GetOptions();
                WX_APPEND_ARRAY(arr, dlg.GetSelectedPreset());
                m_pBuildSysOptions->SetOptions(arr);

                // update the "presets used" label
                UpdateOptions();
            }
        }
        break;
    }
}

void wxPackagePropertiesPanel::OnBuildSystemListBoxDClick(wxCommandEvent &ev)
{
    OnUserChange();

    switch (ev.GetId())
    {
        case ID_PROP_BUILDSYS_COMMANDS:
        {
            int stageidx = m_pBuildSystemStage->GetSelection();
            int idx = m_pBuildSysCommands->GetSelection();
            if (idx == wxNOT_FOUND)
            {
                // this may happen on wxMSW 's listboxes when they have > 0 items
                // and the user clicks on the empty space
                return;
            }

            wxPackageCommand newentry;
            if (GetBuildSysCommand(&newentry, wxIdx2PackageBuildSystemStage(stageidx),
                                   m_pBuildSysCommands->GetCommand(idx)))
            {
                m_pBuildSysCommands->SetCommand(idx, newentry);
                UpdateCommands();
            }
        }
        break;
    }
}

void wxPackagePropertiesPanel::OnOptionDClick(wxCommandEvent &ev)
{
    wxString optname(ev.GetString()), presetname;
    if (IsPresetOption(optname, &presetname))
    {
        int reply = wxMessageBox(
            wxString::Format(
                wxT("Are you sure you want to edit an option which belongs to the '%s' preset?\nIf you do, this package won't use that preset anymore and all its options\nwill be copied in the WXP file making it longer (and hard to maintain/review)..."),
                presetname.c_str()),
            wxT("Question"), wxYES_NO|wxICON_QUESTION, this);
        if (reply == wxID_NO)
            return;
    }

    wxPackageCommandOptionArray &arr = m_pBuildSysOptions->GetOptions();
    wxPackageCommandOption newentry, initial = arr.ItemByName(optname);
    if (GetBuildSysOption(&newentry, initial))
    {
        arr.UpdateOption(newentry);
        m_pBuildSysOptions->TransferDataToWindow();
        UpdateOptions();
    }

    OnUserChange();
}

void wxPackagePropertiesPanel::OnBuildSystemPageUpdateUI(wxUpdateUIEvent &WXUNUSED(ev))
{
    FindWindowById(ID_PROP_DELETE_COMMAND)->Enable(m_pBuildSysCommands->GetSelection() != wxNOT_FOUND);
    FindWindowById(ID_PROP_DELETE_OPTION)->Enable(m_pBuildSysOptions->GetSelection().IsOk());
}

