/******************************************************************************
 *
 *  Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.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 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 "core/manager.h"

#include "core/aggregation.h"
#include "core/theme.h"
#include "core/view.h"
#include "core/widgetbase.h"
#include "core/storagemodelbase.h"
#include "core/model.h"
#include "core/model_p.h"
#include "core/settings.h"

#include "utils/configureaggregationsdialog.h"
#include "utils/configureaggregationsdialog_p.h"
#include "utils/configurethemesdialog.h"
#include "utils/configurethemesdialog_p.h"

#include "messagecore/globalsettings.h"

#include "messagelistutil.h"

#include <QPixmap>

#include <kmime/kmime_dateformatter.h> // kdepimlibs

#include <KConfig>
#include <KDebug>
#include <KIconLoader>
#include <KGlobalSettings>
#include <KApplication>
#include <KLocale>
#include <KStandardDirs>

using namespace MessageList::Core;

Manager * Manager::mInstance = 0;

Manager::Manager()
  : QObject()
{
  mInstance = this;

  mDateFormatter = new KMime::DateFormatter();

  mPixmapMessageNew = new QPixmap( SmallIcon( QLatin1String( "mail-unread-new" ) ) );
  mPixmapMessageUnread = new QPixmap( SmallIcon( QLatin1String( "mail-unread" ) ) );
  mPixmapMessageRead = new QPixmap( SmallIcon( QLatin1String( "mail-read" ) ) );
  mPixmapMessageDeleted = new QPixmap( SmallIcon( QLatin1String( "mail-deleted" ) ) );
  mPixmapMessageReplied = new QPixmap( SmallIcon( QLatin1String( "mail-replied" ) ) );
  mPixmapMessageRepliedAndForwarded = new QPixmap( SmallIcon( QLatin1String( "mail-forwarded-replied" ) ) );
  mPixmapMessageQueued = new QPixmap( SmallIcon( QLatin1String( "mail-queued" ) ) ); // mail-queue ?
  mPixmapMessageActionItem = new QPixmap( SmallIcon( QLatin1String( "mail-task" ) ) );
  mPixmapMessageSent = new QPixmap( SmallIcon( QLatin1String( "mail-sent" ) ) );
  mPixmapMessageForwarded = new QPixmap( SmallIcon( QLatin1String( "mail-forwarded" ) ) );
  mPixmapMessageImportant = new QPixmap( SmallIcon( QLatin1String( "emblem-important" ) ) ); // "flag"
  mPixmapMessageWatched = new QPixmap( KStandardDirs::locate( "data", QLatin1String( "messagelist/pics/mail-thread-watch.png" ) ) );
  mPixmapMessageIgnored = new QPixmap( KStandardDirs::locate( "data", QLatin1String( "messagelist/pics/mail-thread-ignored.png" ) ) );
  mPixmapMessageSpam = new QPixmap( SmallIcon( QLatin1String( "mail-mark-junk" ) ) );
  mPixmapMessageHam = new QPixmap( SmallIcon( QLatin1String( "mail-mark-notjunk" ) ) );
  mPixmapMessageFullySigned = new QPixmap( SmallIcon( QLatin1String( "mail-signed-verified" ) ) );
  mPixmapMessagePartiallySigned = new QPixmap( SmallIcon( QLatin1String( "mail-signed-part" ) ) );
  mPixmapMessageUndefinedSigned = new QPixmap( SmallIcon( QLatin1String( "mail-signed" ) ) );
  mPixmapMessageNotSigned = new QPixmap( SmallIcon( QLatin1String( "text-plain" ) ) );
  mPixmapMessageFullyEncrypted = new QPixmap( SmallIcon( QLatin1String( "mail-encrypted-full" ) ) );
  mPixmapMessagePartiallyEncrypted = new QPixmap( SmallIcon( QLatin1String( "mail-encrypted-part" ) ) );
  mPixmapMessageUndefinedEncrypted = new QPixmap( SmallIcon( QLatin1String( "mail-encrypted" ) ) );
  mPixmapMessageNotEncrypted = new QPixmap( SmallIcon( QLatin1String( "text-plain" ) ) );
  mPixmapMessageAttachment = new QPixmap( SmallIcon( QLatin1String( "mail-attachment" ) ) );
  mPixmapMessageAnnotation = new QPixmap( SmallIcon( QLatin1String( "view-pim-notes" ) ) );
  mPixmapMessageInvitation = new QPixmap( SmallIcon( QLatin1String( "mail-invitation" ) ) );
  //mPixmapShowMore = new QPixmap( SmallIcon( "list-add.png" ) );
  //mPixmapShowLess = new QPixmap( SmallIcon( "list-remove.png" ) );
  if ( KApplication::isRightToLeft() )
    mPixmapShowMore = new QPixmap( SmallIcon( QLatin1String( "arrow-left" ) ) );
  else
    mPixmapShowMore = new QPixmap( SmallIcon( QLatin1String( "arrow-right" ) ) );
  mPixmapShowLess = new QPixmap( SmallIcon( QLatin1String( "arrow-down" ) ) );
  mPixmapVerticalLine = new QPixmap( KStandardDirs::locate( "data", QLatin1String( "messagelist/pics/mail-vertical-separator-line.png" ) ) );
  mPixmapHorizontalSpacer = new QPixmap( KStandardDirs::locate( "data", QLatin1String( "messagelist/pics/mail-horizontal-space.png" ) ) );

  mCachedLocalizedUnknownText = i18nc( "Unknown date", "Unknown" ) ;

  loadConfiguration();
  connect( Settings::self(), SIGNAL(configChanged()),
           this, SLOT(reloadGlobalConfiguration()) );
}

Manager::~Manager()
{
  saveConfiguration();
  removeAllAggregations();
  removeAllThemes();

  delete mPixmapMessageNew;
  delete mPixmapMessageUnread;
  delete mPixmapMessageRead;
  delete mPixmapMessageDeleted;
  delete mPixmapMessageReplied;
  delete mPixmapMessageRepliedAndForwarded;
  delete mPixmapMessageQueued;
  delete mPixmapMessageActionItem;
  delete mPixmapMessageSent;
  delete mPixmapMessageForwarded;
  delete mPixmapMessageImportant; // "flag"
  delete mPixmapMessageWatched;
  delete mPixmapMessageIgnored;
  delete mPixmapMessageSpam;
  delete mPixmapMessageHam;
  delete mPixmapMessageFullySigned;
  delete mPixmapMessagePartiallySigned;
  delete mPixmapMessageUndefinedSigned;
  delete mPixmapMessageNotSigned;
  delete mPixmapMessageFullyEncrypted;
  delete mPixmapMessagePartiallyEncrypted;
  delete mPixmapMessageUndefinedEncrypted;
  delete mPixmapMessageNotEncrypted;
  delete mPixmapMessageAttachment;
  delete mPixmapMessageAnnotation;
  delete mPixmapMessageInvitation;
  delete mPixmapShowMore;
  delete mPixmapShowLess;
  delete mPixmapVerticalLine;
  delete mPixmapHorizontalSpacer;

  delete mDateFormatter;

  mInstance = 0;
}

void Manager::registerWidget( Widget *pWidget )
{
  if ( !mInstance )
    mInstance = new Manager();

  mInstance->mWidgetList.append( pWidget );
}

void Manager::unregisterWidget( Widget *pWidget )
{
  if ( !mInstance )
  {
    qWarning("ERROR: MessageList::Manager::unregisterWidget() called when Manager::mInstance is 0");
    return;
  }

  mInstance->mWidgetList.removeAll( pWidget );

  if ( mInstance->mWidgetList.isEmpty() )
  {
    delete mInstance;
    mInstance = 0;
  }
}

unsigned long Manager::preSelectedMessageForStorageModel( const StorageModel *storageModel )
{
  KConfigGroup conf( Settings::self()->config(),
                     MessageList::Util::storageModelSelectedMessageGroup() );

  // QVariant supports unsigned int OR unsigned long long int, NOT unsigned long int... doh...
  qulonglong defValue = 0;

  return conf.readEntry( MessageList::Util::messageUniqueIdConfigName().arg( storageModel->id() ), defValue );
}

void Manager::savePreSelectedMessageForStorageModelId( const QString &storageModelId, unsigned long uniqueIdOfMessage )
{
  KConfigGroup conf( Settings::self()->config(),
                     MessageList::Util::storageModelSelectedMessageGroup() );


  if ( uniqueIdOfMessage )
  {
    // QVariant supports unsigned int OR unsigned long long int, NOT unsigned long int... doh...
    qulonglong val = uniqueIdOfMessage;

    conf.writeEntry( MessageList::Util::messageUniqueIdConfigName().arg( storageModelId ), val );
  } else
    conf.deleteEntry( MessageList::Util::messageUniqueIdConfigName().arg( storageModelId ) );
}

const Aggregation * Manager::aggregation( const QString &id )
{
  Aggregation * opt = mAggregations.value( id );
  if ( opt )
    return opt;

  return defaultAggregation();
}

const Aggregation * Manager::defaultAggregation()
{
  KConfigGroup conf( Settings::self()->config(),
                     MessageList::Util::storageModelAggregationsGroup() );

  const QString aggregationId = conf.readEntry( QLatin1String( "DefaultSet" ), "" );

  Aggregation * opt = 0;

  if ( !aggregationId.isEmpty() )
    opt = mAggregations.value( aggregationId );

  if ( opt )
    return opt;

  // try just the first one
  QHash< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin();
  if ( it != mAggregations.constEnd() )
    return *it;

  // aargh
  createDefaultAggregations();

  return *( mAggregations.constBegin() );
}

void Manager::saveAggregationForStorageModel( const Akonadi::Collection &col, const QString &id, bool storageUsesPrivateAggregation )
{
  if ( !col.isValid() )
    return;
  saveAggregationForStorageModel( QString::number( col.id() ), id, storageUsesPrivateAggregation );
}

void Manager::saveAggregationForStorageModel( const StorageModel *storageModel, const QString &id, bool storageUsesPrivateAggregation )
{
  saveAggregationForStorageModel( storageModel->id(), id, storageUsesPrivateAggregation );
}

void Manager::saveAggregationForStorageModel( const QString &modelId, const QString &id, bool storageUsesPrivateAggregation )
{
  KConfigGroup conf( Settings::self()->config(),
                     MessageList::Util::storageModelAggregationsGroup() );

  if ( storageUsesPrivateAggregation )
    conf.writeEntry( MessageList::Util::setForStorageModelConfigName().arg( modelId ), id );
  else
    conf.deleteEntry( MessageList::Util::setForStorageModelConfigName().arg( modelId ) );

  if ( !storageUsesPrivateAggregation )
    conf.writeEntry( QLatin1String( "DefaultSet" ), id );
}

const Aggregation * Manager::aggregationForStorageModel( const Akonadi::Collection &col, bool *storageUsesPrivateAggregation )
{
  Q_ASSERT( storageUsesPrivateAggregation );

  *storageUsesPrivateAggregation = false; // this is by default

  if ( !col.isValid() )
    return defaultAggregation();
  return Manager::aggregationForStorageModel( QString::number( col.id() ), storageUsesPrivateAggregation );
}


const Aggregation * Manager::aggregationForStorageModel( const StorageModel *storageModel, bool *storageUsesPrivateAggregation )
{
  Q_ASSERT( storageUsesPrivateAggregation );

  *storageUsesPrivateAggregation = false; // this is by default

  if ( !storageModel )
    return defaultAggregation();
  return Manager::aggregationForStorageModel( storageModel->id(), storageUsesPrivateAggregation );
}

const Aggregation * Manager::aggregationForStorageModel( const QString &storageId, bool *storageUsesPrivateAggregation )
{
  KConfigGroup conf( Settings::self()->config(),
                     MessageList::Util::storageModelAggregationsGroup() );

  const QString aggregationId = conf.readEntry( MessageList::Util::setForStorageModelConfigName().arg( storageId ), "" );

  Aggregation * opt = 0;

  if ( !aggregationId.isEmpty() )
  {
    // a private aggregation was stored
    opt = mAggregations.value( aggregationId );
    *storageUsesPrivateAggregation = ( opt != 0 );
  }

  if ( opt )
    return opt;

  // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset...
  //        We could even try to guess if the storageModel is a mailing list

  return defaultAggregation();
}

void Manager::addAggregation( Aggregation *set )
{
  Aggregation * old = mAggregations.value( set->id() );
  delete old;
  mAggregations.insert( set->id(), set );
}

void Manager::createDefaultAggregations()
{
  addAggregation(
      new Aggregation(
          i18n( "Current Activity, Threaded" ),
          i18n( "This view uses smart date range groups. " \
                "Messages are threaded. " \
                "So for example, in \"Today\" you will find all the messages arrived today " \
                "and all the threads that have been active today."
            ),
          Aggregation::GroupByDateRange,
          Aggregation::ExpandRecentGroups,
          Aggregation::PerfectReferencesAndSubject,
          Aggregation::MostRecentMessage,
          Aggregation::ExpandThreadsWithUnreadOrImportantMessages,
          Aggregation::FavorInteractivity
       )
    );

  addAggregation(
      new Aggregation(
          i18n( "Current Activity, Flat" ),
          i18n( "This view uses smart date range groups. " \
                "Messages are not threaded. " \
                "So for example, in \"Today\" you will simply find all the messages arrived today."
            ),
          Aggregation::GroupByDateRange,
          Aggregation::ExpandRecentGroups,
          Aggregation::NoThreading,
          Aggregation::MostRecentMessage,
          Aggregation::NeverExpandThreads,
          Aggregation::FavorInteractivity
       )
    );

  addAggregation(
      new Aggregation(
          i18n( "Activity by Date, Threaded" ),
          i18n( "This view uses day-by-day groups. " \
                "Messages are threaded. " \
                "So for example, in \"Today\" you will find all the messages arrived today " \
                "and all the threads that have been active today."
            ),
          Aggregation::GroupByDate,
          Aggregation::ExpandRecentGroups,
          Aggregation::PerfectReferencesAndSubject,
          Aggregation::MostRecentMessage,
          Aggregation::ExpandThreadsWithUnreadOrImportantMessages,
          Aggregation::FavorInteractivity
       )
    );

  addAggregation(
      new Aggregation(
          i18n( "Activity by Date, Flat" ),
          i18n( "This view uses day-by-day groups. " \
                "Messages are not threaded. " \
                "So for example, in \"Today\" you will simply find all the messages arrived today."
            ),
          Aggregation::GroupByDate,
          Aggregation::ExpandRecentGroups,
          Aggregation::NoThreading,
          Aggregation::MostRecentMessage,
          Aggregation::NeverExpandThreads,
          Aggregation::FavorInteractivity
       )
    );

  addAggregation(
      new Aggregation(
          i18n( "Standard Mailing List" ),
          i18n( "This is a plain and old mailing list view: no groups and heavy threading." ),
          Aggregation::NoGrouping,
          Aggregation::NeverExpandGroups,
          Aggregation::PerfectReferencesAndSubject,
          Aggregation::TopmostMessage,
          Aggregation::ExpandThreadsWithUnreadOrImportantMessages,
          Aggregation::FavorInteractivity
       )
    );

  addAggregation(
      new Aggregation(
          i18n( "Flat Date View" ),
          i18n( "This is a plain and old list of messages sorted by date: no groups and no threading." \
            ),
          Aggregation::NoGrouping,
          Aggregation::NeverExpandGroups,
          Aggregation::NoThreading,
          Aggregation::TopmostMessage,
          Aggregation::NeverExpandThreads,
          Aggregation::FavorInteractivity
        )
    );

  addAggregation(
      new Aggregation(
          i18n( "Senders/Receivers, Flat" ),
          i18n( "This view groups the messages by senders or receivers (depending on the folder " \
                "type). " \
                "Messages are not threaded."
            ),
          Aggregation::GroupBySenderOrReceiver,
          Aggregation::NeverExpandGroups,
          Aggregation::NoThreading,
          Aggregation::TopmostMessage,
          Aggregation::NeverExpandThreads,
          Aggregation::FavorSpeed
        )
    );

  addAggregation(
      new Aggregation(
          i18n( "Thread Starters" ),
          i18n( "This view groups the messages in threads and then groups the threads by the starting user." ),
          Aggregation::GroupBySenderOrReceiver,
          Aggregation::NeverExpandGroups,
          Aggregation::PerfectReferencesAndSubject,
          Aggregation::TopmostMessage,
          Aggregation::NeverExpandThreads,
          Aggregation::FavorSpeed
        )
    );

/*
  FIX THIS
  addAggregation(
      new Aggregation(
          i18n( "Recent Thread Starters" ),
          i18n( "This view groups the messages in threads and then groups the threads by the starting user. " \
                "Groups are sorted by the date of the first thread start. "
            ),
          Aggregation::GroupBySenderOrReceiver,
          Aggregation::SortGroupsByDateTimeOfMostRecent,
          Aggregation::Descending,
          Aggregation::PerfectReferencesAndSubject,
          Aggregation::TopmostMessage,
          Aggregation::SortMessagesByDateTime,
          Aggregation::Descending
        )
    );
*/
}

