/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2003-2006 by the KFTPGrabber developers
 * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net>
 *
 * 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
 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
 * NON-INFRINGEMENT.  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 Steet, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 *
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * file(s) with this exception, you may extend this exception to your
 * version of the file(s), but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your
 * version.  If you delete this exception statement from all source
 * files in the program, then also delete it here.
 */
#include "filtereditor.h"
#include "listview.h"
#include "misc/filterwidgethandler.h"

#include <qlayout.h>
#include <qhbox.h>
#include <qtooltip.h>
#include <qheader.h>
#include <qbuttongroup.h>

#include <kiconloader.h>
#include <klocale.h>
#include <kdialog.h>
#include <kinputdialog.h>

using namespace KFTPCore::Filter;

namespace KFTPWidgets {

FilterEditor::FilterEditor(QWidget *parent)
  : QWidget(parent),
    m_rule(0)
{
  QHBoxLayout *mainLayout = new QHBoxLayout(this, 0, KDialog::spacingHint());
  
  m_listView = new FilterListView(this);
  mainLayout->addWidget(m_listView, 1);
  
  QVBoxLayout *rightLayout = new QVBoxLayout(mainLayout);
  mainLayout->setStretchFactor(rightLayout, KDialog::spacingHint());
  
  m_enabledCheck = new QCheckBox(i18n("Filter &enabled"), this);
  rightLayout->addWidget(m_enabledCheck);
  
  m_conditionsList = new FilterConditionsList(this);
  rightLayout->addWidget(m_conditionsList, 0, Qt::AlignTop);
  
  m_actionsList = new FilterActionsList(this);
  rightLayout->addWidget(m_actionsList, 0, Qt::AlignTop);
  
  rightLayout->addStretch(1);
  
  // Connect some signals
  connect(m_enabledCheck, SIGNAL(clicked()), this, SLOT(slotEnabledChanged()));
  
  connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), this, SLOT(slotRuleChanged(KFTPCore::Filter::Rule*)));
  connect(m_listView, SIGNAL(ruleRemoved()), this, SLOT(slotRuleRemoved()));
  
  connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), m_conditionsList, SLOT(loadRule(KFTPCore::Filter::Rule*)));
  connect(m_listView, SIGNAL(ruleRemoved()), m_conditionsList, SLOT(reset()));
  
  connect(m_listView, SIGNAL(ruleChanged(KFTPCore::Filter::Rule*)), m_actionsList, SLOT(loadRule(KFTPCore::Filter::Rule*)));
  connect(m_listView, SIGNAL(ruleRemoved()), m_actionsList, SLOT(reset())); 
}

void FilterEditor::slotRuleChanged(KFTPCore::Filter::Rule *rule)
{
  m_enabledCheck->setEnabled(true);
  m_enabledCheck->setChecked(rule->isEnabled());
  
  m_rule = rule;
}

void FilterEditor::slotRuleRemoved()
{
  m_enabledCheck->setChecked(false);
  m_enabledCheck->setEnabled(false);
}

void FilterEditor::slotEnabledChanged()
{
  if (m_rule)
    m_rule->setEnabled(m_enabledCheck->isChecked());
}

void FilterEditor::reset()
{
  m_enabledCheck->setChecked(false);
  m_enabledCheck->setEnabled(false);
  
  m_conditionsList->reset();
  m_actionsList->reset();
  m_listView->reset();
}

FilterListItem::FilterListItem(ListView *parent, KFTPCore::Filter::Rule *rule)
  : QListViewItem(parent),
    m_rule(rule)
{
  setText(0, rule->name());
}

