/**
 * @file sel_layer.cpp
 * @brief dialogs for one layer selection and a layer pair selection.
 */
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
 * Copyright (C) 1992-2013 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
 */


#include <fctsys.h>
#include <common.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <wxBasePcbFrame.h>
#include <pcbcommon.h>
#include <class_layer_box_selector.h>
#include <class_board.h>
#include <dialogs/dialog_layer_selection_base.h>


/* classes to display a layer list using a wxGrid.
 */
class PCB_LAYER_SELECTOR: public LAYER_SELECTOR
{
    BOARD * m_brd;

public:
    PCB_LAYER_SELECTOR( BOARD* aBrd ):LAYER_SELECTOR()
    {
        m_brd = aBrd;
    }

protected:
    // Returns true if the layer id is enabled (i.e. is it should be displayed)
    bool IsLayerEnabled( LAYER_NUM aLayer ) const
    {
        return m_brd->IsLayerEnabled( aLayer );
    }

    // Returns a color index from the layer id
    // Virtual function
    EDA_COLOR_T GetLayerColor( LAYER_NUM aLayer ) const
    {
        return m_brd->GetLayerColor( aLayer );
    }

    // Returns the name of the layer id
    // Virtual function
    wxString GetLayerName( LAYER_NUM aLayer ) const
    {
        return m_brd->GetLayerName( aLayer );
    }

};

/*
 * This class display a pcb layers list in adialog,
 * to select one layer from this list
 */
class PCB_ONE_LAYER_SELECTOR : public PCB_LAYER_SELECTOR,
                               public DIALOG_LAYER_SELECTION_BASE
{
    LAYER_NUM m_layerSelected;
    LAYER_NUM m_minLayer;
    LAYER_NUM m_maxLayer;
    std::vector<LAYER_NUM> m_layersIdLeftColumn;
    std::vector<LAYER_NUM> m_layersIdRightColumn;

public:
    PCB_ONE_LAYER_SELECTOR( wxWindow* aParent, BOARD * aBrd,
                        LAYER_NUM aDefaultLayer,
                        LAYER_NUM aMinLayer = -1, LAYER_NUM aMaxLayer = -1,
                        bool aClearTool = false )
        :PCB_LAYER_SELECTOR( aBrd ), DIALOG_LAYER_SELECTION_BASE( aParent )
        {
            m_layerSelected = (int) aDefaultLayer;
            // When not needed, remove the "Clear" button
            m_buttonClear->Show( aClearTool );
            m_minLayer = aMinLayer;
            m_maxLayer = aMaxLayer;
            BuildList();
            Layout();
            GetSizer()->SetSizeHints(this);
            SetFocus();
        }

    LAYER_NUM GetLayerSelection() { return m_layerSelected; }

private:
    // Event handlers
    void OnLeftGridClick( wxGridEvent& event );
    void OnRightGridClick( wxGridEvent& event );
	void OnClearSelection( wxCommandEvent& event )
    {
        m_layerSelected = NB_PCB_LAYERS;
        EndModal( NB_PCB_LAYERS );
    }

    void BuildList();
};

// Build the layers list
// Column position by function:
#define SELECT_COLNUM 0
#define COLOR_COLNUM 1
#define LAYERNAME_COLNUM 2
static DECLARE_LAYERS_ORDER_LIST( layertranscode );