void Manager::removeAllAggregations()
{
  QHash< QString, Aggregation * >::ConstIterator end( mAggregations.constEnd() );
  for( QHash< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin(); it != end; ++it )
    delete ( *it );

  mAggregations.clear();
}

void Manager::aggregationsConfigurationCompleted()
{
  if ( mAggregations.isEmpty() )
    createDefaultAggregations(); // panic

  saveConfiguration(); // just to be sure :)

  // notify all the widgets that they should reload the option set combos
  emit aggregationsChanged();
}

const SortOrder Manager::sortOrderForStorageModel( const StorageModel *storageModel, bool *storageUsesPrivateSortOrder )
{
  Q_ASSERT( storageUsesPrivateSortOrder );

  *storageUsesPrivateSortOrder = false; // this is by default

  if ( !storageModel )
    return SortOrder();

  KConfigGroup conf( Settings::self()->config(), MessageList::Util::storageModelSortOrderGroup() );
  SortOrder ret;
  ret.readConfig( conf, storageModel->id(), storageUsesPrivateSortOrder );
  return ret;
}

void Manager::saveSortOrderForStorageModel( const StorageModel *storageModel,
                                            const SortOrder& order, bool storageUsesPrivateSortOrder )
{
  KConfigGroup conf( Settings::self()->config(), MessageList::Util::storageModelSortOrderGroup() );
  order.writeConfig( conf, storageModel->id(), storageUsesPrivateSortOrder );
}

