/*
    Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
    Copyright (c) 2010 Andras Mantia <andras@kdab.com>

    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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include "filteractionwidget.h"

#include "filteraction.h"
#include "filtermanager.h"
#include "mailfilter.h"
#include "minimumcombobox.h"

#include <KLocalizedString>
#include <KPushButton>

#include <QtGui/QGridLayout>
#include <QtGui/QLabel>

using namespace MailCommon;

//=============================================================================
//
// class FilterActionWidget
//
//=============================================================================

class FilterActionWidget::Private
{
  public:
  Private( FilterActionWidget *qq )
    : q( qq )
    {
    }

  ~Private()
    {
      qDeleteAll( mActionList );
      mActionList.clear();
    }

  void setFilterAction( QWidget *widget = 0 );

  void slotFilterTypeChanged( int index );
  void slotAddWidget();
  void slotRemoveWidget();
  
  FilterActionWidget *q;
  QList<MailCommon::FilterAction*> mActionList;
  KComboBox *mComboBox;
  KPushButton *mAdd;
  KPushButton *mRemove;

  QGridLayout *mLayout;
};

void FilterActionWidget::Private::setFilterAction( QWidget *widget )
{
  if ( mLayout->itemAtPosition( 1, 2 ) )
    delete mLayout->itemAtPosition( 1, 2 )->widget();

  if ( widget )
    mLayout->addWidget( widget, 1, 2 );
  else
    mLayout->addWidget( new QLabel( i18n( "Please select an action." ), q ), 1, 2 );
}

void FilterActionWidget::Private::slotAddWidget()
{
  emit q->addWidget( q );
  emit q->filterModified();
}

void FilterActionWidget::Private::slotRemoveWidget()
{
  emit q->removeWidget( q );
  emit q->filterModified();
}

void FilterActionWidget::Private::slotFilterTypeChanged( int index )
{
  setFilterAction( index < mActionList.count() ? mActionList.at( index )->createParamWidget( q ) : 0 );
}


FilterActionWidget::FilterActionWidget( QWidget *parent )
  : KHBox( parent ), d( new Private( this ) )
{
  QWidget *widget = new QWidget( this );

  d->mLayout = new QGridLayout( widget );
  d->mLayout->setContentsMargins( 0, 0, 0, 0 );

  d->mComboBox = new MinimumComboBox( widget );
  d->mComboBox->setEditable( false );
  Q_ASSERT( d->mComboBox );
  d->mLayout->addWidget( d->mComboBox, 1, 1 );
  d->mAdd = new KPushButton( widget );
  d->mAdd->setIcon( KIcon( "list-add" ) );
  d->mAdd->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );

  d->mRemove = new KPushButton( widget );
  d->mRemove->setIcon( KIcon( "list-remove" ) );
  d->mRemove->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
  
  setSpacing( 4 );

  int index;
  QList<FilterActionDesc*> list = MailCommon::FilterManager::filterActionDict()->list();
  QList<FilterActionDesc*>::const_iterator it;  
  QList<FilterActionDesc*>::const_iterator end( list.constEnd() );
  for ( index = 0, it = list.constBegin() ; it != end ; ++it, ++index ) {
    //create an instance:
    FilterAction *action = (*it)->create();

    // append to the list of actions:
    d->mActionList.append( action );

    // add (i18n-ized) name to combo box
    d->mComboBox->addItem( (*it)->label );

    // Register the FilterAction modification signal
    connect( action, SIGNAL(filterActionModified()), this, SIGNAL(filterModified()) );
  }

  // widget for the case where no action is selected.
  d->mComboBox->addItem( " " );
  d->mComboBox->setCurrentIndex( index );

  // don't show scroll bars.
  d->mComboBox->setMaxCount( d->mComboBox->count() );

  // layout management:
  // o the combo box is not to be made larger than it's sizeHint(),
  //   the parameter widget should grow instead.
  // o the whole widget takes all space horizontally, but is fixed vertically.
  d->mComboBox->adjustSize();
  d->mComboBox->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
  setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
  updateGeometry();

  // redirect focus to the filter action combo box
  setFocusProxy( d->mComboBox );

  // now connect the combo box and the widget stack
  connect( d->mComboBox, SIGNAL(activated(int)),
           this, SLOT(slotFilterTypeChanged(int)) );

  connect( d->mComboBox, SIGNAL(activated(int)),
           this, SIGNAL(filterModified()) );

  connect( d->mAdd, SIGNAL(clicked()),
           this, SLOT(slotAddWidget()) );
  connect( d->mRemove, SIGNAL(clicked()),
           this, SLOT(slotRemoveWidget()) );
  
  d->setFilterAction();  
  d->mLayout->addWidget( d->mAdd, 1, 3 );
  d->mLayout->addWidget( d->mRemove, 1, 4 );
  
}

FilterActionWidget::~FilterActionWidget()
{
  delete d;
}

void FilterActionWidget::updateAddRemoveButton( bool addButtonEnabled, bool removeButtonEnabled )
{
  d->mAdd->setEnabled( addButtonEnabled );
  d->mRemove->setEnabled( removeButtonEnabled );
}

void FilterActionWidget::setAction( const FilterAction *action )
{
  bool found = false;
  const int count = d->mComboBox->count() - 1 ; // last entry is the empty one
  const QString label = (action ? action->label() : QString());

  // find the index of typeOf(action) in mComboBox
  // and clear the other widgets on the way.
  for ( int i = 0; i < count ; ++i ) {
    if ( action && d->mComboBox->itemText( i ) == label ) {
      d->setFilterAction( d->mActionList.at( i )->createParamWidget( this ) );

      //...set the parameter widget to the settings
      // of aAction...
      action->setParamWidgetValue( d->mLayout->itemAtPosition( 1, 2 )->widget() );

      //...and show the correct entry of
      // the combo box
      d->mComboBox->setCurrentIndex( i ); // (mm) also raise the widget, but doesn't
      found = true;
    }
  }

  if ( found )
    return;

  // not found, so set the empty widget
  d->setFilterAction();

  d->mComboBox->setCurrentIndex( count ); // last item
}

FilterAction* FilterActionWidget::action() const
{
  // look up the action description via the label
  // returned by KComboBox::currentText()...
  FilterActionDesc *description = MailCommon::FilterManager::filterActionDict()->value( d->mComboBox->currentText() );
  if ( description ) {
    // ...create an instance...
    FilterAction *action = description->create();
    if ( action ) {
      // ...and apply the setting of the parameter widget.
      action->applyParamWidgetValue( d->mLayout->itemAtPosition( 1, 2 )->widget() );
      return action;
    }
  }

  return 0;
}


//=============================================================================
//
// class FilterActionWidgetLister (the filter action editor)
//
//=============================================================================

class FilterActionWidgetLister::Private
{
  public:
    Private( FilterActionWidgetLister *qq )
      : q( qq ), mActionList( 0 )
    {
    }

    void regenerateActionListFromWidgets();

    FilterActionWidgetLister *q;
    QList<MailCommon::FilterAction*> *mActionList;
};

void FilterActionWidgetLister::Private::regenerateActionListFromWidgets()
{
  if ( !mActionList )
    return;

  mActionList->clear();

  foreach ( const QWidget *widget, q->widgets() ) {
    FilterAction *action = qobject_cast<const FilterActionWidget*>( widget )->action();
    if ( action )
      mActionList->append( action );
  }
  q->updateAddRemoveButton();
}


FilterActionWidgetLister:: FilterActionWidgetLister( QWidget *parent )
  : KWidgetLister( false, 1, FILTER_MAX_ACTIONS, parent ), d( new Private( this ) )
{
}

FilterActionWidgetLister::~FilterActionWidgetLister()
{
  delete d;
}

void FilterActionWidgetLister::setActionList( QList<FilterAction*> *list )
{
  Q_ASSERT( list );
  if ( d->mActionList && d->mActionList!= list )
    d->regenerateActionListFromWidgets();

  d->mActionList = list;

  static_cast<QWidget*>( parent() )->setEnabled( true );

  if ( !widgets().isEmpty() ) // move this below next 'if'?
    widgets().first()->blockSignals(true);

  if ( list->isEmpty() ) {
    slotClear();
    widgets().first()->blockSignals(false);
    return;
  }

  int superfluousItems = (int)d->mActionList->count() - widgetsMaximum();
  if ( superfluousItems > 0 ) {
    kDebug() << "FilterActionWidgetLister: Clipping action list to"
             << widgetsMaximum() << "items!";

    for ( ; superfluousItems ; superfluousItems-- )
      d->mActionList->removeLast();
  }

  // set the right number of widgets
  setNumberOfShownWidgetsTo( d->mActionList->count() );

  // load the actions into the widgets
  QList<QWidget*> widgetList = widgets();
  QList<FilterAction*>::const_iterator aEnd( d->mActionList->constEnd() );
  QList<QWidget*>::ConstIterator wIt = widgetList.constBegin();
  QList<QWidget*>::ConstIterator wEnd = widgetList.constEnd();
  for ( QList<FilterAction*>::const_iterator aIt = d->mActionList->constBegin(); ( aIt != aEnd && wIt != wEnd );++aIt, ++wIt ) {
    FilterActionWidget *w = qobject_cast<FilterActionWidget*>( *wIt );
    w->setAction( ( *aIt ) );
    connect( w, SIGNAL(filterModified()), this, SIGNAL(filterModified()), Qt::UniqueConnection );
    reconnectWidget( w );
  }
  widgets().first()->blockSignals(false);
  updateAddRemoveButton();

}

void FilterActionWidgetLister::slotAddWidget( QWidget* w)
{
  addWidgetAfterThisWidget( w );
  updateAddRemoveButton();
}

void FilterActionWidgetLister::slotRemoveWidget( QWidget* w)
{
  removeWidget( w );
  updateAddRemoveButton();
}

void FilterActionWidgetLister::updateAddRemoveButton()
{
  QList<QWidget*> widgetList = widgets();
  const int numberOfWidget( widgetList.count() );
  bool addButtonEnabled = false;
  bool removeButtonEnabled = false;
  if ( numberOfWidget <= widgetsMinimum() )
  {
    addButtonEnabled = true;
    removeButtonEnabled = false;
  }
  else if ( numberOfWidget >= widgetsMaximum() )
  {
    addButtonEnabled = false;
    removeButtonEnabled = true;
  }
  else
  {
    addButtonEnabled = true;
    removeButtonEnabled = true;
  }
  QList<QWidget*>::ConstIterator wIt = widgetList.constBegin();
  QList<QWidget*>::ConstIterator wEnd = widgetList.constEnd();
  for ( ; wIt != wEnd ;++wIt ) {
    FilterActionWidget *w = qobject_cast<FilterActionWidget*>( *wIt );
    w->updateAddRemoveButton( addButtonEnabled, removeButtonEnabled );
  }
}

void FilterActionWidgetLister::updateActionList()
{
  d->regenerateActionListFromWidgets();
}

void FilterActionWidgetLister::reset()
{
  if ( d->mActionList )
    d->regenerateActionListFromWidgets();

  d->mActionList = 0;
  slotClear();

  static_cast<QWidget*>( parent() )->setEnabled( false );
}

void FilterActionWidgetLister::reconnectWidget( FilterActionWidget *w )
{
  connect( w, SIGNAL(addWidget(QWidget*)), this, SLOT(slotAddWidget(QWidget*)), Qt::UniqueConnection );
  connect( w, SIGNAL(removeWidget(QWidget*)), this, SLOT(slotRemoveWidget(QWidget*)), Qt::UniqueConnection );
}

QWidget* FilterActionWidgetLister::createWidget( QWidget *parent )
{
  FilterActionWidget *w = new FilterActionWidget( parent );
  reconnectWidget( w );
  return w;
}

void FilterActionWidgetLister::clearWidget( QWidget *widget )
{
  if ( widget ) {
    FilterActionWidget *w = static_cast<FilterActionWidget*>( widget );
    w->setAction( 0 );
    w->disconnect( this );
    reconnectWidget( w ) ;
  }
}

#include "filteractionwidget.moc"
