/**
 * @file wizard_add_fplib.cpp
 */

/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
 *
 * 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, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/** @brief this code creates a wizard to add entries in the footprint library table.
 * The wizard contains 3 pages:
 * The first is the selection of the type of libraries (plugin type):
 *  * Kicad .pretty library (actually a folder containing .kicad_mod footprint files)
 *  * Gihtub .pretty on line library, accessible via a internet connection
 *  * Legacy library (old .mod format file containing footprints desc)
 *  * Eagle xml V6 library (.lbr files)
 *  * Geda-PCB library (actually a folder containing .fp footprint files
 * The second is the selection of path management:
 *  use environment variable or absolute path.
 *  When using an environment variable, selec it
 * The third is the library list selection
 *  It allows entering entries by running a tool to select a set of libraries
 *  The tool depend on the type of lib (files/folder/urls):
 *    * multi files selector
 *    * multi folder selector
 *    * multi URL selector (via a web viewer)
 * The path and the plugin type comes from the selection, and a library nickname
 * is built from the path.
 */

#include <wx/wx.h>
#include <wx/url.h>
#include <wx/progdlg.h>

#include <pgm_base.h>
#include <kiface_i.h>
#include <dialog_helpers.h>
#include <project.h>        // For PROJECT_VAR_NAME definition
#include <fp_lib_table.h>   // For KISYSMOD definition
#include <io_mgr.h>
#include <wizard_add_fplib.h>
#include <dialog_select_dirlist_base.h>

// a key to store the default Kicad Github libs URL
#define KICAD_FPLIBS_URL_KEY wxT( "kicad_fplib_url" )

// key to store last options of th wizard
#define WIZARD_LAST_PLUGIN_KEY wxT( "wizard_plugin" )
#define WIZARD_LAST_PATHOPTION_KEY wxT( "wizard_path_option" )

// static members to store last choices during a session
int WIZARD_FPLIB_TABLE::m_last_plugin_choice = 0;
int WIZARD_FPLIB_TABLE::m_last_defaultpath_choice = 2;

WIZARD_FPLIB_TABLE::WIZARD_FPLIB_TABLE( wxWindow* aParent, wxArrayString& aEnvVariableList )
        : WIZARD_FPLIB_TABLE_BASE( aParent )
{
    initDlg( aEnvVariableList );

    // Allows github plugin selection only when the plugin is compiled:
#ifndef BUILD_GITHUB_PLUGIN
    m_rbFpLibFormat->Enable( GITHUB_PLUGIN, false );

    if( m_rbFpLibFormat->GetSelection() == GITHUB_PLUGIN )
        m_rbFpLibFormat->SetSelection( KICAD_PLUGIN );
#endif

    // Currently, I (JPC) do not know the best way to add/store
    // what is currently called env variables
    // So do not show tools to change them,
    // but do not remove the code, just in case
	m_buttonAddEV->Show( false );
	m_buttonRemoveEV->Show( false );

#ifndef BUILD_GITHUB_PLUGIN
    m_buttonGithubLibList->Show( false );
#endif

    // Gives a minimal size to the dialog, which allows displaying any page
    wxSize minsize;

    for( unsigned ii = 0; ii < m_pages.size(); ii++ )
    {
        wxSize size = m_pages[ii]->GetSizer()->CalcMin();
        minsize.x = std::max( minsize.x, size.x );
        minsize.y = std::max( minsize.y, size.y );
    }

    SetMinSize( minsize );
    SetPageSize( minsize );
    GetSizer()->SetSizeHints( this );
    Center();
}


WIZARD_FPLIB_TABLE::~WIZARD_FPLIB_TABLE()
{
    // Use this if you want to store kicad lib URL in pcbnew/cvpcb section config:
//    wxConfigBase* cfg = Kiface().KifaceSettings();
    // Use this if you want to store kicad lib URL in common section config:
    wxConfigBase* cfg = Pgm().CommonSettings();
    cfg->Write( KICAD_FPLIBS_URL_KEY, m_textCtrlGithubURL->GetValue() );

    m_last_plugin_choice = m_rbFpLibFormat->GetSelection();
    m_last_defaultpath_choice = m_rbPathManagement->GetSelection();

    cfg->Write( WIZARD_LAST_PLUGIN_KEY, m_last_plugin_choice );
    cfg->Write( WIZARD_LAST_PATHOPTION_KEY, m_last_defaultpath_choice );
}