const Theme * Manager::theme( const QString &id )
{
  Theme * opt = mThemes.value( id );
  if ( opt )
    return opt;

  return defaultTheme();
}

const Theme * Manager::defaultTheme()
{
  KConfigGroup conf( Settings::self()->config(), MessageList::Util::storageModelThemesGroup() );

  const QString themeId = conf.readEntry( QLatin1String( "DefaultSet" ), "" );

  Theme * opt = 0;

  if ( !themeId.isEmpty() )
    opt = mThemes.value( themeId );

  if ( opt )
    return opt;

  // try just the first one
  QHash< QString, Theme * >::ConstIterator it = mThemes.constBegin();
  if ( it != mThemes.constEnd() )
    return *it;

  // aargh
  createDefaultThemes();

  it = mThemes.constBegin();

  Q_ASSERT( it != mThemes.constEnd() );

  return *it;
}

void Manager::saveThemeForStorageModel( int index, const QString &id, bool storageUsesPrivateTheme )
{
  saveThemeForStorageModel( QString::number( index ), id, storageUsesPrivateTheme );
}

void Manager::saveThemeForStorageModel( const StorageModel *storageModel, const QString &id, bool storageUsesPrivateTheme )
{
  saveThemeForStorageModel( storageModel->id(), id, storageUsesPrivateTheme );
}

