/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright The 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <wx/sizer.h>
#include <wx/aui/auibook.h>
#include <wx/msgdlg.h>
#include <confirm.h>
#include <lib_table_grid_data_model.h>
#include <lib_table_notebook_panel.h>
#include <libraries/library_manager.h>
#include <pgm_base.h>


LIB_TABLE_NOTEBOOK_PANEL::~LIB_TABLE_NOTEBOOK_PANEL()
{
    // Delete the GRID_TRICKS.
    GetGrid()->PopEventHandler( true );

    GetGrid()->Unbind( wxEVT_GRID_CELL_CHANGING, &LIB_TABLE_NOTEBOOK_PANEL::onGridCellChanging, this );
}


void LIB_TABLE_NOTEBOOK_PANEL::AddTable( wxAuiNotebook* aNotebook, const wxString& aTitle, bool aClosable )
{
    LIB_TABLE_NOTEBOOK_PANEL* panel = new LIB_TABLE_NOTEBOOK_PANEL( aNotebook, wxID_ANY );
   	wxBoxSizer*               sizer = new wxBoxSizer( wxVERTICAL );
    WX_GRID*                  grid = new WX_GRID( panel, wxID_ANY );

   	// Grid
   	grid->CreateGrid( 1, 7 );
   	grid->EnableGridLines( true );
   	grid->SetMargins( 0, 0 );
    grid->SetSelectionMode( wxGrid::wxGridSelectRows );

   	// Columns
   	grid->SetColSize( 0, 30 );
   	grid->SetColSize( 1, 48 );
   	grid->SetColSize( 2, 48 );
   	grid->SetColSize( 3, 240 );
   	grid->SetColSize( 4, 100 );
   	grid->SetColSize( 5, 80 );
   	grid->SetColSize( 6, 240 );
   	grid->SetColLabelSize( 22 );
   	grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );

   	// Rows
   	grid->EnableDragRowSize( false );
   	grid->SetRowLabelSize( 0 );
   	grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );

   	// Cell Defaults
   	grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );

    grid->DisableColResize( COL_STATUS );
    grid->DisableColResize( COL_VISIBLE );
    grid->DisableColResize( COL_ENABLED );

    grid->AutoSizeColumn( COL_VISIBLE, true );
    grid->AutoSizeColumn( COL_ENABLED, true );

    sizer->Add( grid, 1, wxALL|wxEXPAND, 5 );

    panel->SetSizer( sizer );
    panel->SetClosable( aClosable );
   	panel->Layout();
   	sizer->Fit( panel );

    grid->Bind( wxEVT_GRID_CELL_CHANGING, &LIB_TABLE_NOTEBOOK_PANEL::onGridCellChanging, panel );

    panel->m_baseTitle = aTitle;
    aNotebook->AddPage( panel, aTitle, false );
}


void LIB_TABLE_NOTEBOOK_PANEL::MarkDirty()
{
    wxAuiNotebook* notebook = dynamic_cast<wxAuiNotebook*>( GetParent() );

    if( !notebook )
        return;

    int page = notebook->GetPageIndex( this );

    if( page == wxNOT_FOUND )
        return;

    if( !notebook->GetPageText( page ).EndsWith( wxT( " *" ) ) )
        notebook->SetPageText( page, m_baseTitle + wxT( " *" ) );
}


void LIB_TABLE_NOTEBOOK_PANEL::ClearDirty()
{
    wxAuiNotebook* notebook = dynamic_cast<wxAuiNotebook*>( GetParent() );

    if( !notebook )
        return;

    int page = notebook->GetPageIndex( this );

    if( page == wxNOT_FOUND )
        return;

    notebook->SetPageText( page, m_baseTitle );
}


bool LIB_TABLE_NOTEBOOK_PANEL::TableModified()
{
    LIBRARY_TABLE& table = GetModel()->Table();
    wxFileName     file( table.Path() );

    std::unique_ptr<LIBRARY_TABLE> sourceTable =
            std::make_unique<LIBRARY_TABLE>( file, LIBRARY_TABLE_SCOPE::GLOBAL );

    if( table.IsReadOnly() )
    {
        // Apply stored overrides to the source table so we compare against the
        // "loaded with overrides" state, not the raw on-disk state.
        Pgm().GetLibraryManager().ApplyLibOverrides( *sourceTable );

        if( sourceTable->Rows().size() != table.Rows().size() )
            return false;

        for( size_t i = 0; i < table.Rows().size(); ++i )
        {
            const LIBRARY_TABLE_ROW& current = table.Rows()[i];
            const LIBRARY_TABLE_ROW& source  = sourceTable->Rows()[i];

            if( current.Disabled() != source.Disabled() || current.Hidden() != source.Hidden() )
                return true;
        }

        return false;
    }

    return table != *sourceTable;
}


void LIB_TABLE_NOTEBOOK_PANEL::onGridCellChanging( wxGridEvent& aEvent )
{
    int      row = aEvent.GetRow();
    int      col = aEvent.GetCol();

    if( col == COL_URI || col == COL_TYPE || col == COL_OPTIONS )
    {
        WX_GRID* grid = GetGrid();
        wxString editValue = grid->GetCellValue( row, col );

        if( wxGridCellEditor* cellEditor = grid->GetCellEditor( row, col ) )
        {
            if( cellEditor->IsCreated() && cellEditor->GetWindow()->IsShown() )
                editValue = cellEditor->GetValue();

            cellEditor->DecRef();
        }

        grid->GetTable()->SetValue( row, col, editValue );

        if( GetModel()->Adapter() )
        {
            LIBRARY_TABLE_ROW& tableRow = GetModel()->At( row );
            GetModel()->Adapter()->CheckTableRow( tableRow );
        }

        grid->RefreshBlock( row, COL_STATUS, row, COL_STATUS );
    }
}


bool LIB_TABLE_NOTEBOOK_PANEL::SaveTable()
{
    LIBRARY_TABLE& table = GetModel()->Table();

    if( table.IsReadOnly() )
    {
        bool retVal = SaveOverrides();

        if( retVal )
            ClearDirty();

        return retVal;
    }

    bool retVal = true;

    table.Save().map_error(
            [&]( const LIBRARY_ERROR& aError )
            {
                wxMessageBox( _( "Error saving nested library table:\n\n" ) + aError.message,
                              _( "File Save Error" ), wxOK | wxICON_ERROR );

                retVal = false;
            } );

    if( retVal )
        ClearDirty();

    return retVal;
}


bool LIB_TABLE_NOTEBOOK_PANEL::SaveOverrides()
{
    LIBRARY_TABLE& table     = GetModel()->Table();
    wxString       tablePath = table.Path();

    LIBRARY_MANAGER& libMgr = Pgm().GetLibraryManager();

    for( const LIBRARY_TABLE_ROW& row : table.Rows() )
    {
        if( row.Disabled() || row.Hidden() )
            libMgr.SetLibOverride( tablePath, row.Nickname(), row.Disabled(), row.Hidden() );
        else
            libMgr.ClearLibOverride( tablePath, row.Nickname() );
    }

    return true;
}


bool LIB_TABLE_NOTEBOOK_PANEL::GetCanClose()
{
    if( !GetGrid()->CommitPendingChanges() )
        return false;

    if( TableModified() )
    {
        if( !HandleUnsavedChanges( this, _( "Save changes to nested library table?" ),
                                   [&]() -> bool
                                   {
                                       return SaveTable();
                                   } ) )
        {
            return false;
        }
    }

    return true;
}