FilterListView::FilterListView(QWidget *parent)
  : QGroupBox(1, Horizontal, i18n("Filters"), parent)
{
  m_listView = new ListView(this);
  m_listView->setSelectionMode(QListView::Single);
  m_listView->setSorting(-1);
  m_listView->header()->hide();
  m_listView->setMinimumWidth(150);
  m_listView->setEmptyListText(i18n("No filters."));
  m_listView->addColumn("");
  m_listView->setFullWidth(true);
  
  QHBox *hb = new QHBox(this);
  hb->setSpacing(4);
  
  // Up/down buttons
  m_buttonUp = new KPushButton(QString::null, hb);
  m_buttonUp->setAutoRepeat(true);
  m_buttonUp->setIconSet(BarIconSet("up", KIcon::SizeSmall));
  m_buttonUp->setMinimumSize(m_buttonUp->sizeHint() * 1.2);
  
  m_buttonDown = new KPushButton(QString::null, hb);
  m_buttonDown->setAutoRepeat(true);
  m_buttonDown->setIconSet(BarIconSet("down", KIcon::SizeSmall));
  m_buttonDown->setMinimumSize(m_buttonDown->sizeHint() * 1.2);
  
  QToolTip::add(m_buttonUp, i18n("Up"));
  QToolTip::add(m_buttonDown, i18n("Down"));
  
  // New, copy, delete buttons
  hb = new QHBox(this);
  hb->setSpacing(4);
  
  m_buttonNew = new QPushButton(QString::null, hb);
  m_buttonNew->setPixmap(BarIcon("filenew", KIcon::SizeSmall));
  m_buttonNew->setMinimumSize(m_buttonNew->sizeHint() * 1.2);
  
  m_buttonCopy = new QPushButton(QString::null, hb);
  m_buttonCopy->setPixmap(BarIcon("editcopy", KIcon::SizeSmall));
  m_buttonCopy->setMinimumSize(m_buttonCopy->sizeHint() * 1.2);
  
  m_buttonDelete = new QPushButton(QString::null, hb);
  m_buttonDelete->setPixmap(BarIcon("editdelete", KIcon::SizeSmall));
  m_buttonDelete->setMinimumSize(m_buttonDelete->sizeHint() * 1.2);
  
  m_buttonRename = new QPushButton(i18n("Rename..."), hb);
  
  QToolTip::add(m_buttonNew, i18n("New"));
  QToolTip::add(m_buttonCopy, i18n("Copy"));
  QToolTip::add(m_buttonDelete, i18n("Delete"));
  
  // Connect the signals
  connect(m_buttonNew, SIGNAL(clicked()), this, SLOT(slotNewRule()));
  connect(m_buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteRule()));
  connect(m_buttonRename, SIGNAL(clicked()), this, SLOT(slotRenameRule()));
  connect(m_buttonCopy, SIGNAL(clicked()), this, SLOT(slotCopyRule()));
  
  connect(m_buttonUp, SIGNAL(clicked()), this, SLOT(slotUp()));
  connect(m_buttonDown, SIGNAL(clicked()), this, SLOT(slotDown()));
  
  connect(m_listView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotSelectionChanged(QListViewItem*)));
  
  m_buttonUp->setEnabled(false);
  m_buttonDown->setEnabled(false);
  m_buttonRename->setEnabled(false);
  
  // Reset the view, loading all the existing rules
  reset();
}

void FilterListView::reset()
{
  m_listView->clear();
  
  // Load all existing rules
  Filters *filters = Filters::self();
  Filters::ConstIterator le = filters->end();
  
  for (Filters::ConstIterator i = filters->begin(); i != le; ++i) {
    FilterListItem *item = new FilterListItem(m_listView, (*i));
    item->moveItem(m_listView->lastItem());
  }
    
  // Select the first rule
  m_listView->setSelected(m_listView->firstChild(), true);
}

void FilterListView::slotSelectionChanged(QListViewItem *item)
{
  FilterListItem *selected = static_cast<FilterListItem*>(item);
  
  m_buttonUp->setEnabled(item->itemAbove());
  m_buttonDown->setEnabled(item->nextSibling());
  m_buttonRename->setEnabled(true);
  
  // Signal the rule change
  emit ruleChanged(selected->rule());
}