void Manager::saveThemeForStorageModel( const QString &storageModelIndex, const QString &id, bool storageUsesPrivateTheme )
{
  KConfigGroup conf( Settings::self()->config(), MessageList::Util::storageModelThemesGroup() );

  if ( storageUsesPrivateTheme )
    conf.writeEntry( MessageList::Util::setForStorageModelConfigName().arg( storageModelIndex ), id );
  else
    conf.deleteEntry( MessageList::Util::setForStorageModelConfigName().arg( storageModelIndex ) );

  if ( !storageUsesPrivateTheme )
    conf.writeEntry( QLatin1String( "DefaultSet" ), id );
}


const Theme * Manager::themeForStorageModel( const Akonadi::Collection & col,  bool * storageUsesPrivateTheme )
{
  Q_ASSERT( storageUsesPrivateTheme );

  *storageUsesPrivateTheme = false; // this is by default

  if ( !col.isValid() )
    return defaultTheme();
  return Manager::themeForStorageModel( QString::number( col.id() ), storageUsesPrivateTheme );

}

const Theme * Manager::themeForStorageModel( const StorageModel *storageModel, bool *storageUsesPrivateTheme )
{


  Q_ASSERT( storageUsesPrivateTheme );

  *storageUsesPrivateTheme = false; // this is by default

  if ( !storageModel )
    return defaultTheme();
  return Manager::themeForStorageModel( storageModel->id(), storageUsesPrivateTheme );
}

