/*
  This file is part of the kholidays library.

  Copyright 2010 John Layt <john@layt.net>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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 Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to the
  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "holidayregionselector.h"
#include "ui_holidayregionselector.h"

#include "holidayregion.h"

#include <KDebug>
#include <KLocale>
#include <KComboBox>

using namespace KHolidays;

class HolidayRegionSelector::Private
{
public:
  Private( HolidayRegionSelector *q_ptr )
  {
    q = q_ptr;
  }

  ~Private()
  {
  }

  // Reorder this to change column order
  enum Column {
    RegionColumn = 0,
    LanguageColumn,
    DescriptionColumn,
    SelectColumn,
    ComboColumn
  };

  void clearSelection();
  QTreeWidgetItem *findItem( const QString &holidayRegionCode );
  void initItem( QTreeWidgetItem *listItem, HolidayRegion *region );
  QString itemRegion( QTreeWidgetItem *item );
  QString itemLanguage( QTreeWidgetItem *item );
  SelectionStatus itemSelectionStatus( QTreeWidgetItem *item );
  void setItemStatus( QTreeWidgetItem *item, SelectionStatus status );
  KComboBox *itemCombo( QTreeWidgetItem *item );
  void setItemRegionUseFlags( QTreeWidgetItem *item, RegionUseFlags regionUseFlags );
  HolidayRegionSelector::RegionUseFlags itemRegionUseFlags( QTreeWidgetItem *item );

  QAbstractItemView::SelectionMode m_selectionMode;
  bool m_enableRegionUseFlags;
  QStringList m_languageFilter;
  QStringList m_holidayRegionCodes;
  Ui::holidayRegionSelector m_ui;
  HolidayRegionSelector *q;
};

void HolidayRegionSelector::Private::clearSelection()
{
  m_ui.regionTreeWidget->blockSignals( true );
  QTreeWidgetItemIterator it( m_ui.regionTreeWidget, QTreeWidgetItemIterator::Checked );
  while ( *it ) {
    (*it)->setCheckState( Private::SelectColumn, Qt::Unchecked );
    (*it)->setData( Private::SelectColumn, Qt::UserRole, RegionAvailable );
    itemCombo( (*it) )->setCurrentIndex( 0 );
    (*it)->setData( Private::ComboColumn, Qt::UserRole, NotUsed );
    ++it;
  }
  m_ui.regionTreeWidget->blockSignals( false );
}

QTreeWidgetItem *HolidayRegionSelector::Private::findItem( const QString &holidayRegionCode )
{
  QTreeWidgetItemIterator it( m_ui.regionTreeWidget );
  while ( *it ) {
    if ( (*it)->data( HolidayRegionSelector::Private::RegionColumn, Qt::UserRole ) == holidayRegionCode ) {
      return (*it);
    }
    ++it;
  }
  return 0;
}

void HolidayRegionSelector::Private::initItem( QTreeWidgetItem *listItem, HolidayRegion *region )
{
  m_ui.regionTreeWidget->blockSignals( true );
  QString languageName = KGlobal::locale()->languageCodeToName( region->languageCode() );
  listItem->setCheckState( Private::SelectColumn, Qt::Unchecked );
  QString text = i18n("<p>Select to use Holiday Region</p>");
  listItem->setToolTip( Private::SelectColumn, text );
  listItem->setToolTip( Private::ComboColumn, text );
  text = i18n("<p>Select to use Holiday Region</p>");
  listItem->setToolTip( Private::SelectColumn, text );
  listItem->setToolTip( Private::ComboColumn, text );
  listItem->setText( Private::RegionColumn, region->name() );
  QString toolTip = i18n("<p><b>Region:</b> %1<br/>"
                         "<b>Language:</b> %2<br/>"
                         "<b>Description:</b> %3</p>",
                         region->name(), languageName, region->description() );
  listItem->setToolTip( Private::RegionColumn, toolTip );
  listItem->setData( Private::RegionColumn, Qt::UserRole, region->regionCode() );
  listItem->setText( Private::LanguageColumn, languageName );
  listItem->setData( Private::LanguageColumn, Qt::UserRole, region->languageCode() );
  listItem->setText( Private::DescriptionColumn, region->description() );
  listItem->setToolTip( Private::DescriptionColumn, region->description() );
  KComboBox *combo = new KComboBox();
  combo->setAutoFillBackground( true );
  QString comboText = i18n("<p>You can choose to display the Holiday Region for information only, or to use the Holiday Region when displaying or calculating days off such as Public Holidays.  If you choose to use the Holiday Region for Days Off, then only those Holiday Events marked in the Holiday Region as Days Off will be used for non-work days, Holiday Events that are not marked in the Holiday Region as Days Off will continue to be work days.</p>");
  combo->setToolTip( comboText );
  comboText = i18nc("Combobox label, Holiday Region not used", "Not Used");
  combo->addItem( comboText, QVariant( NotUsed ) );
  comboText = i18nc("Combobox label, use Holiday Region for information only", "Information");
  combo->addItem( comboText, QVariant( UseInformationOnly ) );
  comboText = i18nc("Combobox label, use Holiday Region for days off", "Days Off");
  combo->addItem( comboText, QVariant( UseDaysOff ) );
  combo->setCurrentIndex( 0 );
  listItem->setData( Private::ComboColumn, Qt::UserRole, NotUsed );
  m_ui.regionTreeWidget->setItemWidget( listItem, ComboColumn, combo );
  connect( combo, SIGNAL( currentIndexChanged( int ) ),
           q,     SLOT(   itemChanged( int ) ) );
  m_ui.regionTreeWidget->blockSignals( false );
}

QString HolidayRegionSelector::Private::itemRegion( QTreeWidgetItem *item )
{
  return item->data( Private::RegionColumn, Qt::UserRole ).toString();
}

QString HolidayRegionSelector::Private::itemLanguage( QTreeWidgetItem *item )
{
  return item->data( Private::LanguageColumn, Qt::UserRole ).toString();
}

HolidayRegionSelector::SelectionStatus HolidayRegionSelector::Private::itemSelectionStatus( QTreeWidgetItem *item )
{
  return (HolidayRegionSelector::SelectionStatus)
         item->data( Private::SelectColumn, Qt::UserRole ).toInt();
}

void HolidayRegionSelector::Private::setItemRegionUseFlags( QTreeWidgetItem *item, RegionUseFlags regionUseFlags )
{
  // Just do simple flags for now, cheat on the index
  item->setData( ComboColumn, Qt::UserRole, QVariant( regionUseFlags ) );
  if ( regionUseFlags & UseDaysOff ){
    setItemStatus( item, RegionSelected );
    itemCombo( item )->setCurrentIndex( 2 );
  } else if ( regionUseFlags & UseInformationOnly ) {
    setItemStatus( item, RegionSelected );
    itemCombo( item )->setCurrentIndex( 1 );
  } else { // NotUsed
    setItemStatus( item, RegionAvailable );
    itemCombo( item )->setCurrentIndex( 0 );
  }
}

HolidayRegionSelector::RegionUseFlags HolidayRegionSelector::Private::itemRegionUseFlags( QTreeWidgetItem *item )
{
  return (RegionUseFlags) item->data( ComboColumn, Qt::UserRole ).toInt();
}

KComboBox *HolidayRegionSelector::Private::itemCombo( QTreeWidgetItem *item )
{
  return static_cast<KComboBox*> (m_ui.regionTreeWidget->itemWidget( item, ComboColumn ) );
}

void HolidayRegionSelector::Private::setItemStatus( QTreeWidgetItem *item, SelectionStatus status )
{
  if ( m_selectionMode == QAbstractItemView::SingleSelection &&
       status == RegionSelected ) {
    clearSelection();
  }

  Qt::CheckState selectStatus = Qt::Unchecked;
  bool hidden = false;
  bool disabled = false;

  switch ( status ) {
  case RegionHidden:
    hidden = true;
    disabled = true;
    break;
  case RegionDisabled:
    disabled = true;
    break;
  case RegionAvailable:
    break;
  case RegionSelected:
  {
    if ( m_selectionMode != QAbstractItemView::NoSelection ) {
      selectStatus = Qt::Checked;
    }
    break;
  }
  default:
    break;
  }

  m_ui.regionTreeWidget->blockSignals( true );
  item->setData( Private::SelectColumn, Qt::UserRole, status );
  item->setCheckState( Private::SelectColumn, selectStatus );
  item->setHidden( hidden );
  item->setDisabled( disabled );
  m_ui.regionTreeWidget->blockSignals( false );
}

HolidayRegionSelector::HolidayRegionSelector( QWidget *parent )
                     : QWidget( parent ),
                       d( new Private( this ) )
{
  d->m_ui.setupUi( this );

  // Setup the columns
  d->m_ui.regionTreeWidget->setColumnCount( 5 );
  QTreeWidgetItem *headerItem = d->m_ui.regionTreeWidget->headerItem();
  QString header = i18nc( "Header for Select column", "Select" );
  QString text = i18n("<p>This column selects to use the Holiday Region</p>");
  headerItem->setText( Private::SelectColumn, header );
  headerItem->setToolTip( Private::SelectColumn, text );
  headerItem->setWhatsThis( Private::SelectColumn, text );
  headerItem->setText( Private::ComboColumn, header );
  headerItem->setToolTip( Private::ComboColumn, text );
  headerItem->setWhatsThis( Private::ComboColumn, text );
  header = i18nc( "Header for Holiday Region column", "Region" );
  text = i18n("<p>This column displays the name of the Holiday Region</p>");
  headerItem->setText( Private::RegionColumn, header );
  headerItem->setToolTip( Private::RegionColumn, text );
  headerItem->setWhatsThis( Private::RegionColumn, text );
  header = i18nc( "Header for Language column", "Language" );
  text = i18n("<p>This column displays the language of the Holiday Region</p>");
  headerItem->setText( Private::LanguageColumn, header );
  headerItem->setToolTip( Private::LanguageColumn, text );
  headerItem->setWhatsThis( Private::LanguageColumn, text );
  header = i18nc( "Header for Description column", "Description" );
  text = i18n("<p>This column displays the description of the Holiday Region</p>");
  headerItem->setText( Private::DescriptionColumn, header );
  headerItem->setToolTip( Private::DescriptionColumn, text );
  headerItem->setWhatsThis( Private::DescriptionColumn, text );

  d->m_ui.regionTreeWidget->setSelectionMode( QAbstractItemView::NoSelection );
  d->m_ui.regionTreeWidget->setItemsExpandable( false );
  d->m_ui.regionTreeWidget->setUniformRowHeights( true );
  d->m_ui.regionTreeWidget->setAllColumnsShowFocus( true );
  connect( d->m_ui.regionTreeWidget, SIGNAL( itemChanged( QTreeWidgetItem*, int ) ),
           this,                     SLOT(   itemChanged( QTreeWidgetItem*, int ) ) );

  QMap<QString, QStringList> regionCodeMap;
  QMap<QString, HolidayRegion*> regionMap;
  foreach ( const QString &regionCode, HolidayRegion::regionCodes() ) {
    regionMap[regionCode] = new HolidayRegion( regionCode );
    QString country = regionMap[regionCode]->countryCode().split('-').at(0);
    regionCodeMap[country].append( regionCode );
  }

  QMapIterator<QString, QStringList> it( regionCodeMap );
  while ( it.hasNext() ) {
    it.next();
    // if only 1 region for country and that region is the country, then only show the country line
    // Otherwise show a non-selectable country header and list all regions below it
    QTreeWidgetItem *rootItem = new QTreeWidgetItem( d->m_ui.regionTreeWidget );
    QString country = it.key();
    if ( it.value().count() == 1 && regionMap[it.value().at( 0 )]->countryCode() == country ) {
      d->initItem( rootItem, regionMap[ it.value().at( 0 ) ] );
    } else {
      rootItem->setText( Private::RegionColumn,
                         KGlobal::locale()->countryCodeToName( country ) );
      d->m_ui.regionTreeWidget->setFirstItemColumnSpanned ( rootItem, true );
      foreach ( const QString &regionCode, it.value() ) {
        QTreeWidgetItem *childItem = new QTreeWidgetItem( rootItem );
        d->initItem( childItem, regionMap[ regionCode ] );
      }
    }
  }

  QMapIterator<QString, HolidayRegion*> i( regionMap );
  while ( i.hasNext() ) {
    i.next();
    delete i.value();
  }

  d->m_ui.regionTreeWidget->expandAll();
  d->m_ui.regionTreeWidget->sortItems( Private::RegionColumn, Qt::AscendingOrder );
  d->m_ui.regionTreeWidget->resizeColumnToContents( Private::SelectColumn );
  d->m_ui.regionTreeWidget->resizeColumnToContents( Private::ComboColumn );
  d->m_ui.regionTreeWidget->resizeColumnToContents( Private::RegionColumn );
  d->m_ui.regionTreeWidget->resizeColumnToContents( Private::LanguageColumn );

  // Setup search widget
  d->m_ui.searchLineWidget->searchLine()->addTreeWidget( d->m_ui.regionTreeWidget );

  // Default to MultiSelection mode with Region Use Flags
  setSelectionMode( QAbstractItemView::MultiSelection );
  setRegionUseFlagsEnabled( true );
}

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

QStringList HolidayRegionSelector::holidayRegions() const
{
  return d->m_holidayRegionCodes;
}

void HolidayRegionSelector::setSelectionMode( QAbstractItemView::SelectionMode selectionMode )
{
  d->m_selectionMode = selectionMode;

  if ( selectionMode == QAbstractItemView::NoSelection ) {
    setRegionUseFlagsEnabled( false );
  }
}

QAbstractItemView::SelectionMode HolidayRegionSelector::selectionMode() const
{
  return d->m_selectionMode;
}

void HolidayRegionSelector::setRegionUseFlagsEnabled( bool enableRegionUseFlags )
{
  d->m_enableRegionUseFlags = enableRegionUseFlags;
  d->m_ui.regionTreeWidget->setColumnHidden( Private::SelectColumn, enableRegionUseFlags );
  d->m_ui.regionTreeWidget->setColumnHidden( Private::ComboColumn, !enableRegionUseFlags );
}

bool HolidayRegionSelector::regionUseFlagsEnabled() const
{
  return d->m_enableRegionUseFlags;
}

void HolidayRegionSelector::setSelectionStatus( const QString &holidayRegionCode,
                                                HolidayRegionSelector::SelectionStatus status )
{
  QTreeWidgetItem *item = d->findItem( holidayRegionCode );
  if ( item ) {
    d->setItemStatus( item, status );
  }
}

HolidayRegionSelector::SelectionStatus HolidayRegionSelector::selectionStatus( const QString &holidayRegionCode ) const
{
  QTreeWidgetItem *item = d->findItem( holidayRegionCode );
  if ( item ) {
    return d->itemSelectionStatus( item );
  }
  return HolidayRegionSelector::RegionHidden;
}

QHash<QString, HolidayRegionSelector::SelectionStatus> HolidayRegionSelector::selectionStatus() const
{
  QHash<QString, HolidayRegionSelector::SelectionStatus> selection;
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  while ( *it ) {
    selection.insert( d->itemRegion( (*it) ), d->itemSelectionStatus( (*it) ) );
    ++it;
  }
  return selection;
}

QStringList HolidayRegionSelector::selection( HolidayRegionSelector::SelectionStatus selectionStatus ) const
{
  if ( selectionMode() == QAbstractItemView::NoSelection ) {
    return QStringList();
  }

  QStringList selection;
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  while ( *it ) {
    if ( d->itemSelectionStatus( (*it) ) == selectionStatus ) {
      selection.append( d->itemRegion( (*it) ) );
    }
    ++it;
  }
  return selection;
}

QStringList HolidayRegionSelector::selection( HolidayRegionSelector::RegionUseFlags regionUseFlags ) const
{
  if ( selectionMode() == QAbstractItemView::NoSelection ) {
    return QStringList();
  }

  QStringList selection;
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  while ( *it ) {
    if ( d->itemRegionUseFlags( (*it) ) & regionUseFlags ) {
      selection.append( d->itemRegion( (*it) ) );
    }
    ++it;
  }
  return selection;
}

void HolidayRegionSelector::setRegionUseFlags( const QString &holidayRegionCode,
                                               HolidayRegionSelector::RegionUseFlags regionUseFlags )
{
  QTreeWidgetItem *item = d->findItem( holidayRegionCode );
  if ( item ) {
    d->setItemRegionUseFlags( item, regionUseFlags );
  }
}

HolidayRegionSelector::RegionUseFlags HolidayRegionSelector::regionUseFlags( const QString &holidayRegionCode ) const
{
  QTreeWidgetItem *item = d->findItem( holidayRegionCode );
  if ( item ) {
    return d->itemRegionUseFlags( item );
  }
  return HolidayRegionSelector::NotUsed;
}

QHash<QString, HolidayRegionSelector::RegionUseFlags> HolidayRegionSelector::regionUseFlags() const
{
  QHash<QString, HolidayRegionSelector::RegionUseFlags> selection;
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  while ( *it ) {
    selection.insert( d->itemRegion( (*it) ), d->itemRegionUseFlags( (*it) ) );
    ++it;
  }
  return selection;
}

void HolidayRegionSelector::itemChanged( QTreeWidgetItem *item, int column )
{
  if ( column == Private::SelectColumn ) {
    if ( item->checkState( Private::SelectColumn ) == Qt::Unchecked ) {
      d->setItemStatus( item, RegionAvailable );
    } else {
      d->setItemStatus( item, RegionSelected );
    }
  }
  emit selectionChanged();
}

// The slot for the combo box when changed
void HolidayRegionSelector::itemChanged( int index )
{
  KComboBox *combo = static_cast<KComboBox*> (sender());
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  bool found = false;
  while ( *it && !found ) {
    if ( d->itemCombo( (*it) ) == combo ) {
      (*it)->setData( Private::ComboColumn, Qt::UserRole, d->itemCombo( (*it) )->itemData( index ) );
      if ( d->itemRegionUseFlags( (*it) ) == NotUsed )  {
        d->setItemStatus( (*it), RegionAvailable );
      } else {
        d->setItemStatus( (*it), RegionSelected );
      }
      found = true;
    }
    ++it;
  }
}

void HolidayRegionSelector::clearSelection()
{
  d->clearSelection();
}

void HolidayRegionSelector::setLanguageFilter( const QStringList &languages )
{
  // First reset to visible those languages previously hidden
  QTreeWidgetItemIterator it( d->m_ui.regionTreeWidget );
  while ( *it ) {
    if ( !d->m_languageFilter.contains( (*it)->data( Private::LanguageColumn, Qt::UserRole ).toString() ) ) {
      d->setItemStatus( (*it), RegionAvailable );
    }
    ++it;
  }
  // Now hide those languages not in the new list
  d->m_languageFilter = languages;
  QTreeWidgetItemIterator it2( d->m_ui.regionTreeWidget );
  while ( *it2 ) {
    QString language = (*it2)->data( Private::LanguageColumn, Qt::UserRole ).toString();
    if ( d->m_languageFilter.contains( language ) ) {
      // Make sure the parent is always visible, otherwise the child is not visible
      if ( (*it2)->parent() && (*it2)->parent() != d->m_ui.regionTreeWidget->invisibleRootItem() ) {
        d->setItemStatus( (*it2)->parent(), RegionAvailable );
      }
    } else {
      d->setItemStatus( (*it2), RegionHidden );
    }
    ++it2;
  }
}

QStringList HolidayRegionSelector::languageFilter() const
{
  return d->m_languageFilter;
}

void HolidayRegionSelector::setSearchHidden( bool hideSearch )
{
  d->m_ui.searchLineWidget->setHidden( hideSearch );
}

bool HolidayRegionSelector::searchHidden() const
{
  return d->m_ui.searchLineWidget->isHidden();
}

void HolidayRegionSelector::setLanguageHidden( bool hideLanguage )
{
  d->m_ui.regionTreeWidget->setColumnHidden( Private::LanguageColumn, hideLanguage );
}

bool HolidayRegionSelector::languageHidden() const
{
  return d->m_ui.regionTreeWidget->isColumnHidden( Private::LanguageColumn );
}

void HolidayRegionSelector::setDescriptionHidden( bool hideDescription )
{
  d->m_ui.regionTreeWidget->setColumnHidden( Private::DescriptionColumn, hideDescription );
}

bool HolidayRegionSelector::descriptionHidden() const
{
  return d->m_ui.regionTreeWidget->isColumnHidden( Private::DescriptionColumn );
}

#include "holidayregionselector.moc"