void FilterListView::slotNewRule()
{
  Rule *rule = new Rule(i18n("Unnamed Rule"));
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  FilterListItem *item = new FilterListItem(m_listView, rule);
  
  if (selected) {
    Filters::self()->insert(Filters::self()->findRef(selected->rule()) + 1, rule);
    item->moveItem(selected);
  } else {
    Filters::self()->append(rule);
  }
  
  m_listView->setSelected(item, true);
}

void FilterListView::slotDeleteRule()
{
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  
  if (selected) {
    Rule *rule = selected->rule();
    delete selected;
    
    emit ruleRemoved();
    
    Filters::self()->removeRef(rule);
    m_listView->setSelected(m_listView->lastItem(), true);
  }
  
  if (!m_listView->selectedItem())
    m_buttonRename->setEnabled(false);
}

void FilterListView::slotRenameRule()
{
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  
  if (selected) {
    QString name = KInputDialog::getText(i18n("Rename Rule"), i18n("Rename rule '%1' to:").arg(selected->rule()->name()), selected->rule()->name());
    
    if (name.stripWhiteSpace().isEmpty())
      name = i18n("Unnamed Rule");
    
    selected->rule()->setName(name);
    selected->setText(0, name);
  }
}

void FilterListView::slotCopyRule()
{
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  
  if (selected) {
    Rule *rule = new Rule(selected->rule());
    FilterListItem *item = new FilterListItem(m_listView, rule);
    
    Filters::self()->insert(Filters::self()->findRef(selected->rule()) + 1, rule);
    item->moveItem(selected);
    m_listView->setSelected(item, true);
  }
}

void FilterListView::slotUp()
{
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  QListViewItem *tmp = selected->itemAbove();
  if (!tmp)
    return;
  
  FilterListItem *previous = static_cast<FilterListItem*>(tmp->itemAbove());
  
  if (selected) {
    Rule *rule = Filters::self()->take(Filters::self()->findRef(selected->rule()));
    
    if (previous) {
      Filters::self()->insert(Filters::self()->findRef(previous->rule()) + 1, rule);
      selected->moveItem(previous);
    } else {
      Filters::self()->insert(0, rule);
      m_listView->takeItem(selected);
      m_listView->insertItem(selected);
      m_listView->setSelected(selected, true);
    }
    
    m_buttonUp->setEnabled(selected->itemAbove());
    m_buttonDown->setEnabled(selected->nextSibling());
  }
}

void FilterListView::slotDown()
{
  FilterListItem *selected = static_cast<FilterListItem*>(m_listView->selectedItem());
  FilterListItem *next = static_cast<FilterListItem*>(selected->nextSibling());
  
  if (selected && next) {
    Rule *rule = Filters::self()->take(Filters::self()->findRef(selected->rule()));
    Filters::self()->insert(Filters::self()->findRef(next->rule()) + 1, rule);
    selected->moveItem(next);
    
    m_buttonUp->setEnabled(selected->itemAbove());
    m_buttonDown->setEnabled(selected->nextSibling());
  }
}

FilterConditionsList::FilterConditionsList(QWidget *parent)
  : QGroupBox(1, Horizontal, i18n("Conditions"), parent)
{
  setEnabled(false);
  
  m_buttonAll = new QRadioButton(i18n("Match a&ll of the following"), this);
  m_buttonAny = new QRadioButton(i18n("Match an&y of the following"), this);
  
  m_buttonAll->setChecked(true);
  m_buttonAny->setChecked(false);
  
  QButtonGroup *bg = new QButtonGroup(this);
  bg->hide();
  bg->insert(m_buttonAll, (int) ConditionChain::All);
  bg->insert(m_buttonAny, (int) ConditionChain::Any);
  
  // Connect some signals
  connect(bg, SIGNAL(clicked(int)), this, SLOT(slotMatchTypeChanged(int)));
  
  m_lister = new FilterConditionWidgetLister(this);
}

void FilterConditionsList::reset()
{
  m_lister->clear();
  setEnabled(false);
}