const Theme * Manager::themeForStorageModel( const QString &id, bool *storageUsesPrivateTheme )
{
  KConfigGroup conf( Settings::self()->config(), MessageList::Util::storageModelThemesGroup() );
  const QString themeId = conf.readEntry( MessageList::Util::setForStorageModelConfigName().arg( id ), "" );

  Theme * opt = 0;

  if ( !themeId.isEmpty() )
  {
    // a private theme was stored
    opt = mThemes.value( themeId );
    *storageUsesPrivateTheme = (opt != 0);
  }

  if ( opt )
    return opt;

  // FIXME: If the storageModel is a mailing list, maybe suggest a mailing-list like preset...
  //        We could even try to guess if the storageModel is a mailing list

  // FIXME: Prefer right-to-left themes when application layout is RTL.

  return defaultTheme();
}

void Manager::addTheme( Theme *set )
{
  Theme * old = mThemes.value( set->id() );
  delete old;
  mThemes.insert( set->id(), set );
}

static Theme::Column * add_theme_simple_text_column( Theme * s, const QString &name, Theme::ContentItem::Type type, bool visibleByDefault, SortOrder::MessageSorting messageSorting, bool alignRight, bool addGroupHeaderItem )
{
  Theme::Column * c = new Theme::Column();
  c->setLabel( name );
  c->setVisibleByDefault( visibleByDefault );
  c->setMessageSorting( messageSorting );

  Theme::Row * r = new Theme::Row();

  Theme::ContentItem * i = new Theme::ContentItem( type );
  i->setFont( KGlobalSettings::generalFont() );

  if ( alignRight )
    r->addRightItem( i );
  else
    r->addLeftItem( i );

  c->addMessageRow( r );

  if ( addGroupHeaderItem )
  {
    Theme::Row * r = new Theme::Row();

    Theme::ContentItem * i = new Theme::ContentItem( type );
    i->setFont( KGlobalSettings::generalFont() );

    if ( alignRight )
      r->addRightItem( i );
    else
      r->addLeftItem( i );

    c->addGroupHeaderRow( r );
  }


  s->addColumn( c );

  return c;
}

