/////////////////////////////////////////////////////////////////////////////
// Name:        browsepanel.cpp
// Purpose:     wxPackageBrowserPanel
// Author:      Julian Smart, Francesco Montorsi
// Modified by:
// Created:     04/06/2006 12:04:43
// RCS-ID:      $Id: browsepanel.cpp,v 1.8 2007/02/02 09:42:57 frm Exp $
// Copyright:   (c) 2004 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 <wx/notebook.h>
#include <wx/config.h>
#include <wx/uri.h>
#include <wx/stdpaths.h>
#include <wx/dialup.h>
#include <wx/mstream.h>
#include <wx/zstream.h>

#include "guipm/smartmsgdlg.h"
#include "guipm/browsepanel.h"
#include "guipm/filterpanel.h"
#include "guipm/viewerpanel.h"
#include "guipm/extcmddlg.h"
#include "guipm/builddlg.h"
#include "guipm/batchdownloaddlg.h"
#include "guipm/decompressdlg.h"
#include "guipm/dependencydlg.h"

#include "wx/download.h"
#include "wxp/wxp.h"

// statics:

wxString wxPackageBrowserPanel::s_strDefaultWxlName;
wxString wxPackageBrowserPanel::s_strDefaultWxlURL;


// ----------------------------------------------------------------------------
// wxPackageBrowserPanel
// ----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS( wxPackageBrowserPanel, wxPanel )

BEGIN_EVENT_TABLE( wxPackageBrowserPanel, wxPanel )
    EVT_BUTTON( wxID_ANY, wxPackageBrowserPanel::OnButtonClick )
    EVT_UPDATE_UI( wxID_ANY, wxPackageBrowserPanel::OnUpdateUI )
    EVT_IDLE( wxPackageBrowserPanel::OnIdle )
    EVT_LISTBOX( wxID_ANY, wxPackageBrowserPanel::OnListBoxSelected )
    EVT_NOTEBOOK_PAGE_CHANGED( wxID_ANY, wxPackageBrowserPanel::OnPageChanged )

    EVT_FILTER_PACKAGES( wxID_ANY, wxPackageBrowserPanel::OnFilterPackages )

    EVT_PROGRESS_END(wxID_ANY, wxPackageBrowserPanel::OnProgressPanelEnd)

    // source list
    EVT_TEXT_ENTER( ID_BROWSER_SOURCELIST, wxPackageBrowserPanel::OnSourceListEnter )
    EVT_COMBOBOX( ID_BROWSER_SOURCELIST, wxPackageBrowserPanel::OnSourceListSelected )
END_EVENT_TABLE()

wxPackageBrowserPanel::wxPackageBrowserPanel( )
{
}

wxPackageBrowserPanel::wxPackageBrowserPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
    Create(parent, id, pos, size, style);
}

wxPackageBrowserPanel::~wxPackageBrowserPanel( )
{
}

bool wxPackageBrowserPanel::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
    // auxiliary stuff
    m_pViewerPanel = NULL;
    m_pStatusBar = NULL;
    m_pMenuBar = NULL;
    m_bNeedLocalPackageRefresh = false;
    m_bAutoLoadLocalPackages = true;

    SetExtraStyle(GetExtraStyle()|wxWS_EX_VALIDATE_RECURSIVELY);
    wxPanel::Create( parent, id, pos, size, style );

    CreateControls();
    GetSizer()->Fit(this);
    GetSizer()->SetSizeHints(this);

    return true;
}