void PCB_ONE_LAYER_SELECTOR::BuildList()
{
    // Hide layerid column which is used only to know the layer id
    // not to be shown in dialogs
    m_leftGridLayers->SetColSize( COLOR_COLNUM, 20 );
    m_rightGridLayers->SetColSize( COLOR_COLNUM, 20 );

    int left_row = 0;
    int right_row = 0;
    wxString   layername;
    for( LAYER_NUM i = FIRST_LAYER; i < NB_LAYERS; ++i )
    {
        LAYER_NUM  layerid = i;

        if( m_layerorder )
            layerid = layertranscode[i];

        if( ! IsLayerEnabled( layerid ) )
            continue;

        if( m_minLayer >= 0 && layerid < m_minLayer )
            continue;

        if( m_maxLayer >= 0 && layerid > m_maxLayer )
            continue;

        wxColour color = MakeColour( GetLayerColor( layerid ) );
        layername = GetLayerName( layerid );

        if( layerid <= LAST_COPPER_LAYER )
        {
            if( left_row )
                m_leftGridLayers->AppendRows( 1 );

            m_leftGridLayers->SetCellBackgroundColour ( left_row, COLOR_COLNUM,
                                                        color );
            m_leftGridLayers->SetCellValue( left_row, LAYERNAME_COLNUM,
                                            layername );

            if( m_layerSelected == layerid )
            {
                m_leftGridLayers->SetCellValue( left_row, SELECT_COLNUM,
                                                wxT("X") );
                m_leftGridLayers->SetCellBackgroundColour ( left_row, SELECT_COLNUM,
                                                        color );
                m_leftGridLayers->SetGridCursor( left_row, LAYERNAME_COLNUM );
            }

            m_layersIdLeftColumn.push_back( layerid );
            left_row++;
        }
        else
        {
            if( right_row )
                m_rightGridLayers->AppendRows( 1 );

            m_rightGridLayers->SetCellBackgroundColour ( right_row, COLOR_COLNUM,
                                                         color );
            m_rightGridLayers->SetCellValue( right_row, LAYERNAME_COLNUM,
                                             layername );

            if( m_layerSelected == layerid )
            {
                m_rightGridLayers->SetCellValue( right_row, SELECT_COLNUM,
                                                 wxT("X") );
                m_rightGridLayers->SetCellBackgroundColour ( right_row, SELECT_COLNUM,
                                                         color );
                m_rightGridLayers->SetGridCursor( right_row, LAYERNAME_COLNUM );
            }

            m_layersIdRightColumn.push_back( layerid );
            right_row++;
        }
    }

    // Show only populated lists:
    if( left_row <= 0 )
        m_leftGridLayers->Show( false );

    if( right_row <= 0 )
        m_rightGridLayers->Show( false );

    m_leftGridLayers->AutoSizeColumn(LAYERNAME_COLNUM);
    m_rightGridLayers->AutoSizeColumn(LAYERNAME_COLNUM);
    m_leftGridLayers->AutoSizeColumn(SELECT_COLNUM);
    m_rightGridLayers->AutoSizeColumn(SELECT_COLNUM);
}

void PCB_ONE_LAYER_SELECTOR::OnLeftGridClick( wxGridEvent& event )
{
    m_layerSelected = m_layersIdLeftColumn[ event.GetRow() ];
    EndModal( 1 );
}

void PCB_ONE_LAYER_SELECTOR::OnRightGridClick( wxGridEvent& event )
{
    m_layerSelected = m_layersIdRightColumn[ event.GetRow() ];
    EndModal( 2 );
}

/** Install the dialog box for layer selection
 * @param aDefaultLayer = Preselection (NB_PCB_LAYERS for "(Deselect)" layer)
 * @param aMinlayer = min layer id value (-1 if no min value)
 * @param aMaxLayer = max layer id value (-1 if no max value)
 * @param aDeselectTool = display a "Clear" button when true
 * @return new layer value (NB_PCB_LAYERS when "(Deselect)" radiobutton selected),
 *                         or -1 if canceled
 *
 * Providing the option to also display a "Clear" button makes the
 * "Swap Layers" command more "user friendly",
 * by permitting any layer to be "deselected" immediately after its
 * corresponding radiobutton has been clicked on. (It would otherwise be
 * necessary to first cancel the "Select Layer:" dialog box (invoked after a
 * different radiobutton is clicked on) prior to then clicking on the
 * "Clear" button provided within the "Swap Layers:"
 * or "Layer selection:" dialog box).
 */