static Theme::Column * add_theme_simple_icon_column( Theme * s, const QString &name, const QString &pixmapName, Theme::ContentItem::Type type, bool visibleByDefault, SortOrder::MessageSorting messageSorting )
{
  Theme::Column * c = new Theme::Column();
  c->setLabel( name );
  c->setPixmapName( pixmapName );
  c->setVisibleByDefault( visibleByDefault );
  c->setMessageSorting( messageSorting );

  Theme::Row * r = new Theme::Row();

  Theme::ContentItem * i = new Theme::ContentItem( type );
  i->setSoftenByBlendingWhenDisabled( true );

  r->addLeftItem( i );

  c->addMessageRow( r );

  s->addColumn( c );

  return c;
}


void Manager::createDefaultThemes()
{
  Theme * s;
  Theme::Column * c;
  Theme::Row * r;
  Theme::ContentItem * i;

  // The "Classic" backward compatible theme

  s = new Theme(
      i18nc( "Default theme name", "Classic" ),
      i18n( "A simple, backward compatible, single row theme" ), true /*readOnly*/
    );

    c = new Theme::Column();
    c->setLabel( i18nc( "@title:column Subject of messages", "Subject" ) );
    c->setMessageSorting( SortOrder::SortMessagesBySubject );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::ExpandedStateIcon );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::GroupHeaderLabel );
        QFont bigFont = KGlobalSettings::generalFont();
        bigFont.setBold( true );
        i->setFont( bigFont );
        i->setUseCustomFont( true );
      r->addLeftItem( i );
    c->addGroupHeaderRow( r );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::CombinedReadRepliedStateIcon );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::AttachmentStateIcon );
        i->setHideWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::AnnotationIcon );
        i->setHideWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::InvitationIcon );
        i->setHideWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::SignatureStateIcon );
        i->setHideWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::EncryptionStateIcon );
        i->setHideWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::Subject );
      r->addLeftItem( i );
    c->addMessageRow( r );

  s->addColumn( c );

  c = add_theme_simple_text_column( s, i18n( "Sender/Receiver" ), Theme::ContentItem::SenderOrReceiver, true, SortOrder::SortMessagesBySenderOrReceiver, false, false);
  c->setIsSenderOrReceiver( true );
  add_theme_simple_text_column( s, i18nc( "Sender of a message", "Sender" ), Theme::ContentItem::Sender, false, SortOrder::SortMessagesBySender, false, false );
  add_theme_simple_text_column( s, i18nc( "Receiver of a message", "Receiver" ), Theme::ContentItem::Receiver, false, SortOrder::SortMessagesByReceiver, false, false );
  add_theme_simple_text_column( s, i18nc( "Date of a message", "Date" ), Theme::ContentItem::Date, true, SortOrder::SortMessagesByDateTime, false, false );
  add_theme_simple_text_column( s, i18n( "Most Recent Date" ), Theme::ContentItem::MostRecentDate, false, SortOrder::SortMessagesByDateTimeOfMostRecent, false, true );
  add_theme_simple_text_column( s, i18nc( "Size of a message", "Size" ), Theme::ContentItem::Size, false, SortOrder::SortMessagesBySize, false, false );
  add_theme_simple_icon_column( s, i18nc( "Attachement indication", "Attachment" ), QLatin1String( "mail-attachment" ), Theme::ContentItem::AttachmentStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Read/Unread" ), QLatin1String( "mail-unread-new" ), Theme::ContentItem::ReadStateIcon, false, SortOrder::SortMessagesByUnreadStatus );
  add_theme_simple_icon_column( s, i18n( "Replied" ), QLatin1String( "mail-replied" ), Theme::ContentItem::RepliedStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18nc( "Message importance indication", "Important" ), QLatin1String( "emblem-important" ), Theme::ContentItem::ImportantStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Action Item" ), QLatin1String( "mail-task" ), Theme::ContentItem::ActionItemStateIcon, false, SortOrder::SortMessagesByActionItemStatus );
  add_theme_simple_icon_column( s, i18n( "Spam/Ham" ), QLatin1String( "mail-mark-junk" ), Theme::ContentItem::SpamHamStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Watched/Ignored" ), QLatin1String( "mail-thread-watch" ), Theme::ContentItem::WatchedIgnoredStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Encryption" ), QLatin1String( "mail-encrypted-full" ), Theme::ContentItem::EncryptionStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Signature" ), QLatin1String( "mail-signed-verified" ), Theme::ContentItem::SignatureStateIcon, false, SortOrder::NoMessageSorting );
  add_theme_simple_icon_column( s, i18n( "Tag List" ), QLatin1String( "feed-subscribe" ), Theme::ContentItem::TagList, false, SortOrder::NoMessageSorting );

  s->resetColumnState(); // so it's initially set from defaults

  addTheme( s );

  // The Fancy theme

  s = new Theme(
      i18n( "Fancy" ),
      i18n( "A fancy multiline and multi item theme" ), true /*readOnly*/
    );

    c = new Theme::Column();
    c->setLabel( i18n( "Message" ) );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::ExpandedStateIcon );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::GroupHeaderLabel );
        QFont aBigFont = KGlobalSettings::generalFont();
        aBigFont.setBold( true );
        i->setFont( aBigFont );
        i->setUseCustomFont( true );
      r->addLeftItem( i );
    c->addGroupHeaderRow( r );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::Subject );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::ReadStateIcon );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::RepliedStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::AttachmentStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::AnnotationIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::InvitationIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::EncryptionStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::SignatureStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::TagList );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
    c->addMessageRow( r );

  Theme::Row * firstFancyRow = r; // save it so we can continue adding stuff below (after cloning the theme)

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::SenderOrReceiver );
        i->setSoftenByBlending( true );
        QFont aItalicFont = KGlobalSettings::generalFont();
        aItalicFont.setItalic( true );
        i->setFont( aItalicFont );
        i->setUseCustomFont( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::Date );
        i->setSoftenByBlending( true );
        i->setFont( aItalicFont );
        i->setUseCustomFont( true );
      r->addRightItem( i );
    c->addMessageRow( r );

  s->addColumn( c );

  // clone the "Fancy theme" here so we'll use it as starting point for the "Fancy with clickable status"
  Theme * fancyWithClickableStatus = new Theme( *s );
  fancyWithClickableStatus->detach();
  fancyWithClickableStatus->generateUniqueId();

  // and continue the "Fancy" specific settings
  r = firstFancyRow;

        i = new Theme::ContentItem( Theme::ContentItem::ActionItemStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::ImportantStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::SpamHamStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::WatchedIgnoredStateIcon );
        i->setHideWhenDisabled( true );
      r->addRightItem( i );

  s->setViewHeaderPolicy( Theme::NeverShowHeader );

  s->resetColumnState(); // so it's initially set from defaults

  addTheme( s );


  // The "Fancy with Clickable Status" theme

  s = fancyWithClickableStatus;

  s->setName( i18n( "Fancy with Clickable Status" ) );
  s->setDescription( i18n( "A fancy multiline and multi item theme with a clickable status column" ) );
  s->setReadOnly( true );

    c = new Theme::Column();
    c->setLabel( i18n( "Status" ) );
    c->setVisibleByDefault( true );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::ActionItemStateIcon );
        i->setSoftenByBlendingWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::ImportantStateIcon );
        i->setSoftenByBlendingWhenDisabled( true );
      r->addLeftItem( i );
    c->addMessageRow( r );

      r = new Theme::Row();
        i = new Theme::ContentItem( Theme::ContentItem::SpamHamStateIcon );
        i->setSoftenByBlendingWhenDisabled( true );
      r->addLeftItem( i );
        i = new Theme::ContentItem( Theme::ContentItem::WatchedIgnoredStateIcon );
        i->setSoftenByBlendingWhenDisabled( true );
      r->addLeftItem( i );
    c->addMessageRow( r );

  s->addColumn( c );

  s->resetColumnState(); // so it's initially set from defaults

  addTheme( s );
}