void WIZARD_FPLIB_TABLE::initDlg( wxArrayString& aEnvVariableList )
{
    m_currLibDescr = NULL;

    SetBitmap( KiBitmap( wizard_add_fplib_icon_xpm ) );

    wxString msg;
    wxConfigBase* cfg = Pgm().CommonSettings();
    cfg->Read( WIZARD_LAST_PLUGIN_KEY, &m_last_plugin_choice );
    cfg->Read( WIZARD_LAST_PATHOPTION_KEY, &m_last_defaultpath_choice );
    cfg->Read( KICAD_FPLIBS_URL_KEY, &msg );

    if( msg.IsEmpty() )     // Give our current KiCad github URL
        msg = wxT( "http://github.com/KiCad" );

    // Be sure there is no trailing '/' at the end of the repo name
    if( msg.EndsWith( wxT("/" ) ) )
        msg.RemoveLast();

    m_textCtrlGithubURL->SetValue( msg );

    // KIGITHUB is frequently used (examples in docs, and other place)
    // So add it if it not yet in list, but if it is defined as env var
    // (note this env var is not hardcoded or existing in kicad sources)
    if( aEnvVariableList.Index( wxT("KIGITHUB"), false ) == wxNOT_FOUND )
    {
        //  Not yet in use in lib table, see in env. vars
        wxString evValue;
        wxGetEnv( wxT("KIGITHUB"), &evValue );

        //  Not yet in use in lib table, but it is defined in environment,
        // so add it, just in case
        if( ! evValue.IsEmpty() )
            aEnvVariableList.Add( wxT("KIGITHUB") );
    }

    m_rowPrjEnvVarPosition = 0;
    m_predefinedEnvVarCnt = aEnvVariableList.GetCount();

    for( int row = 0; row < m_predefinedEnvVarCnt; row++ )
    {
        if( GetEnvVarCount() <= row )
            m_gridEnvironmentVariablesList->AppendRows(1);

        m_gridEnvironmentVariablesList->SetCellValue( row, 0, aEnvVariableList[row] );

        if( aEnvVariableList[row] == PROJECT_VAR_NAME )
            m_rowPrjEnvVarPosition = row;

        wxString evValue;

        if( wxGetEnv( aEnvVariableList[row], &evValue ) )
            m_gridEnvironmentVariablesList->SetCellValue( row, 1, evValue );

        // All these env var are defined outside the wizard,
        // and cannot be modified in this dialog
        m_gridEnvironmentVariablesList->SetReadOnly( row, 0, true );
        m_gridEnvironmentVariablesList->SetReadOnly( row, 1, true );
    }

    m_gridEnvironmentVariablesList->Fit();

    m_buttonRemoveEV->Enable( GetEnvVarCount() > m_predefinedEnvVarCnt );
    m_gridEnvironmentVariablesList->AutoSizeColumns();

    m_rbFpLibFormat->SetSelection( m_last_plugin_choice );
    m_rbPathManagement->SetSelection( m_last_defaultpath_choice );

    wxCommandEvent event;
    updateFromPlugingChoice();
    OnPathManagementSelection( event );
}

int WIZARD_FPLIB_TABLE::HasGithubEnvVarCompatible()
{
    // Return true if at least one env var defines a url relative to github
    for( int row = 0; row < GetEnvVarCount(); row++ )
    {
        if( m_gridEnvironmentVariablesList->GetCellValue(
                wxGridCellCoords( row, 1 ) ).Lower().StartsWith( "http" ) )
            return row;
    }

    return -1;
}