void FilterConditionsList::loadRule(Rule *rule)
{
  m_rule = rule;
  
  switch (rule->conditions()->type()) {
    case ConditionChain::All: m_buttonAll->setChecked(true); break;
    case ConditionChain::Any: m_buttonAny->setChecked(true); break;
  }
  
  m_lister->loadConditions(rule);
  setEnabled(true);
}

void FilterConditionsList::slotMatchTypeChanged(int type)
{
  if (m_rule)
    const_cast<ConditionChain*>(m_rule->conditions())->setType((ConditionChain::Type) type);
}

FilterConditionWidgetLister::FilterConditionWidgetLister(QWidget *parent)
  : WidgetLister(parent, 0, 7),
    m_rule(0)
{
  setMinimumWidth(400);
}

void FilterConditionWidgetLister::loadConditions(KFTPCore::Filter::Rule *rule)
{
  const ConditionChain *conditions = rule->conditions();
  
  // Clear the current list
  setNumberShown(QMAX(conditions->count(), 0));
  
  ConditionChain::ConstIterator le = conditions->end();
  QPtrList<QWidget>::Iterator wi = m_widgetList.begin();
  for (ConditionChain::ConstIterator i = conditions->begin(); i != le; ++i, ++wi)
    static_cast<FilterConditionWidget*>((*wi))->setCondition((*i));
  
  m_rule = rule;
}

void FilterConditionWidgetLister::slotMore()
{
  WidgetLister::slotMore();
  
  // Actually add the condition and update the latest widget
  Condition *condition = new Condition(Filename, Condition::Contains, QVariant(""));
  
  const_cast<ConditionChain*>(m_rule->conditions())->append(condition);
  static_cast<FilterConditionWidget*>(m_widgetList.last())->setCondition(condition);
}

void FilterConditionWidgetLister::slotFewer()
{
  // Actually remove the condition
  Condition *condition = static_cast<FilterConditionWidget*>(m_widgetList.last())->condition();
  const_cast<ConditionChain*>(m_rule->conditions())->remove(condition);
  
  WidgetLister::slotFewer();
}

void FilterConditionWidgetLister::slotClear()
{
  if (m_rule)
    const_cast<ConditionChain*>(m_rule->conditions())->clear();
  
  WidgetLister::slotClear();
}

QWidget *FilterConditionWidgetLister::createWidget(QWidget *parent)
{
  return new FilterConditionWidget(parent);
}

FilterConditionWidget::FilterConditionWidget(QWidget *parent)
  : QWidget(parent),
    m_condition(0)
{
  QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
  
  m_fieldCombo = new QComboBox(this);
  m_fieldCombo->insertStringList(Filters::self()->getFieldNames());
  layout->addWidget(m_fieldCombo);
  
  m_typeStack = new QWidgetStack(this);
  m_typeStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
  layout->addWidget(m_typeStack);
  
  m_valueStack = new QWidgetStack(this);
  m_valueStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
  layout->addWidget(m_valueStack);
  layout->setStretchFactor(m_valueStack, 10);
  
  // Initialize widgets
  WidgetHandlerManager::self()->createConditionWidgets(m_typeStack, m_valueStack, this);
  
  // Connect signals
  connect(m_fieldCombo, SIGNAL(activated(int)), this, SLOT(slotFieldChanged(int)));
  
  setFocusProxy(m_fieldCombo);
}

void FilterConditionWidget::setCondition(const Condition *condition)
{
  m_condition = const_cast<Condition*>(condition);
  
  m_fieldCombo->setCurrentItem((int) condition->field());
  WidgetHandlerManager::self()->setCondition(m_typeStack, m_valueStack, condition);
}

void FilterConditionWidget::slotFieldChanged(int field)
{
  WidgetHandlerManager::self()->update((Field) field, m_typeStack, m_valueStack);
  
  if (m_condition) {
    // Update the current condition
    m_condition->setField((Field) field);
    slotTypeChanged();
  }
}