void Manager::removeAllThemes()
{
  QHash< QString, Theme * >::ConstIterator end( mThemes.constEnd() );
  for( QHash< QString, Theme * >::ConstIterator it = mThemes.constBegin(); it != end; ++it )
    delete ( *it );

  mThemes.clear();
}

void Manager::themesConfigurationCompleted()
{
  if ( mThemes.isEmpty() )
    createDefaultThemes(); // panic

  saveConfiguration(); // just to be sure :)

  // notify all the widgets that they should reload the option set combos
  emit themesChanged();
}

void Manager::reloadAllWidgets()
{

  QList< Widget * >::ConstIterator end( mWidgetList.constEnd() );
  for( QList< Widget * >::ConstIterator it = mWidgetList.constBegin(); it != end; ++it )
  {
    if ( ( *it )->view() )
      ( *it )->view()->reload();
  }
}


void Manager::reloadGlobalConfiguration()
{
  // This is called when configuration changes (probably edited by the options dialog)
  const int oldDateFormat = (int)mDateFormatter->format();
  const QString oldDateCustomFormat = mDateFormatter->customFormat();

  loadGlobalConfiguration();

  if (
       ( oldDateFormat != (int)mDateFormatter->format() ) ||
       ( oldDateCustomFormat != mDateFormatter->customFormat() )
     )
    reloadAllWidgets();
}