bool WIZARD_FPLIB_TABLE::ValidateOptions()
{
    // Some choices can be conficting or do not work.
    // Warn the user when this is the case
    wxString msg;

    if( getSelectedEnvVarValue().IsEmpty() )
    {
        // PROJECT_PATH option cannot be used with empty local path
        if( m_rbPathManagement->GetSelection() == PROJECT_PATH )
        {
            msg = _("The project path is empty and this option is not valid.\n"
                    "Looks like you are running the wizard outside a project.");
            wxMessageBox( msg );
            return false;
        }
        else if( m_rbPathManagement->GetSelection() != ABSOLUTE_PATH )
        {
            wxMessageBox( wxString::Format(
                _("The default path defined by env var \"%s\" is empty.\nCannot use it"),
                GetChars( getSelectedEnvVar() ) ) );
            return false;
        }
    }
    else
    {
        if( IsGithubPlugin() )
        {
            // Github plugin cannot be used with local path; Need absolute path or valid URL
            if( !getSelectedEnvVarValue().Lower().StartsWith( "http" ) )
            {
                msg = _("Github Plugin uses a valid Internet URL starting by http.\n"
                        "Cannot be used as URL");
                wxMessageBox( msg );
                return false;
            }
        }
        else
        {
            if( getSelectedEnvVarValue().Lower().StartsWith( "http" ) )
            {
                msg = _("This default path looks strange.\n"
                        "Cannot be used for a file path");
                wxMessageBox( msg );
                return false;
            }
        }
    }

    // Other conficts: TODO

    return true;
}


void WIZARD_FPLIB_TABLE::OnPluginSelection( wxCommandEvent& event )
{
    updateFromPlugingChoice();
}

void WIZARD_FPLIB_TABLE::updateFromPlugingChoice()
{
    #ifdef BUILD_GITHUB_PLUGIN
    m_buttonGithubLibList->Show( IsGithubPlugin() || IsKicadPlugin() );
    #endif

    // update dialog options and widgets depending on a plugin choice
    // Project path has no sense for GITHUB_PLUGIN
    bool enablePrjPathOpt = not IsGithubPlugin();

    // Project path cannot be used if unknown
    if( m_gridEnvironmentVariablesList->GetCellValue(
                                wxGridCellCoords( m_rowPrjEnvVarPosition, 1 ) ).IsEmpty() )
        enablePrjPathOpt = false;

    m_rbPathManagement->Enable( PROJECT_PATH, enablePrjPathOpt );

    // Sometimes only the choice "absolute path" is allowed;
    // Force this choice, at least make it the default choice
    bool force_absolute_path = false;

    // For github plugin, the project path is not allowed
    if( ( m_rbPathManagement->GetSelection() == PROJECT_PATH ) && !enablePrjPathOpt )
        force_absolute_path = true;

    // For github plugin, at least one github compatible path must exist
    // If no github path, force absolute path
    int first_github_envvar = HasGithubEnvVarCompatible();
    if( IsGithubPlugin() )
    {
        if( first_github_envvar < 0 )
            force_absolute_path = true;
        else if( !getSelectedEnvVarValue().StartsWith( "http" ) )
                m_gridEnvironmentVariablesList->SelectRow( first_github_envvar );

    }

    if( force_absolute_path )
        m_rbPathManagement->SetSelection( ABSOLUTE_PATH );
}


void WIZARD_FPLIB_TABLE::OnPathManagementSelection( wxCommandEvent& event )
{
    // Disable irrevant options, and enable others.
    int row_count = GetEnvVarCount();

    switch( m_rbPathManagement->GetSelection() )
    {
        case PROJECT_PATH:          // Choice = path relative to the project
            m_gridEnvironmentVariablesList->Enable( true );
            m_buttonAddEV->Enable( false );
            m_gridEnvironmentVariablesList->ShowRow( PROJECT_PATH );

            for( int row = 0; row < row_count; row++ )
            {
                if( row == PROJECT_PATH )
                    continue;

                m_gridEnvironmentVariablesList->HideRow( row );
            }
            break;

        case ENV_VAR_PATH:          // Choice = path relative to env var
            m_gridEnvironmentVariablesList->Enable( true );
            m_buttonAddEV->Enable( true );

            for( int row = 0; row < row_count; row++ )
                m_gridEnvironmentVariablesList->ShowRow( row );

            break;

        case ABSOLUTE_PATH:          // Choice = path relative to the project
            m_gridEnvironmentVariablesList->Enable( false );
            m_buttonAddEV->Enable( false );
            break;
    }
}

