/*
    Copyright (c) 2007 Bruno Virlet <bruno.virlet@gmail.com>
    Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>


    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 "entitymimetypefiltermodel.h"

#include "entitytreemodel.h"
#include "mimetypechecker.h"

#include <kdebug.h>
#include <kmimetype.h>

#include <QtCore/QString>
#include <QtCore/QStringList>

using namespace Akonadi;

namespace Akonadi {
/**
 * @internal
 */
class EntityMimeTypeFilterModelPrivate
{
  public:
    EntityMimeTypeFilterModelPrivate( EntityMimeTypeFilterModel *parent )
      : q_ptr( parent ),
        m_headerGroup( EntityTreeModel::EntityTreeHeaders )
    {
    }

    Q_DECLARE_PUBLIC(EntityMimeTypeFilterModel)
    EntityMimeTypeFilterModel *q_ptr;

    QStringList includedMimeTypes;
    QStringList excludedMimeTypes;

    QPersistentModelIndex m_rootIndex;

    EntityTreeModel::HeaderGroup m_headerGroup;
};

}

EntityMimeTypeFilterModel::EntityMimeTypeFilterModel( QObject *parent )
  : QSortFilterProxyModel( parent ),
    d_ptr( new EntityMimeTypeFilterModelPrivate( this ) )
{
}

EntityMimeTypeFilterModel::~EntityMimeTypeFilterModel()
{
  delete d_ptr;
}

void EntityMimeTypeFilterModel::addMimeTypeInclusionFilters(const QStringList &typeList)
{
  Q_D(EntityMimeTypeFilterModel);
  d->includedMimeTypes << typeList;
  invalidateFilter();
}

void EntityMimeTypeFilterModel::addMimeTypeExclusionFilters(const QStringList &typeList)
{
  Q_D(EntityMimeTypeFilterModel);
  d->excludedMimeTypes << typeList;
  invalidateFilter();
}

void EntityMimeTypeFilterModel::addMimeTypeInclusionFilter(const QString &type)
{
  Q_D(EntityMimeTypeFilterModel);
  d->includedMimeTypes << type;
  invalidateFilter();
}

void EntityMimeTypeFilterModel::addMimeTypeExclusionFilter(const QString &type)
{
  Q_D(EntityMimeTypeFilterModel);
  d->excludedMimeTypes << type;
  invalidateFilter();
}

bool EntityMimeTypeFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent) const
{
  Q_D(const EntityMimeTypeFilterModel);

  const QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent);

  const QString rowMimetype = idx.data( EntityTreeModel::MimeTypeRole ).toString();

  if ( d->excludedMimeTypes.contains( rowMimetype ) )
    return false;
  if ( d->includedMimeTypes.isEmpty() || d->includedMimeTypes.contains( rowMimetype ) )
    return true;

  return false;
}

QStringList EntityMimeTypeFilterModel::mimeTypeInclusionFilters() const
{
  Q_D(const EntityMimeTypeFilterModel);
  return d->includedMimeTypes;
}

QStringList EntityMimeTypeFilterModel::mimeTypeExclusionFilters() const
{
  Q_D(const EntityMimeTypeFilterModel);
  return d->excludedMimeTypes;
}

void EntityMimeTypeFilterModel::clearFilters()
{
  Q_D(EntityMimeTypeFilterModel);
  d->includedMimeTypes.clear();
  d->excludedMimeTypes.clear();
  invalidateFilter();
}

void EntityMimeTypeFilterModel::setHeaderGroup(EntityTreeModel::HeaderGroup headerGroup)
{
  Q_D(EntityMimeTypeFilterModel);
  d->m_headerGroup = headerGroup;
}

QVariant EntityMimeTypeFilterModel::headerData(int section, Qt::Orientation orientation, int role ) const
{
  if (!sourceModel())
    return QVariant();

  Q_D(const EntityMimeTypeFilterModel);
  role += (EntityTreeModel::TerminalUserRole * d->m_headerGroup);
  return sourceModel()->headerData(section, orientation, role);
}

QModelIndexList EntityMimeTypeFilterModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const
{
  if (!sourceModel())
    return QModelIndexList();

  if (EntityTreeModel::AmazingCompletionRole != role)
  {
    if (role < Qt::UserRole)
      return QSortFilterProxyModel::match(start, role, value, hits, flags);

    QModelIndexList list;
    QModelIndex proxyIndex;
    foreach(const QModelIndex idx, sourceModel()->match(mapToSource(start), role, value, hits, flags))
    {
      proxyIndex = mapFromSource(idx);
      if (proxyIndex.isValid())
        list << proxyIndex;
    }
    return list;
  }
  // We match everything in the source model because sorting will change what we should show.
  const int allHits = -1;

  QModelIndexList proxyList;
  QMap<int, QModelIndex> proxyMap;
  QModelIndexList sourceList = sourceModel()->match(mapToSource(start), role, value, allHits, flags);
  QModelIndexList::const_iterator it;
  const QModelIndexList::const_iterator begin = sourceList.constBegin();
  const QModelIndexList::const_iterator end = sourceList.constEnd();
  QModelIndex proxyIndex;
  for (it = begin; it != end; ++it)
  {
    proxyIndex = mapFromSource(*it);

    // Any filtered indexes will be invalid when mapped.
    if (!proxyIndex.isValid())
      continue;

    // Inserting in a QMap gives us sorting by key for free.
    proxyMap.insert(proxyIndex.row(), proxyIndex);
  }

  if (hits == -1)
    return proxyMap.values();

  return proxyMap.values().mid(0, hits);
}

int EntityMimeTypeFilterModel::columnCount(const QModelIndex &parent) const
{
  Q_D(const EntityMimeTypeFilterModel);

  if (!sourceModel())
    return 0;

  QVariant var = sourceModel()->data(parent, EntityTreeModel::ColumnCountRole + (EntityTreeModel::TerminalUserRole * d->m_headerGroup));
  if( !var.isValid() )
    return 0;
  return var.toInt();
}

bool EntityMimeTypeFilterModel::hasChildren(const QModelIndex &parent) const
{
  if (!sourceModel())
    return false;

  if ( !parent.isValid() )
    return sourceModel()->hasChildren(parent);

  Q_D(const EntityMimeTypeFilterModel);
  if ( EntityTreeModel::ItemListHeaders == d->m_headerGroup)
    return false;

  if ( EntityTreeModel::CollectionTreeHeaders == d->m_headerGroup )
  {
    QModelIndex childIndex = parent.child( 0, 0 );
    while ( childIndex.isValid() )
    {
      Collection col = childIndex.data( EntityTreeModel::CollectionRole ).value<Collection>();
      if (col.isValid())
        return true;
      childIndex = childIndex.sibling( childIndex.row() + 1, childIndex.column() );
    }
  }
  return false;
}

bool EntityMimeTypeFilterModel::canFetchMore( const QModelIndex &parent ) const
{
  Q_D(const EntityMimeTypeFilterModel);
  if ( EntityTreeModel::CollectionTreeHeaders == d->m_headerGroup )
    return false;
  return QSortFilterProxyModel::canFetchMore(parent);
}

#include "entitymimetypefiltermodel.moc"