void FilterConditionWidget::slotTypeChanged()
{
  if (m_condition) {
    // Update the current condition
    m_condition->setType(WidgetHandlerManager::self()->getConditionType(m_condition->field(), m_typeStack));
    slotValueChanged();
  }
}

void FilterConditionWidget::slotValueChanged()
{
  if (m_condition) {
    // Update the current condition
    m_condition->setValue(WidgetHandlerManager::self()->getConditionValue(m_condition->field(), m_valueStack));
  }
}

FilterActionsList::FilterActionsList(QWidget *parent)
  : QGroupBox(1, Horizontal, i18n("Actions"), parent)
{
  setEnabled(false);
  
  m_lister = new FilterActionWidgetLister(this);
}

void FilterActionsList::reset()
{
  m_lister->clear();
  setEnabled(false);
}

void FilterActionsList::loadRule(Rule *rule)
{
  m_rule = rule;
  
  m_lister->loadActions(rule);
  setEnabled(true);
}

FilterActionWidgetLister::FilterActionWidgetLister(QWidget *parent)
  : WidgetLister(parent, 0, 7),
    m_rule(0)
{
  setMinimumWidth(400);
}

void FilterActionWidgetLister::loadActions(KFTPCore::Filter::Rule *rule)
{
  const ActionChain *actions = rule->actions();
  
  // Clear the current list
  setNumberShown(QMAX(actions->count(), 0));
  
  ActionChain::ConstIterator le = actions->end();
  QPtrList<QWidget>::Iterator wi = m_widgetList.begin();
  for (ActionChain::ConstIterator i = actions->begin(); i != le; ++i, ++wi)
    static_cast<FilterActionWidget*>((*wi))->setAction((*i));
  
  m_rule = rule;
}

void FilterActionWidgetLister::slotMore()
{
  WidgetLister::slotMore();
  
  // Actually add the action and update the latest widget
  Action *action = new Action(Action::None, QVariant());
  
  const_cast<ActionChain*>(m_rule->actions())->append(action);
  static_cast<FilterActionWidget*>(m_widgetList.last())->setAction(action);
}

void FilterActionWidgetLister::slotFewer()
{
  // Actually remove the action
  Action *action = static_cast<FilterActionWidget*>(m_widgetList.last())->action();
  const_cast<ActionChain*>(m_rule->actions())->remove(action);
  
  WidgetLister::slotFewer();
}

void FilterActionWidgetLister::slotClear()
{
  if (m_rule)
    const_cast<ActionChain*>(m_rule->actions())->clear();
  
  WidgetLister::slotClear();
}

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

FilterActionWidget::FilterActionWidget(QWidget *parent)
  : QWidget(parent),
    m_action(0)
{
  QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
  
  m_actionCombo = new QComboBox(this);
  m_actionCombo->insertStringList(Filters::self()->getActionNames());
  layout->addWidget(m_actionCombo);
  
  m_valueStack = new QWidgetStack(this);
  m_valueStack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
  layout->addWidget(m_valueStack);
  layout->setStretchFactor(m_valueStack, 10);
  
  // Initialize widgets
  WidgetHandlerManager::self()->createActionWidgets(m_valueStack, this);
  
  // Connect signals
  connect(m_actionCombo, SIGNAL(activated(int)), this, SLOT(slotActionChanged(int)));
  connect(m_actionCombo, SIGNAL(activated(int)), m_valueStack, SLOT(raiseWidget(int)));
  
  setFocusProxy(m_actionCombo);
}

void FilterActionWidget::setAction(const Action *action)
{
  m_action = const_cast<Action*>(action);
  
  m_actionCombo->setCurrentItem((int) action->type());
  WidgetHandlerManager::self()->setAction(m_valueStack, action);
}

void FilterActionWidget::slotActionChanged(int field)
{
  if (m_action) {
    m_action->setType((Action::Type) field);
    slotValueChanged();
  }
}

void FilterActionWidget::slotValueChanged()
{
  if (m_action)
    m_action->setValue(WidgetHandlerManager::self()->getActionValue(m_valueStack));
}

}

#include "filtereditor.moc"