void WIZARD_FPLIB_TABLE::OnAddEVariable( wxCommandEvent& event )
{
    m_gridEnvironmentVariablesList->AppendRows( 1 );
    m_gridEnvironmentVariablesList->AutoSizeColumns();
    m_buttonRemoveEV->Enable( GetEnvVarCount() > m_predefinedEnvVarCnt );
    m_gridEnvironmentVariablesList->SetGridCursor( GetEnvVarCount()-1, 0 );
}

void WIZARD_FPLIB_TABLE::OnRemoveEVariable( wxCommandEvent& event )
{
    wxArrayInt selectedRows	= m_gridEnvironmentVariablesList->GetSelectedRows();
    int row_cursor = m_gridEnvironmentVariablesList->GetGridCursorRow();

    if( selectedRows.size() == 0 && row_cursor >= 0 )
        selectedRows.Add( row_cursor );

    std::sort( selectedRows.begin(), selectedRows.end() );

    for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
    {
        int row = selectedRows[ii];

        // don't remove them env var. which are already existing in lib table
        if( row > m_predefinedEnvVarCnt-1 )
            m_gridEnvironmentVariablesList->DeleteRows( row, 1 );
    }

    m_gridEnvironmentVariablesList->SelectRow( m_gridEnvironmentVariablesList->GetGridCursorRow() );
    m_buttonRemoveEV->Enable( GetEnvVarCount() > m_predefinedEnvVarCnt );
}

void WIZARD_FPLIB_TABLE::OnSelectEnvVarCell( wxGridEvent& event )
{
    // Ensure the selected row is also the row which have the focus.
    // useful when the user want to delete a row, and select it by the mouse
    m_gridEnvironmentVariablesList->SelectRow( event.GetRow() );
}

wxString WIZARD_FPLIB_TABLE::getSelectedEnvVar()
{
    wxString envVar;
    wxArrayInt selectedRows	= m_gridEnvironmentVariablesList->GetSelectedRows();
    int row   = selectedRows.GetCount() ? selectedRows[0] :
                m_gridEnvironmentVariablesList->GetGridCursorRow();

    switch( m_rbPathManagement->GetSelection() )
    {
        case ENV_VAR_PATH:    // Choice = path relative to env var
            envVar = m_gridEnvironmentVariablesList->GetCellValue(
                                wxGridCellCoords( row, 0 ) );
            break;

        case PROJECT_PATH:    // Choice = path relative to the project
            envVar = PROJECT_VAR_NAME;
            break;

        case ABSOLUTE_PATH:    // Choice = absolute path
        default:
            break;
    }

    return envVar;
}


wxString WIZARD_FPLIB_TABLE::getSelectedEnvVarValue()
{
    wxString envVarValue;
    wxArrayInt selectedRows	= m_gridEnvironmentVariablesList->GetSelectedRows();
    int row   = selectedRows.GetCount() ? selectedRows[0] :
                m_gridEnvironmentVariablesList->GetGridCursorRow();

    switch( m_rbPathManagement->GetSelection() )
    {
        case ENV_VAR_PATH:    // Choice = path relative tp env var
            envVarValue = m_gridEnvironmentVariablesList->GetCellValue(
                                wxGridCellCoords( row, 1 ) );
            break;

        case PROJECT_PATH:    // Choice = path relative to the project
            envVarValue = m_gridEnvironmentVariablesList->GetCellValue(
                                wxGridCellCoords( m_rowPrjEnvVarPosition, 1 ) );
            break;

        case ABSOLUTE_PATH:    // Choice = absolute path
        default:
            break;
    }

    return envVarValue;
}

void WIZARD_FPLIB_TABLE::OnPageChanged( wxWizardEvent& event )
{
    if( GetCurrentPage() == m_pages[2] )
        setLastPage();
    else if( GetCurrentPage() == m_pages[1] )
        setSecondPage();
}