LAYER_NUM PCB_BASE_FRAME::SelectLayer( LAYER_NUM  aDefaultLayer,
                                       LAYER_NUM  aMinlayer,
                                       LAYER_NUM  aMaxLayer,
                                       bool aDeselectTool )
{
    PCB_ONE_LAYER_SELECTOR dlg( this, GetBoard(),
                                aDefaultLayer, aMinlayer, aMaxLayer,
                                aDeselectTool );
    dlg.ShowModal();
    LAYER_NUM layer = dlg.GetLayerSelection();
    return layer;
}


/*
 * This class display a double pcb copper layers list in a dialog,
 * to select a layer pair from this list
 */
class SELECT_COPPER_LAYERS_PAIR_DIALOG: public PCB_LAYER_SELECTOR,
                                        public DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE
{
private:
    BOARD* m_brd;
    LAYER_NUM m_frontLayer;
    LAYER_NUM m_backLayer;
    int m_leftRowSelected;
    int m_rightRowSelected;
    std::vector<LAYER_NUM> m_layersId;

public:
    SELECT_COPPER_LAYERS_PAIR_DIALOG( wxWindow* aParent, BOARD * aPcb,
                                      LAYER_NUM aFrontLayer, LAYER_NUM aBackLayer );

    void GetLayerPair( LAYER_NUM& aFrontLayer, LAYER_NUM& aBackLayer )
    {
        aFrontLayer = m_frontLayer;
        aBackLayer = m_backLayer;
    }

private:
    void OnLeftGridClick( wxGridEvent& event );
    void OnRightGridClick( wxGridEvent& event );

    void OnOkClick( wxCommandEvent& event )
    {
        EndModal( wxID_OK );
    }

    void OnCancelClick( wxCommandEvent& event )
    {
        EndModal( wxID_CANCEL );
    }

    void BuildList();

};

/* Display a list of two copper layers to choose a pair of layers
 * for auto-routing, vias ...
 */
void PCB_BASE_FRAME::SelectLayerPair()
{
    PCB_SCREEN* screen = GetScreen();
    SELECT_COPPER_LAYERS_PAIR_DIALOG dlg( this, GetBoard(),
                                         screen->m_Route_Layer_TOP,
                                         screen->m_Route_Layer_BOTTOM );

    if( dlg.ShowModal() == wxID_OK )
    {
        dlg.GetLayerPair( screen->m_Route_Layer_TOP, screen->m_Route_Layer_BOTTOM );

        // select the same layer for both layers is allowed (normal in some boards)
        // but could be a mistake. So display an info message
        if( screen->m_Route_Layer_TOP == screen->m_Route_Layer_BOTTOM )
            DisplayInfoMessage( this,
                                _( "Warning: The Top Layer and Bottom Layer are same." ) );
    }

    m_canvas->MoveCursorToCrossHair();
}

SELECT_COPPER_LAYERS_PAIR_DIALOG::
    SELECT_COPPER_LAYERS_PAIR_DIALOG( wxWindow* aParent, BOARD * aPcb,
                                      LAYER_NUM aFrontLayer, LAYER_NUM aBackLayer) :
    PCB_LAYER_SELECTOR( aPcb ),
    DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE( aParent )
{
    m_frontLayer = aFrontLayer;
    m_backLayer = aBackLayer;
    m_leftRowSelected = 0;
    m_rightRowSelected = 0;
    BuildList();
    SetFocus();
    GetSizer()->SetSizeHints( this );
    Center();
}