void wxPackageBrowserPanel::CreateControls()
{
////@begin wxPackageBrowserPanel content construction
    wxPackageBrowserPanel* itemPanel1 = this;

    wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
    itemPanel1->SetSizer(itemBoxSizer2);

    m_pNotebook = new wxNotebook( itemPanel1, ID_BROWSER_NOTEBOOK, wxDefaultPosition, wxDefaultSize, wxNB_DEFAULT|wxNB_TOP );

    wxPanel* itemPanel4 = new wxPanel( m_pNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
    wxBoxSizer* itemBoxSizer5 = new wxBoxSizer(wxVERTICAL);
    itemPanel4->SetSizer(itemBoxSizer5);

    m_pLocalFilter = new wxPackageFilterPanel( itemPanel4, ID_BROWSER_INSTALLED_FILTER, wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer5->Add(m_pLocalFilter, 0, wxGROW|wxALL, 5);

    wxStaticText* itemStaticText7 = new wxStaticText( itemPanel4, wxID_STATIC, _("Local packages:"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer5->Add(itemStaticText7, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT|wxTOP|wxADJUST_MINSIZE, 5);

    wxBoxSizer* itemBoxSizer8 = new wxBoxSizer(wxHORIZONTAL);
    itemBoxSizer5->Add(itemBoxSizer8, 1, wxGROW, 5);
    m_pLocalList = new wxPackageListBox( itemPanel4, ID_BROWSER_INSTALLED_PACKAGELIST, wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer8->Add(m_pLocalList, 1, wxGROW|wxALL, 5);

    wxBoxSizer* itemBoxSizer10 = new wxBoxSizer(wxVERTICAL);
    itemBoxSizer8->Add(itemBoxSizer10, 0, wxALIGN_BOTTOM, 5);
    wxButton* itemButton11 = new wxButton( itemPanel4, ID_BROWSER_INSTALLED_REFRESH, _("&Refresh"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer10->Add(itemButton11, 0, wxGROW|wxALL, 5);

    itemBoxSizer10->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);

    wxButton* itemButton13 = new wxButton( itemPanel4, ID_BROWSER_DECOMPRESS, _("&Unpack..."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer10->Add(itemButton13, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    wxButton* itemButton14 = new wxButton( itemPanel4, ID_BROWSER_REBUILD, _("(Re)&build..."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer10->Add(itemButton14, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    wxButton* itemButton15 = new wxButton( itemPanel4, ID_BROWSER_REINSTALL, _("(Re)&install..."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer10->Add(itemButton15, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    wxButton* itemButton16 = new wxButton( itemPanel4, ID_BROWSER_UNINSTALL, _("&Uninstall..."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer10->Add(itemButton16, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    itemBoxSizer10->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);

    wxButton* itemButton18 = new wxButton( itemPanel4, ID_BROWSER_UPDATECHECK, _("Update &check"), wxDefaultPosition, wxDefaultSize, 0 );
    itemButton18->SetDefault();
    itemBoxSizer10->Add(itemButton18, 0, wxGROW|wxALL, 5);

    m_pNotebook->AddPage(itemPanel4, _("Local packages"));

    wxPanel* itemPanel19 = new wxPanel( m_pNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
    wxBoxSizer* itemBoxSizer20 = new wxBoxSizer(wxVERTICAL);
    itemPanel19->SetSizer(itemBoxSizer20);

    wxStaticBox* itemStaticBoxSizer21Static = new wxStaticBox(itemPanel19, wxID_ANY, _("Package repository URL"));
    wxStaticBoxSizer* itemStaticBoxSizer21 = new wxStaticBoxSizer(itemStaticBoxSizer21Static, wxHORIZONTAL);
    itemBoxSizer20->Add(itemStaticBoxSizer21, 0, wxGROW|wxALL, 5);
    wxString* m_pSourceListStrings = NULL;
    m_pSourceList = new wxComboBox( itemPanel19, ID_BROWSER_SOURCELIST, _T(""), wxDefaultPosition, wxSize(100, -1), 0, m_pSourceListStrings, wxCB_DROPDOWN );
    itemStaticBoxSizer21->Add(m_pSourceList, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);

    m_pNewFilter = new wxPackageFilterPanel( itemPanel19, ID_BROWSER_NEW_FILTER, wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer20->Add(m_pNewFilter, 0, wxGROW|wxALL, 5);

    wxStaticText* itemStaticText24 = new wxStaticText( itemPanel19, wxID_STATIC, _("Available packages:"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer20->Add(itemStaticText24, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP|wxADJUST_MINSIZE, 5);

    wxBoxSizer* itemBoxSizer25 = new wxBoxSizer(wxHORIZONTAL);
    itemBoxSizer20->Add(itemBoxSizer25, 1, wxGROW, 5);
    m_pNewList = new wxPackageListBox( itemPanel19, ID_BROWSER_NEW_PACKAGELIST, wxDefaultPosition, wxDefaultSize, wxLB_MULTIPLE );
    itemBoxSizer25->Add(m_pNewList, 1, wxGROW|wxALL, 5);

    wxBoxSizer* itemBoxSizer27 = new wxBoxSizer(wxVERTICAL);
    itemBoxSizer25->Add(itemBoxSizer27, 0, wxALIGN_BOTTOM, 5);
    wxButton* itemButton28 = new wxButton( itemPanel19, ID_BROWSER_NEW_SELECTALL, _("Select &All"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer27->Add(itemButton28, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    wxButton* itemButton29 = new wxButton( itemPanel19, ID_BROWSER_NEW_DESELECTALL, _("&Deselect All"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer27->Add(itemButton29, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5);

    wxButton* itemButton30 = new wxButton( itemPanel19, ID_BROWSER_NEW_REFRESH, _("&Refresh"), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer27->Add(itemButton30, 0, wxGROW|wxALL, 5);

    itemBoxSizer27->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);

    wxButton* itemButton32 = new wxButton( itemPanel19, ID_BROWSER_DOWNLOAD, _("&Download..."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer27->Add(itemButton32, 0, wxGROW|wxALL, 5);

    m_pNotebook->AddPage(itemPanel19, _("Get new packages"));

    itemBoxSizer2->Add(m_pNotebook, 1, wxGROW, 5);

////@end wxPackageBrowserPanel content construction

    // every pixel is preciuos in the horizontal direction!
#define RATIO 0.9

    m_pLocalFilter->SetMinSize(m_pLocalFilter->GetBestSize().Scale(RATIO, 1.0));
    m_pNewFilter->SetMinSize(m_pNewFilter->GetBestSize().Scale(RATIO, 1.0));

    // init POD
    m_bShowDupPackages = false;
    m_pNewList->ShowStatus(false);
}



// ----------------------------------------------------------------------------
// wxPackageBrowserPanel - additional functions
// ----------------------------------------------------------------------------

void wxPackageBrowserPanel::SetAuxiliaryViewerPanel(wxPackageViewerPanel *p)
{
    m_pViewerPanel=p;
    m_pViewerPanel->GetTaskQueuePanel()->SetEventHandler(this);
}

void wxPackageBrowserPanel::FilterNewPackages()
{
    // since new packages listbox is a multiple selection listbox,
    // we will restore the selection only on the first package...
    wxPackage sel = m_pNewList->GetFirstSelectedPackage();
    m_pNewList->SetPackages(m_arrNew, m_pNewFilter);
    m_pNewList->RefreshAll();

    // eventually update selection
    m_pNewList->SetSelectedPackage(sel);

    size_t max = m_arrNew.GetCount();
    SetStatusText(
        wxString::Format(_("Filtered out %d package(s) on a total of %d..."),
                            max - m_pLocalList->GetCount(),
                            max));
}

void wxPackageBrowserPanel::FilterLocalPackages()
{
    wxPackage sel = m_pLocalList->GetSelectedPackage();
    m_pLocalList->SetPackages(m_arrLocal, m_pLocalFilter);
    m_pLocalList->RefreshAll();

    // eventually update selection
    m_pLocalList->SetSelectedPackage(sel);

    size_t max = m_arrLocal.GetCount();
    SetStatusText(
        wxString::Format(_("Filtered out %d package(s) on a total of %d..."),
                            max - m_pLocalList->GetCount(),
                            max));
}

bool wxPackageBrowserPanel::SetRepositoryURL(const wxString &str)
{
    wxString url(str);
 /*   if (wxIsFileProtocol(url))
    {
        wxLogError(_("To browse local repositories, just add the folder you want to browse in the 'Local repositories' path list in the 'Settings' dialog."));
        return false;
        /*
        // be sure that this is a folder or an url to a WXL file and that it does exist
        wxFileName fn(wxFileSystem::URLToFileName(url));
        if (fn.IsDir())
        {
            if (!fn.DirExists())
                return false;

            // make sure this is a valid URL
            url = wxFileSystem::FileNameToURL(fn.GetPathWithSep());
        }
        else
        {
            if (fn.GetExt() != wxT("wxl"))
                return false;

            url = wxFileSystem::FileNameToURL(fn.GetFullPath());
        }*
    }
    else if (wxIsWebProtocol(url))
    {*/
        if (!url.EndsWith(wxT(".wxl")) && !url.EndsWith(wxT("/")))
            url += wxT("/");

        // cannot do any existence test here...
   // }

    // prepend the given URL to the list (i.e. most recently added strings
    // are at the top of the list)
    int n = m_pSourceList->FindString(url);
    if (n == wxNOT_FOUND)
    {
        m_pSourceList->Insert(url, 0);
        m_pSourceList->SetSelection(0);
    }
    else
        m_pSourceList->SetSelection(n);

    //RefreshPackageList();  no refresh in this function !

    return true;
}

bool wxPackageBrowserPanel::BrowseTo(const wxString &path)
{
    BrowseNewPackages();
    if (!SetRepositoryURL(path))
        return false;

    ResetFilter();
    RefreshPackageList();
    return true;
}

void wxPackageBrowserPanel::SelectLocalPackage(unsigned int n)
{
    BrowseLocalPackages();
    m_pLocalList->SetSelection(n);
    wxCommandEvent fake;
    OnListBoxSelected(fake);
}

void wxPackageBrowserPanel::SelectLocalPackage(const wxPackageId &id)
{
    BrowseLocalPackages();

    if (m_pLocalList->SetSelectedPackage(id))
    {
        wxCommandEvent fake;
        OnListBoxSelected(fake);
    }
}

void wxPackageBrowserPanel::SelectNewPackage(unsigned int n)
{
    BrowseNewPackages();
    m_pNewList->SetSelection(n);

    wxCommandEvent fake;
    OnListBoxSelected(fake);
}

void wxPackageBrowserPanel::SelectNewPackage(const wxPackageId &id)
{
    BrowseNewPackages();

    if (m_pNewList->SetSelectedPackage(id))
    {
        wxCommandEvent fake;
        OnListBoxSelected(fake);
    }
}

void wxPackageBrowserPanel::RefreshLocalPackages()
{
    // before reloading the packages info, save
    // the updated info for the current array of packages:
    // FIXME: wxConfigBase::Get() is used here - wxPackageBrowserPanel should
    //        not use such global object!
    m_arrLocal.SavePackagesStatusInfo(wxConfigBase::Get(), wxT("/Packages"));

    // reset the local package list:
    m_arrLocal.Clear();

    // add list of local (maybe not-installed) package list
    m_arrLocal.LoadCompressedPackagesFromLocalRepo();

    // do we want to remove duplicate packages ?
    if (!m_bShowDupPackages)
    {
        // FIXME: show a warning to the user about this !
        size_t n = m_arrLocal.RemoveDuplicates();

        if (GetAuxiliaryStatusBar())
            GetAuxiliaryStatusBar()->SetStatusText(
                wxString::Format(_("Removed %d duplicate package(s)"), n));
    }

    // reload packages info
    // FIXME: wxConfigBase::Get() is used here - wxPackageBrowserPanel should
    //        not use such global object!
    if (!m_arrLocal.LoadPackagesStatusInfo(wxConfigBase::Get(), wxT("/Packages")))
    {
        wxLogError(_("Cannot load status of the local packages.\nCheck your wxConfig installation."));
        return;
    }

    // VERY IMPORTANT: LoadPackageStatusInfo() could have changed the status
    //                 of the packages from wxPS_DOWNLOADED to wxPS_REMOTE
    //                 (because, e.g. the WXZ file has not been found)

    // as last thing, apply the filter on the package list
    FilterLocalPackages();
}

void wxPackageBrowserPanel::RefreshNewPackages()
{
    // the currently selected string in the combobox should always
    // point to a directory !
    wxString sourcedir(m_pSourceList->GetValue());

    // clear old list
    m_pNewList->Clear();
    m_pNewList->RefreshAll();   // this is indispensable to clear old list 
                                // in case for some reason the download fails
                                // and FilterNewPackages() is not called

    if (!sourcedir.EndsWith(wxT(".wxl")))
    {
        if (!sourcedir.EndsWith(wxT("/")))
            sourcedir += wxT("/");
        sourcedir += wxString(s_strDefaultWxlName);
    }

    // is this an URL to a local/remote WXL or
    // rather an URL to a local/remote directory ?
    if (sourcedir.EndsWith(wxT(".wxl")))
        //||  wxIsWebProtocol(sourcedir))
    {
        wxPackageRepositoryXMLDescriptor repo;
        if (true)//wxIsWebProtocol(sourcedir))
        {
            // are we connected ?
            wxDialUpManager *mng = wxDialUpManager::Create();
            if (mng->IsOk())
            {
                if (!mng->IsOnline())
                {
                    // we may be wrong so give the user a choice
                    int reply = wxSmartLogYesNoQuestion(wxT("internetconn"),
                        _("It has been detected that you _may_ be not connected to Internet !\nA working connection is required in order to download the '%s' package list...\nDo you want to proceed anyway?"),
                        sourcedir.c_str());
                    if (reply == wxID_NO)
                    {
                        wxDELETE(mng);
                        return;
                    }
                }
            }
            wxDELETE(mng);

            // do an asynchronous download of the package list
            wxMemoryOutputStream stream;
            wxDownloadDialog dlg;
            if (!dlg.Create(sourcedir, &stream, wxT("Downloading..."), wxEmptyString, 
                            wxTheApp->GetTopWindow()))
                return;

            if (dlg.ShowModal() == wxDRF_FAILED)
            {
                wxLogError(_("Invalid remote repository URL '%s'.\nCould not download it."),
                           sourcedir.c_str());
                return;
            }

            if (dlg.GetReturnCode() == wxDRF_USER_ABORTED)
                return;

            // decompress the list and load it
            wxMemoryInputStream input(stream);
            wxZlibInputStream unzipped(input);
            if (!repo.Load(unzipped))
            {
                wxLogError(_("Invalid repository '%s'.\nIt's not a valid WXL file."),
                           sourcedir.c_str());
                return;
            }
        }
        else
        {
            /*
            // do a synchronous load of the package list
            if (!repo.LoadLocalPackagesList(sourcedir))
            {
                wxMessageBox(wxString::Format(_T("Invalid local repository URL '%s'.\nCould not find that WXL file."), sourcedir.c_str()),
                            _T("Error"), wxOK|wxICON_ERROR);
                return;
            }*/

            wxASSERT(0);
        }

        // copy the list of packages loaded from this WXL
        m_arrNew = repo.GetPackageArray(sourcedir);
    }
    else
    {
        /*wxASSERT(wxIsFileProtocol(sourcedir));

        // use the synchronous version of wxPackageArray::Load for local packages
        // as we suppose the packages will be quickly available and thus there's
        // no need to show a progress dialog
        wxFileName fn(wxFileSystem::URLToFileName(sourcedir));
        if (!GetNewPackages().LoadCompressedPackagesFrom(fn.GetPath()))
        {
            wxMessageBox(wxString::Format(_T("Invalid local directory '%s'.\nCould not find any WXZ file."), sourcedir.c_str()),
                        _T("Error"), wxOK|wxICON_ERROR);
            return;
        }*/
    }

    FilterNewPackages();
}

void wxPackageBrowserPanel::RefreshPackageList()
{
    if (IsBrowsingLocalPackages())
        RefreshLocalPackages();
    else
        RefreshNewPackages();

    wxCommandEvent fake;
    OnListBoxSelected(fake);
}

void wxPackageBrowserPanel::FilterPackageList()
{
    if (IsBrowsingLocalPackages())
        FilterLocalPackages();
    else
        FilterNewPackages();

    wxCommandEvent fake;
    OnListBoxSelected(fake);
}

bool wxPackageBrowserPanel::IsPackageSourceLocal() const
{
    wxURI uri(m_pSourceList->GetValue());

    if (uri.GetScheme() == wxT("http") ||
        uri.GetScheme() == wxT("ftp"))
        return false;
    return true;
}

void wxPackageBrowserPanel::EnableCmd(long id, bool enable)
{
    wxWindow *win = FindWindowById(id, this);
    wxASSERT_MSG(win!=NULL, wxT("Invalid ID!"));
    win->Enable(enable);

    wxMenuItem *item;
    if (GetAuxiliaryMenuBar() && (item = GetAuxiliaryMenuBar()->FindItem(id)) != NULL)
        item->Enable(enable);
}

wxPackageArray wxPackageBrowserPanel::GetSelectedPackages() const
{
    wxPackageArray arr;

    if (IsBrowsingNewPackages())
    {
        unsigned long cookie;
        int item = m_pNewList->GetFirstSelected(cookie);
        while ( item != wxNOT_FOUND )
        {
            // copy all selected packages to a temporary array
            arr.Add(m_pNewList->GetPackage(item));
            item = m_pNewList->GetNextSelected(cookie);
        }
    }
    else
    {
        // be sure to add a package != wxEmptyPackage
        if (m_pLocalList->GetSelection() != wxNOT_FOUND)
            arr.Add(m_pLocalList->GetSelectedPackage());
    }

    return arr;
}

bool wxPackageBrowserPanel::LoadCompressedPackage(const wxString &wxzpath)
{
    wxPackage pkg;
    if (!pkg.LoadCompressedPackage(wxzpath))
        return false;

    m_arrLocal.Add(pkg);
    if (m_arrLocal.RemoveDuplicates() == 1)
        return false;

    FilterLocalPackages();
    return true;
}

void wxPackageBrowserPanel::Download(const wxPackageArray &todownload, bool skipdepcheck)
{
    wxPackageArray arr(todownload);

#ifdef __WXDEBUG__
    for (size_t i=0; i<todownload.GetCount(); i++)
        wxASSERT(m_arrNew.Contains(todownload[i]));
#endif

    // show dep
    if (!skipdepcheck)
    {
        wxPackageDependencyDlg dlg;     // do not Create() it yet
        if (!dlg.SetPackageArray(arr, m_arrLocal, m_arrNew))
            return;
        if (!dlg.IsEmpty())
        {
            dlg.Create(this);
            if (dlg.ShowModal() == wxID_NO)
                return;

            wxPackageArray temp(dlg.GetAdditionalPackages());
            WX_APPEND_ARRAY(arr, temp);
        }
    }

    //if (arr.GetCount() > 1)
    //{
        // queue a list of packages to download
        wxPackageBatchDownloadDlg dlg(this, wxID_ANY);
        dlg.SetDestination(m_strDefaultDownloadPath);
        dlg.SetPackages(arr);

        wxPackageDownloadQueuePanel *panel =
            GetAuxiliaryViewerPanel()->GetDownloadQueuePanel();
        if (dlg.ShowModal() == wxID_OK)
        {
            for (size_t i=0; i < arr.GetCount(); i++)
            {
                wxString url = arr[i].GetDownloadLink();
                if (url.IsEmpty())
                {
                    wxLogError(wxT("No URLs are available for the download of '%s' package. Cannot download it."), arr[i].GetName().c_str());
                    continue;
                }

                // tell the package that it's still a remote package
                // but nonetheless tell him also the path where it's being
                // downloaded so that the download queue panel will be
                // able to show it to the user
                arr[i].SetRemoteStatus(
                    dlg.GetDestination() +
                    wxFileName::GetPathSeparator() +
                    url.AfterLast(wxT('/')));

                // last, queue it
                panel->QueueDownloadOf(arr[i]);
            }

            GetAuxiliaryViewerPanel()->SelectDownloadQueuePanel();
        }/*
    }
    else
    {
        // queue the single package selected directly
        GetAuxiliaryViewerPanel()->GetDownloadQueuePanel()->QueueDownloadOf(arr[0]);
        GetAuxiliaryViewerPanel()->SelectDownloadQueuePanel();
    }*/
}

void wxSafeMkdir(const wxString &path)
{
    if (!wxFileName::DirExists(path))
    {
        // ignore any writing-permission error...
        wxLogNull nullLog;
        wxMkdir(path);
    }
}

bool wxPackageBrowserPanel::Load(wxConfigBase *p, const wxString &path)
{
    wxString defpath = wxStandardPaths::Get().GetUserDataDir();

    // paths
    m_strDefaultDownloadPath =
        p->Read(path + wxT("/Directories/DownloadPath"), defpath);
    m_strDefaultDecompressionPath =
        p->Read(path + wxT("/Directories/DecompressionPath"), defpath);
    m_strDefaultInstallationPath =
        p->Read(path + wxT("/Directories/InstallationPath"), defpath);

    // be sure these exist
    wxSafeMkdir(m_strDefaultDownloadPath);
    wxSafeMkdir(m_strDefaultDecompressionPath);
    wxSafeMkdir(m_strDefaultInstallationPath);

    // load the sources from wxPackage
    m_pSourceList->Append(wxPackage::s_arrRemoteRepo);
    if (m_pSourceList->GetCount() > 0)
        m_pSourceList->Select(0);      // by default select first item

    if (m_pSourceList->GetCount() == 0)
    {
        // append & select the default source
        SetRepositoryURL(s_strDefaultWxlURL);
    }

    p->Read(path + wxT("/Packages/AutoLoadPackages"), &m_bAutoLoadLocalPackages, true);

    // we won't call any Refresh*Packages() function here
    // as it could take a lot of time (which goes in the app's startup time)!
    // Rather we'll call RefreshLocalPackages() in the idle event handler
    m_bNeedLocalPackageRefresh = m_bAutoLoadLocalPackages;
    // wxWakeUpIdle(); -- it seems that this is not needed

    return true;
}

void wxPackageBrowserPanel::Save(wxConfigBase *p, const wxString &path) const
{
    // save our settings

    p->Write(path + wxT("/Directories/DownloadPath"), m_strDefaultDownloadPath);
    p->Write(path + wxT("/Directories/DecompressionPath"), m_strDefaultDecompressionPath);
    p->Write(path + wxT("/Directories/InstallationPath"), m_strDefaultInstallationPath);

    p->Write(path + wxT("/Packages/AutoLoadPackages"), m_bAutoLoadLocalPackages);

    // save sources
    wxPackage::s_arrRemoteRepo = m_pSourceList->GetStrings();

    // save local package status
    m_arrLocal.SavePackagesStatusInfo(p, path + wxT("/Packages"));
}



// ----------------------------------------------------------------------------
// wxPackageBrowserPanel - event handlers
// ----------------------------------------------------------------------------

void wxPackageBrowserPanel::OnButtonClick(wxCommandEvent &ev)
{
    wxCommandEvent fake;

    // the package selected in the 'local packages' list...
    wxPackage &p = m_pLocalList->GetSelectedPackage();

    switch (ev.GetId())
    {
    // buttons for the "Installed packages" tab
/*
    case ID_BROWSER_INSTALLED_SELECTALL:
        for (size_t i=0; i<m_pLocalList->GetCount(); i++)
            m_pLocalList->Select(i);

        // send fake event to force update of auxiliary panels
        fake.SetId(ID_BROWSER_INSTALLED_PACKAGELIST);
        fake.SetEventObject(m_pLocalList);
        OnListBoxSelected(fake);
        break;
    case ID_BROWSER_INSTALLED_DESELECTALL:
        m_pLocalList->DeselectAll();
        if (GetAuxiliaryViewerPanel())
            GetAuxiliaryViewerPanel()->Reset();
        break;
*/
    case ID_BROWSER_INSTALLED_REFRESH:
        RefreshPackageList();
        break;

    case ID_BROWSER_DOWNLOAD:
        {
            wxASSERT(GetAuxiliaryViewerPanel());

            // get the array of selected packages
            wxPackageArray arr(GetSelectedPackages());
            wxASSERT(arr.GetCount() > 0);

            Download(arr);
        }
        break;

    case ID_BROWSER_DECOMPRESS:
        {
            wxPackageDecompressDlg dlg(this, wxID_ANY);

            // the package could already have a decompression path
            // so let's check first
            if (p.GetDecompressionPath().IsEmpty())
                p.SetDecompressionPath(m_strDefaultDecompressionPath);

            dlg.SetPackage(p);
            if (dlg.ShowModal() == wxID_OK)
            {
                // update decompression path
                p.SetDecompressedStatus(dlg.GetDestination());

                // update package also in our local array
                m_arrLocal.UpdatePackage(p);
                m_pLocalList->RefreshAll();
            }
        }
        break;

    case ID_BROWSER_REBUILD:
        {
            wxASSERT(GetAuxiliaryViewerPanel());

            // show dep
            {
                wxPackageDependencyDlg dlg;     // do not Create() it yet
                if (!dlg.SetPackage(p, m_arrLocal, m_arrNew))
                    return;
                if (!dlg.IsEmpty())
                {
                    dlg.Create(this);
                    if (dlg.ShowModal() == wxID_NO)
                        return;

                    // skip the dependency check control as we have just done it !
                    Download(dlg.GetAdditionalPackages(), true);
                    return;
                }
            }

            wxPackageBuildDlg dlg(this, wxID_ANY, wxT("Build"));
            dlg.SetPackage(p);
            if (dlg.ShowModal() == wxID_OK)
            {
                // NB: dlg.GetPackage() != p as user has edited its options!
                GetAuxiliaryViewerPanel()->GetTaskQueuePanel()->
                    QueueBuildOf(dlg.GetPackage());
                GetAuxiliaryViewerPanel()->SelectTaskQueuePage();
            }
        }
        break;
    case ID_BROWSER_REINSTALL:
        {
            wxExtCmdWizard dlg(this, wxID_ANY, wxT("Reinstall"));
            dlg.Set(p, wxPBSS_INSTALL);
            dlg.Run();
        }
        break;
    case ID_BROWSER_UNINSTALL:
        {
            wxExtCmdWizard dlg(this, wxID_ANY, wxT("Uninstall"));
            dlg.Set(p, wxPBSS_UNINSTALL);
            dlg.Run();
        }
        break;
    case ID_BROWSER_UPDATECHECK:
        {
            int n = m_arrNew.IndexByName(p.GetName());
            if (n == wxNOT_FOUND)
            {
                wxLogMessage(_("Cannot find the '%s' package in the repository!\nNo update-check is possible..."),
                            p.GetName().c_str());
            }
            else if (m_arrNew[n].GetVersion() <= p.GetVersion())
            {
                wxLogMessage(_("No updates available for %s %s in the repository...\nThe most updated version in the repository is %s."),
                            p.GetName().c_str(), p.GetVersion().GetAsString().c_str(),
                            m_arrNew[n].GetVersion().GetAsString().c_str());
            }
            else if (m_arrNew[n].GetVersion() > p.GetVersion())
            {
                int reply = wxSmartLogYesNoQuestion(
                                wxT("updatecheck"),
                                _("%s %s is available for download in the repository...\nWould you like to download the updated package ?"),
                                m_arrNew[n].GetName().c_str(),
                                m_arrNew[n].GetVersion().GetAsString().c_str());
                if (reply == wxID_YES)
                {
                    Download(m_arrNew[n]);
                }
            }
        }
        break;


    // buttons for the "Get new packages" tab

    case ID_BROWSER_NEW_SELECTALL:
        for (size_t i=0; i<m_pNewList->GetCount(); i++)
            m_pNewList->Select(i);

        // send fake event to force update of auxiliary panels
        fake.SetId(ID_BROWSER_NEW_PACKAGELIST);
        fake.SetEventObject(m_pNewList);
        OnListBoxSelected(fake);
        break;
    case ID_BROWSER_NEW_DESELECTALL:
        m_pNewList->DeselectAll();
        if (GetAuxiliaryViewerPanel())
            GetAuxiliaryViewerPanel()->Reset();
        break;
    case ID_BROWSER_NEW_REFRESH:
        RefreshPackageList();
        break;
    }
}

void wxPackageBrowserPanel::OnUpdateUI(wxUpdateUIEvent & WXUNUSED(ev))
{
    bool hasauxiliaryviewer = GetAuxiliaryViewerPanel() != NULL;
    if (IsBrowsingLocalPackages())
    {
        wxPackage &p = m_pLocalList->GetSelectedPackage();
        bool enable = p.IsOk() && hasauxiliaryviewer;

        //EnableCmd(ID_BROWSER_INSTALLED_SELECTALL, !m_pLocalList->IsEmpty());
        //EnableCmd(ID_BROWSER_INSTALLED_DESELECTALL, enable);

        EnableCmd(ID_BROWSER_DECOMPRESS, p.GetStatus() >= wxPS_DOWNLOADED && enable);
        EnableCmd(ID_BROWSER_REBUILD, p.GetStatus() >= wxPS_DECOMPRESSED && enable);
        EnableCmd(ID_BROWSER_REINSTALL, p.GetStatus() >= wxPS_BUILT && enable);
        EnableCmd(ID_BROWSER_UNINSTALL, p.GetStatus() >= wxPS_INSTALLED && enable);
        EnableCmd(ID_BROWSER_UPDATECHECK, enable);

        // we must disable the new package stuff because EnableCmd
        // also works on menu items
        EnableCmd(ID_BROWSER_NEW_SELECTALL, false);
        EnableCmd(ID_BROWSER_NEW_DESELECTALL, false);
        EnableCmd(ID_BROWSER_DOWNLOAD, false);
        //EnableCmd(ID_BROWSER_BUILD, false);
        //EnableCmd(ID_BROWSER_INSTALL, false);
    }
    else
    {
        bool enable = m_pNewList->GetSelectedCount() != 0;

        EnableCmd(ID_BROWSER_NEW_SELECTALL, !m_pNewList->IsEmpty());
        EnableCmd(ID_BROWSER_NEW_DESELECTALL, enable);
        EnableCmd(ID_BROWSER_DOWNLOAD, enable);
        //EnableCmd(ID_BROWSER_BUILD, enable && hasauxiliaryviewer);
        //EnableCmd(ID_BROWSER_INSTALL, enable);

        //EnableCmd(ID_BROWSER_INSTALLED_SELECTALL, false);
        //EnableCmd(ID_BROWSER_INSTALLED_DESELECTALL, false);
        EnableCmd(ID_BROWSER_REBUILD, false);
        EnableCmd(ID_BROWSER_REINSTALL, false);
        EnableCmd(ID_BROWSER_UNINSTALL, false);
        EnableCmd(ID_BROWSER_UPDATECHECK, false);
    }
}

void wxPackageBrowserPanel::OnListBoxSelected(wxCommandEvent &WXUNUSED(ev))
{
    wxPackageArray arr(GetSelectedPackages());

    // update auxiliary windows if present...
    if (GetAuxiliaryViewerPanel())
    {
        // this call may take some time as it may need to download
        // stuff from www; in the meanwhile show the busy cursor
        wxWindowDisabler disable;
        wxBusyCursor wait;
        GetAuxiliaryViewerPanel()->SetPackages(arr);

        // FIXME: under win at least, somehow the busy cursor disappear
        //        before the summary & details appear in the viewer panel...
        //        the following calls seems to have no effects
        wxYieldIfNeeded();
        GetAuxiliaryViewerPanel()->Refresh();
        GetAuxiliaryViewerPanel()->Update();
    }

    if (GetAuxiliaryStatusBar())
        GetAuxiliaryStatusBar()->SetStatusText(
            wxString::Format(_("Selected %d package(s)."), arr.GetCount()));
}

void wxPackageBrowserPanel::OnFilterPackages(wxCommandEvent &WXUNUSED(ev))
{
    // run the filter
    FilterPackageList();
}

void wxPackageBrowserPanel::OnSourceListEnter(wxCommandEvent &ev)
{
    // update the repository URL
    wxString str(ev.GetString());
    SetRepositoryURL(str);
}

void wxPackageBrowserPanel::OnSourceListSelected(wxCommandEvent & WXUNUSED(ev))
{
    RefreshPackageList();
}

void wxPackageBrowserPanel::OnPageChanged(wxNotebookEvent & WXUNUSED(ev))
{
    static bool firsttime = true;
    if (IsBrowsingNewPackages() && firsttime)
    {
        firsttime = false;
        RefreshPackageList();
    }

    wxCommandEvent fake;
    OnListBoxSelected(fake);
}

void wxPackageBrowserPanel::OnProgressPanelEnd(wxCommandEvent &ev)
{
    // is a package build ended ?
    if (ev.GetInt() == wxPBSS_BUILD && ev.GetExtraLong() == 1)
    {
        wxString pkgname = ev.GetString();
        int idx = m_arrLocal.IndexByName(pkgname);
        wxASSERT(idx != wxNOT_FOUND);

        m_arrLocal[idx].SetBuiltStatus();
        RefreshPackageList();
    }
}

void wxPackageBrowserPanel::OnIdle(wxIdleEvent& WXUNUSED(ev))
{
    if (m_bNeedLocalPackageRefresh)
    {
        RefreshLocalPackages();
        m_bNeedLocalPackageRefresh = false;
    }
}



// ----------------------------------------------------------------------------
// wxPackageListBox
// ----------------------------------------------------------------------------

wxPackageListBox::wxPackageListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos,
                                            const wxSize& size, long style, const wxString& name)
{
    // create base class
    wxHtmlListBox::Create(parent, id, pos, size, style|wxSUNKEN_BORDER, name);
    //SetSelectionBackground(wxColour(wxT("DARK GREEN")));
    SetMargins(5, 5);

    m_bShowStatus = true;
}

wxString wxPackageListBox::OnGetItem(size_t n) const
{
    /*if (IsSelected(n) && !m_items[n].GetWorkingPath().IsEmpty())
        return wxString::Format(wxT("%s<div align='right'>(in path: <tt>%s</tt>)</div>"),
                                m_items[n].GetCommand().c_str(),
                                m_items[n].GetWorkingPath().c_str());*/
    if (!m_bShowStatus)
        return wxString::Format(wxT("<b>%s</b>"), m_items[n].GetName().c_str());

    return wxString::Format(wxT("<b>%s</b> [%s]"),
            m_items[n].GetName().c_str(),
            //m_items[n].GetVersion().c_str(),
            m_items[n].GetStatusStr().MakeLower().c_str());
}

void wxPackageListBox::SetPackages(const wxPackageArray &arr, wxPackageFilterPanel *filter)
{
    Clear();
    for (size_t i=0; i < arr.GetCount(); i++)
    {
        if (filter->IsToPreserve(arr[i]))
            Append(arr[i]);
    }
}