void WIZARD_FPLIB_TABLE::OnPageChanging( wxWizardEvent& event )
{
    if( !( GetCurrentPage() == m_pages[1] && event.GetDirection() ) )
        return;

    if( ! ValidateOptions() )
    {
        event.Veto();
        return;
    }

    if( ( m_rbPathManagement->GetSelection() != ABSOLUTE_PATH ) &&
        ( IsGithubPlugin() ) )
    {
        wxURI uri( getSelectedEnvVarValue() );

        // We cannot use wxURL to test the validity of the url, because
        // wxURL does not know https protocol we are using, and aways returns
        // error for url starting by https
        bool badurl = !uri.HasPath();

        if( badurl )
        {
            wxMessageBox( wxString::Format(
                _("The URL defined by env var \"%s\" is an incorrect URL.\nCannot use it"),
                GetChars( getSelectedEnvVar() ) ) );
            event.Veto();
        }
    }
}

bool WIZARD_FPLIB_TABLE::setSecondPage()
{
    // Init parameters for the second wizard page: this is only
    // the current library description.
    updateFromPlugingChoice();

    delete m_currLibDescr;
    m_currLibDescr = NULL;

    switch( m_rbFpLibFormat->GetSelection() )
    {
        case 0:     // Kicad lib type
            m_currLibDescr = new LIB_DESCR_KICAD;
            m_buttonGithubLibList->SetLabel( _("Download Github Libs") );
            break;

        case 1:     // Github lib type
            m_currLibDescr = new LIB_DESCR_GITHUB;
            m_buttonGithubLibList->SetLabel( _("Github Libs List") );
            break;

        case 2:     // Legacy lib type
            m_currLibDescr = new LIB_DESCR_LEGACY;
            break;

        case 3:     // Eagle V6 lib type
            m_currLibDescr = new LIB_DESCR_EAGLE;
            break;

        case 4:     // Geda lib type
            m_currLibDescr = new LIB_DESCR_GEDA;
            break;
    }

    if( IsGithubPlugin() )
    {
#ifdef KICAD_USE_WEBKIT
        m_buttonAddLib->SetLabel( _("Add Libs with WebViewer") );
#else
        m_buttonAddLib->SetLabel( _("Add FP Library entry") );
#endif
    }
    else
        m_buttonAddLib->SetLabel( _("Add FP Libraries") );

    return m_currLibDescr!= NULL;
}

bool WIZARD_FPLIB_TABLE::setLastPage()     // Init prms for the last wizard page
{
    // Update texts in last wizard page
    m_textPluginType->SetLabel( m_rbFpLibFormat->GetStringSelection() );

    switch( m_rbPathManagement->GetSelection() )
    {
        case ENV_VAR_PATH:    // Choice = path relative env var
        case PROJECT_PATH:    // Choice = path relative to the project
            m_currLibDescr->m_EnvVarName = getSelectedEnvVar();
            m_currLibDescr->m_DefaultPath = getSelectedEnvVarValue();
            m_currLibDescr->m_IsAbsolutePath = false;

            m_textOption->SetLabel( wxString::Format( wxT("%s (%s)"),
                m_rbPathManagement->GetStringSelection().GetData(),
                getSelectedEnvVar().GetData() ) );

            m_textPath->SetLabel( getSelectedEnvVarValue() );
            break;

        case ABSOLUTE_PATH:    // Choice = absolute path
            m_currLibDescr->m_IsAbsolutePath = true;

            m_textOption->SetLabel( m_rbPathManagement->GetStringSelection() );

            if( IsGithubPlugin() )
                m_textPath->SetLabel( _("Full URL") );
            else
                m_textPath->SetLabel( _("Full filename") );
            break;
    }

    return true;
}



void WIZARD_FPLIB_TABLE::OnAddFpLibs( wxCommandEvent& event )
{
    if( m_currLibDescr->m_IsFile )
        selectLibsFiles();
    else if( m_currLibDescr->m_IsGitHub )
        selectLibsGithubWithWebViewer();
    else
        selectLibsFolders();

    m_gridFpListLibs->SetGridCursor( GetLibsCount()-1, 0 );
    m_gridFpListLibs->SelectRow( GetLibsCount()-1 );
}