void Manager::loadGlobalConfiguration()
{
  // Load the date format
  const KMime::DateFormatter::FormatType type = static_cast<KMime::DateFormatter::FormatType>(
       MessageCore::GlobalSettings::self()->dateFormat() );
  mDateFormatter->setCustomFormat( MessageCore::GlobalSettings::self()->customDateFormat() );
  mDateFormatter->setFormat( type );
}

void Manager::loadConfiguration()
{
  loadGlobalConfiguration();

  {
    // load Aggregations

    KConfigGroup conf( Settings::self()->config(), "MessageListView::Aggregations" );

    mAggregations.clear();

    const int cnt = conf.readEntry( "Count", 0 );

    int idx = 0;
    while ( idx < cnt )
    {
      const QString data = conf.readEntry( QString::fromLatin1( "Set%1" ).arg( idx ), QString() );
      if ( !data.isEmpty() )
      {
        Aggregation * set = new Aggregation();
        if ( set->loadFromString( data ) )
        {
          if ( Aggregation * old = mAggregations.value( set->id() ) )
            delete old;
          mAggregations.insert( set->id(), set );
        } else {
          delete set; // b0rken
        }
      }
      idx++;
    }

    if ( mAggregations.isEmpty() )
    {
      // don't allow zero configuration, create some presets
      createDefaultAggregations();
    }
  }

  {
    // load Themes

    KConfigGroup conf( Settings::self()->config(), "MessageListView::Themes" );

    mThemes.clear();

    const int cnt = conf.readEntry( "Count", 0 );

    int idx = 0;
    while ( idx < cnt )
    {
      const QString data = conf.readEntry( QString::fromLatin1( "Set%1" ).arg( idx ), QString() );
      if ( !data.isEmpty() )
      {
        Theme * set = new Theme();
        if ( set->loadFromString( data ) )
        {
          if ( Theme * old = mThemes.value( set->id() ) )
            delete old;
          mThemes.insert( set->id(), set );
        } else {
          kWarning() << "Saved theme loading failed";
          delete set; // b0rken
        }
      }
      ++idx;
    }

    if ( mThemes.isEmpty() )
    {
      // don't allow zero configuration, create some presets
      createDefaultThemes();
    }
  }

}

void Manager::saveGlobalConfiguration()
{
  Settings::self()->writeConfig();
}

void Manager::saveConfiguration()
{
  saveGlobalConfiguration();

  {
    // store aggregations

    KConfigGroup conf( Settings::self()->config(), "MessageListView::Aggregations" );
    //conf.clear();

    conf.writeEntry( "Count", mAggregations.count() );

    int idx = 0;
    QHash< QString, Aggregation * >::ConstIterator end( mAggregations.end() );
    for( QHash< QString, Aggregation * >::ConstIterator it = mAggregations.constBegin(); it != end; ++it )
    {
      conf.writeEntry( QString::fromLatin1( "Set%1" ).arg( idx ), ( *it )->saveToString() );
      ++idx;
    }
  }

  {
    // store themes

    KConfigGroup conf( Settings::self()->config(), "MessageListView::Themes" );
    //conf.clear();

    conf.writeEntry( "Count", mThemes.count() );

    int idx = 0;
    QHash< QString, Theme * >::ConstIterator end( mThemes.constEnd() );
    for( QHash< QString, Theme * >::ConstIterator it = mThemes.constBegin(); it != end; ++it )
    {
      conf.writeEntry( QString::fromLatin1( "Set%1" ).arg( idx ), ( *it )->saveToString() );
      ++idx;
    }
  }

  Settings::self()->config()->sync();
}


#include "manager.moc"