void SELECT_COPPER_LAYERS_PAIR_DIALOG::BuildList()
{
    m_leftGridLayers->SetColSize( COLOR_COLNUM, 20 );
    m_rightGridLayers->SetColSize( COLOR_COLNUM, 20 );

    // Select a not show cell, to avoid a wrong cell selection for user

    int row = 0;
    wxString   layername;

    for( LAYER_NUM i = FIRST_LAYER; i < NB_LAYERS; ++i )
    {
        LAYER_NUM  layerid = i;

        if( m_layerorder )
            layerid = layertranscode[i];

        if( ! IsLayerEnabled( layerid ) )
            continue;

        if( layerid > LAST_COPPER_LAYER )
            continue;

        wxColour color = MakeColour( GetLayerColor( layerid ) );
        layername = GetLayerName( layerid );

        if( row )
            m_leftGridLayers->AppendRows( 1 );

        m_leftGridLayers->SetCellBackgroundColour ( row, COLOR_COLNUM,
                                                    color );
        m_leftGridLayers->SetCellValue( row, LAYERNAME_COLNUM,
                                        layername );
        m_layersId.push_back( layerid );

        if( m_frontLayer == layerid )
        {
            m_leftGridLayers->SetCellValue( row, SELECT_COLNUM,
                                            wxT("X") );
            m_leftGridLayers->SetCellBackgroundColour( row, SELECT_COLNUM,
                                                       color );
            m_leftRowSelected = row;
            m_leftGridLayers->SetGridCursor( row, LAYERNAME_COLNUM );
        }

        if( row )
            m_rightGridLayers->AppendRows( 1 );
        m_rightGridLayers->SetCellBackgroundColour ( row, COLOR_COLNUM,
                                                     color );
        m_rightGridLayers->SetCellValue( row, LAYERNAME_COLNUM,
                                         layername );

        if( m_backLayer == layerid )
        {
            m_rightGridLayers->SetCellValue( row, SELECT_COLNUM,
                                             wxT("X") );
            m_rightGridLayers->SetCellBackgroundColour ( row, SELECT_COLNUM,
                                                     color );
            m_rightRowSelected = row;
            m_rightGridLayers->SetGridCursor( row, LAYERNAME_COLNUM );
        }

        row++;
    }

    m_leftGridLayers->AutoSizeColumn(LAYERNAME_COLNUM);
    m_rightGridLayers->AutoSizeColumn(LAYERNAME_COLNUM);
    m_leftGridLayers->AutoSizeColumn(SELECT_COLNUM);
    m_rightGridLayers->AutoSizeColumn(SELECT_COLNUM);
}

void SELECT_COPPER_LAYERS_PAIR_DIALOG::OnLeftGridClick( wxGridEvent& event )
{
    int row = event.GetRow();
    LAYER_NUM layer = m_layersId[row];

    if( m_frontLayer == layer )
        return;

    m_leftGridLayers->SetCellValue( m_leftRowSelected, SELECT_COLNUM,
                                     wxEmptyString );
    m_leftGridLayers->SetCellBackgroundColour ( m_leftRowSelected, SELECT_COLNUM,
                    m_leftGridLayers->GetDefaultCellBackgroundColour() );

    m_frontLayer = layer;
    m_leftRowSelected = row;
    m_leftGridLayers->SetCellValue( row, SELECT_COLNUM,
                                     wxT("X") );
    m_leftGridLayers->SetCellBackgroundColour( row, SELECT_COLNUM,
                        MakeColour( GetLayerColor( layer ) ) );

    m_leftGridLayers->SetGridCursor( row, LAYERNAME_COLNUM );
}

void SELECT_COPPER_LAYERS_PAIR_DIALOG::OnRightGridClick( wxGridEvent& event )
{
    int row = event.GetRow();
    LAYER_NUM layer = m_layersId[row];

    if(  m_backLayer == layer )
        return;

    m_rightGridLayers->SetCellValue( m_rightRowSelected, SELECT_COLNUM,
                                     wxEmptyString );
    m_rightGridLayers->SetCellBackgroundColour ( m_rightRowSelected, SELECT_COLNUM,
                m_rightGridLayers->GetDefaultCellBackgroundColour() );

    m_backLayer = layer;
    m_rightRowSelected = row;
    m_rightGridLayers->SetCellValue( row, SELECT_COLNUM,
                                     wxT("X") );
    m_rightGridLayers->SetCellBackgroundColour ( row, SELECT_COLNUM,
                                MakeColour( GetLayerColor( layer ) ) );
    m_rightGridLayers->SetGridCursor( row, LAYERNAME_COLNUM );
}