void WIZARD_FPLIB_TABLE::selectLibsFiles()     // select a set of library files
{
    wxString msk = wxT("*.") + m_currLibDescr->m_Ext;

    wxFileDialog dlg( this, _("Select Library Files"), m_currLibDescr->m_DefaultPath,
                      wxEmptyString, msk,
                      wxFD_DEFAULT_STYLE|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE );

    dlg.ShowModal();

    wxArrayString filepaths;
    dlg.GetPaths( filepaths );

    // Create the nickname: currently make it from the filename
    wxArrayString nicknames;
    wxFileName fn;

    for( unsigned ii = 0; ii < filepaths.GetCount(); ii++ )
    {
        fn = filepaths[ii];
        nicknames.Add( fn.GetName() );

        if( m_currLibDescr->m_IsAbsolutePath || m_currLibDescr->m_DefaultPath.IsEmpty() )
        {
            filepaths[ii] = fn.GetPathWithSep();
        }
        else
        {
            if( ! fn.MakeRelativeTo( m_currLibDescr->m_DefaultPath ) )
                filepaths[ii] = fn.GetFullPath();
            else
                filepaths[ii].Printf( wxT("${%s}%c%s"),
                    GetChars( m_currLibDescr->m_EnvVarName ),
                    fn.GetPathSeparator(),
                    GetChars( fn.GetFullPath() ) );
        }
#ifdef __WINDOWS__
        // We store paths using Unix notation, which also works fine on Windows
        filepaths[ii].Replace( wxT("\\"), wxT("/") );
#endif
    }

    populateLibList( nicknames, filepaths, m_currLibDescr->m_PluginName );
    m_gridFpListLibs->AutoSizeColumns();
}


void WIZARD_FPLIB_TABLE::populateLibList( const wxArrayString& aNickNames,
                                          const wxArrayString& aPaths,
                                          const wxString& aPluginName )
{
    if( aPaths.GetCount() <= 0 )
        return;

    // Ensure there is room for selected libs
    int first_row = m_gridFpListLibs->GetTable()->GetRowsCount();
    m_gridFpListLibs->AppendRows( aPaths.GetCount() );

    // Populates the library list
    for( unsigned ii = 0; ii < aPaths.GetCount(); ii++ )
    {
        int jj = first_row + ii;
        // Add the nickname: currently make it from filename
        m_gridFpListLibs->SetCellValue( jj, 0, aNickNames[ii] );
        // Add the full path:
        m_gridFpListLibs->SetCellValue( jj, 1, aPaths[ii] );
        // Add the plugin name:
        m_gridFpListLibs->SetCellValue( jj, 2, aPluginName );
        m_gridFpListLibs->SetReadOnly( jj, 2, true );
    }

    m_gridFpListLibs->Fit();
}


// A helper dialog to show and select a set of directories
class DIALOG_SELECT_DIRLIST : public DIALOG_SELECT_DIRLIST_BASE
{
public:
    DIALOG_SELECT_DIRLIST( wxWindow* parent,
                              const wxString& aDefaultPath ):
                DIALOG_SELECT_DIRLIST_BASE( parent, wxID_ANY )
    {
        if( !aDefaultPath.IsEmpty() )
            m_dirCtrl->SetPath( aDefaultPath );

        Layout();
        GetSizer()->Fit( this );
        GetSizer()->SetSizeHints(this);
        Centre();
    }

    ~DIALOG_SELECT_DIRLIST() {};

    void GetPaths( wxArrayString& aPaths ) { m_dirCtrl->GetPaths( aPaths ); }
};

void WIZARD_FPLIB_TABLE::selectLibsFolders()   // select a set of library folders
{
    DIALOG_SELECT_DIRLIST dlg( this, m_currLibDescr->m_DefaultPath );

    if( dlg.ShowModal() != wxID_OK )
        return;

    wxArrayString filepaths;

    dlg.GetPaths( filepaths );

    // Create the nickname: currently make it from the filename
    wxArrayString nicknames;
    wxFileName fn;

    for( unsigned ii = 0; ii < filepaths.GetCount(); ii++ )
    {
        fn = filepaths[ii];
        nicknames.Add( fn.GetName() );

        fn.AssignDir( filepaths[ii] );

        if( m_currLibDescr->m_IsAbsolutePath || m_currLibDescr->m_DefaultPath.IsEmpty() )
        {
            filepaths[ii] = fn.GetFullPath();
        }
        else
        {
            if( ! fn.MakeRelativeTo( m_currLibDescr->m_DefaultPath ) )
                filepaths[ii] = fn.GetFullPath();
            else
                filepaths[ii].Printf( wxT("${%s}%c%s"),
                    GetChars( m_currLibDescr->m_EnvVarName ),
                    fn.GetPathSeparator(),
                    GetChars( fn.GetFullPath() ) );
        }
#ifdef __WINDOWS__
        // We store paths using Unix notation, which also works fine on Windows
        filepaths[ii].Replace( wxT("\\"), wxT("/") );
#endif
        // Remove trailing path separator, if any.
        if( filepaths[ii].EndsWith( wxT("/") ) )
            filepaths[ii].RemoveLast();
    }

    populateLibList( nicknames, filepaths, m_currLibDescr->m_PluginName );
}

#ifdef KICAD_USE_WEBKIT

// A helper function to run the wen viewer (see webviewer.cpp)
extern int RunWebViewer( wxWindow * aParent, const wxString& aUrlOnStart,
                         wxArrayString* aUrlListSelection = NULL );
#endif

void WIZARD_FPLIB_TABLE::selectLibsGithubWithWebViewer()    // select a set of library on Github
{
    // A string array to store the URLs selected from the web viewer:
    wxArrayString urls;

    // Run the web viewer and open the default URL: the default path
    // or our github library repos
    wxString defaultURL = m_currLibDescr->m_DefaultPath;

    if( defaultURL.IsEmpty() )
        defaultURL = wxT( "https://github.com/KiCad" );
#ifdef KICAD_USE_WEBKIT
    RunWebViewer( this, defaultURL, &urls );
#else
    // If the Web Viewer is not available, just add a template
    // to the fp lib table.
    // The user have to edit it
    urls.Add( defaultURL + wxT("/newlibname.pretty") );
#endif
    installGithubLibsFromList( urls );
}

void WIZARD_FPLIB_TABLE::installGithubLibsFromList( wxArrayString& aUrlList )
{
    // add the libs found in aUrlList, after calculating a nickname and
    // replacing the path by an env variable, if needed
    // Create the nickname: currently make it from the url
    wxArrayString filepaths;
    wxArrayString nicknames;

    for( unsigned ii = 0; ii < aUrlList.GetCount(); ii++ )
    {
        wxString urlstring( aUrlList[ii] );

        wxURI uri( urlstring );

        // We cannot use wxURL to test the validity of the url, because
        // wxURL does not know https protocol we are using, and aways returns
        // error for URLs starting by https. Hope this test is enough
        if( uri.HasPath() )
            nicknames.Add( uri.GetPath().AfterLast( '/').BeforeLast( '.' ) );
        else
            continue;   // Should not happen: bad URL

        if( m_currLibDescr->m_IsAbsolutePath ||
            m_currLibDescr->m_DefaultPath.IsEmpty() )
        {
            filepaths.Add( aUrlList[ii] );  // use the full URL
        }
        else
        {
            wxString shortURI;
            if( aUrlList[ii].Lower().StartsWith(
                        m_currLibDescr->m_DefaultPath.Lower(), &shortURI ) )
            {
                shortURI.Prepend( wxT("${") + m_currLibDescr->m_EnvVarName + wxT("}") );
                filepaths.Add( shortURI );
            }
            else    //  keep the full URL
                filepaths.Add( aUrlList[ii] );   // use the full URL
        }
    }

    populateLibList( nicknames, filepaths, m_currLibDescr->m_PluginName );
}


void WIZARD_FPLIB_TABLE::OnRemoveFpLibs( wxCommandEvent& event )
{
    wxArrayInt selectedRows	= m_gridFpListLibs->GetSelectedRows();
    int row_cursor = m_gridFpListLibs->GetGridCursorRow();

    if( selectedRows.size() == 0 && row_cursor >= 0 )
        selectedRows.Add( row_cursor );

    std::sort( selectedRows.begin(), selectedRows.end() );

    for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
    {
        int row = selectedRows[ii];
        m_gridFpListLibs->DeleteRows( row, 1 );
    }

    m_gridFpListLibs->SelectRow( m_gridFpListLibs->GetGridCursorRow() );
}

#ifdef BUILD_GITHUB_PLUGIN
#include <../github/github_getliblist.h>

void WIZARD_FPLIB_TABLE::OnGithubLibsList( wxCommandEvent& event )
{
    wxArrayString liblist;
    getLibsListGithub( liblist );

    if( liblist.GetCount() == 0 )   // No lib selected
        return;

    if( IsKicadPlugin() )
    {
        wxString msg;

        if( !downloadGithubLibsFromList( liblist, &msg ) )
        {
            wxMessageBox( msg );
            return;
        }
    }
    else
        installGithubLibsFromList( liblist );
}

void WIZARD_FPLIB_TABLE::getLibsListGithub( wxArrayString& aList )
{
    wxBeginBusyCursor();

    // Be sure there is no trailing '/' at the end of the repo name
    wxString git_url = m_textCtrlGithubURL->GetValue();
    if( git_url.EndsWith( wxT("/" ) ) )
    {
        git_url.RemoveLast();
        m_textCtrlGithubURL->SetValue( git_url );
    }

    GITHUB_GETLIBLIST getter( git_url );

    wxArrayString fullList;
    getter.GetLibraryList( fullList );

    wxEndBusyCursor();

    wxArrayInt choices;
    wxString msg( _( "Urls detected as footprint .pretty libraries.\n"
                     "Selected urls will be added to the current footprint library list" ) );

    if( wxGetSelectedChoices( choices, msg,
                _( "Footprint libraries" ), fullList, this ) <= 0 )
        return;

    // Add selected url in list
    for( unsigned ii = 0; ii < choices.GetCount(); ii++ )
    {
        wxString& url = fullList[choices[ii]];
        aList.Add( url );
    }
}


// Download the .pretty libraries found in aUrlLis and store them on disk
// in a master folder
bool WIZARD_FPLIB_TABLE::downloadGithubLibsFromList( wxArrayString& aUrlList,
                                                     wxString * aErrorMessage )
{
    wxString masterFolder;
    wxString default_path;
    wxGetEnv(  FP_LIB_TABLE::GlobalPathEnvVariableName(), &default_path );

    masterFolder =	wxDirSelector( _("Output Folder" ),
                                   default_path, 0, wxDefaultPosition, this );

    if( masterFolder.IsEmpty() )    // Aborted by user
    {
        if( aErrorMessage )
            *aErrorMessage = _( "Aborted" );

        return false;
    }

    if( !wxDirExists( masterFolder ) )
    {
        if( aErrorMessage )
            aErrorMessage->Printf( _( "Folder '%s' does not exists" ),
                                   GetChars( masterFolder ) );

        return false;
    }

    // Display a progress bar to show the downlaod state
    wxProgressDialog pdlg( _("Download libraries"), wxEmptyString, aUrlList.GetCount() );

    // Download libs:
    for( unsigned ii = 0; ii < aUrlList.GetCount(); ii++ )
    {
        wxString& libsrc_name = aUrlList[ii];
        wxString libdst_name;

        // Extract the lib name from the full URL:
        wxURI url( libsrc_name );
        wxFileName fn( url.GetPath() );
        // Set our local path
        fn.SetPath( masterFolder );
        libdst_name = fn.GetFullPath();

        if( !wxDirExists( libdst_name ) )
            wxMkdir( libdst_name );

        pdlg.Update( ii, libsrc_name);

        try
        {
            PLUGIN::RELEASER src( IO_MGR::PluginFind( IO_MGR::GITHUB ) );
            PLUGIN::RELEASER dst( IO_MGR::PluginFind( IO_MGR::KICAD ) );

            wxArrayString footprints = src->FootprintEnumerate( libsrc_name );

            for( unsigned i = 0;  i < footprints.size();  ++i )
            {
                std::auto_ptr<MODULE> m( src->FootprintLoad( libsrc_name, footprints[i] ) );
                dst->FootprintSave( libdst_name, m.get() );
                // m is deleted here by auto_ptr.
            }
        }
        catch( const IO_ERROR& ioe )
        {
            if( aErrorMessage )
                aErrorMessage->Printf( _("Error:\n'%s'\nwhile downloading library:\n'%s'"),
                                       GetChars( ioe.errorText ), GetChars( libsrc_name ) );
            return false;
        }
    }

    return true;
}
#endif

