/*
   This file is part of the Nepomuk KDE project.
   Copyright (C) 2010 Sebastian Trueg <trueg@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) version 3, or any
   later version accepted by the membership of KDE e.V. (or its
   successor approved by the membership of KDE e.V.), which shall
   act as a proxy defined in Section 6 of version 3 of the license.

   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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "facetmodel.h"
#include "simplefacet.h"
#include "dynamicresourcefacet.h"
#include "datefacet.h"
#include "facet.h"

#include <QtCore/QDate>

#include <Soprano/Vocabulary/NAO>

#include <Nepomuk2/Query/StandardQuery>
#include <Nepomuk2/Query/AndTerm>
#include <Nepomuk2/Query/OrTerm>
#include <Nepomuk2/Query/ComparisonTerm>
#include <Nepomuk2/Query/LiteralTerm>
#include <Nepomuk2/Query/ResourceTerm>
#include <Nepomuk2/Query/ResourceTypeTerm>
#include <Nepomuk2/Query/Query>

#include <Nepomuk2/Tag>

#include <Nepomuk2/Vocabulary/TMO>
#include <Nepomuk2/Vocabulary/NMO>
#include <Nepomuk2/Vocabulary/NCO>
#include <Nepomuk2/Vocabulary/NFO>
#include <Nepomuk2/Vocabulary/NIE>

#include <KGuiItem>
#include <KDebug>

#include <KGlobal>
#include <KLocale>
#include <KCalendarSystem>
#include <KGuiItem>
#include <KDebug>

using namespace Nepomuk2::Query;


class Nepomuk2::Utils::FacetModel::Private
{
public:
    QList<Facet*> m_facets;

    bool m_blockQueryTermChangedSignal;

    void addFacet( Facet* facet );
    QModelIndex parentIndexForFacet( Facet* facet ) const;

    void handleFacetsChanged();

    void _k_queryTermChanged();
    void _k_facetSelectionChanged( Nepomuk2::Utils::Facet* facet );
    void _k_facetLayoutChanged( Nepomuk2::Utils::Facet* );

    FacetModel* q;
};


void Nepomuk2::Utils::FacetModel::Private::addFacet( Facet* facet )
{
    q->connect(facet, SIGNAL(queryTermChanged(Nepomuk2::Utils::Facet*,Nepomuk2::Query::Term)),
               SLOT(_k_queryTermChanged()) );
    q->connect(facet, SIGNAL(selectionChanged(Nepomuk2::Utils::Facet*)),
               SLOT(_k_facetSelectionChanged(Nepomuk2::Utils::Facet*)) );
    q->connect(facet, SIGNAL(layoutChanged(Nepomuk2::Utils::Facet*)),
               SLOT(_k_facetLayoutChanged(Nepomuk2::Utils::Facet*)) );
    m_facets.append(facet);
}


QModelIndex Nepomuk2::Utils::FacetModel::Private::parentIndexForFacet( Facet* facet ) const
{
    const int i = m_facets.indexOf( facet );
    return q->index( i, 0, QModelIndex() );
}


void Nepomuk2::Utils::FacetModel::Private::handleFacetsChanged()
{
    if( !m_blockQueryTermChangedSignal )
        emit q->queryTermChanged( q->queryTerm() );
}


void Nepomuk2::Utils::FacetModel::Private::_k_queryTermChanged()
{
    handleFacetsChanged();
}


void Nepomuk2::Utils::FacetModel::Private::_k_facetSelectionChanged( Nepomuk2::Utils::Facet* facet )
{
    kDebug();
    QModelIndex parent = parentIndexForFacet( facet );
    if( facet->count() )
        emit q->dataChanged( q->index( 0, 0, parent ), q->index( q->rowCount(parent)-1, 0, parent ) );
}


void Nepomuk2::Utils::FacetModel::Private::_k_facetLayoutChanged( Nepomuk2::Utils::Facet* )
{
    // sadly we do not know the differences
    q->reset();
}


Nepomuk2::Utils::FacetModel::FacetModel( QObject* parent )
    : QAbstractItemModel( parent ),
      d(new Private() )
{
    d->q = this;
    d->m_blockQueryTermChangedSignal = false;
}


Nepomuk2::Utils::FacetModel::~FacetModel()
{
    qDeleteAll( d->m_facets );
    delete d;
}


int Nepomuk2::Utils::FacetModel::columnCount( const QModelIndex& ) const
{
    return 1;
}


QVariant Nepomuk2::Utils::FacetModel::data( const QModelIndex& index, int role ) const
{
    if( index.isValid() ) {
        if( index.internalPointer() ) {
            Facet* facet = static_cast<Facet*>( index.internalPointer() );

            if ( role == FacetRole )
                return QVariant::fromValue( facet );

            switch( role ) {
            case Qt::DisplayRole: {
                QString title = facet->text(index.row());
                return title;
            }
            case Qt::CheckStateRole:
                return( facet->isSelected(index.row()) ? Qt::Checked : Qt::Unchecked );
            }
        }
        else {
            switch( role ) {
            case FacetRole:
                return QVariant::fromValue( d->m_facets[index.row()] );

//             case Qt::DisplayRole:
//                 return d->m_facets[index.row()]->facet().title();
            }
        }
    }

    return QVariant();
}


bool Nepomuk2::Utils::FacetModel::setData( const QModelIndex& index, const QVariant& value, int role )
{
    if( role == Qt::CheckStateRole && index.internalPointer() ) {
        Facet* facet = static_cast<Facet*>( index.internalPointer() );
        facet->setSelected( index.row(), value.toBool() );
        return true;
    }
    else {
        return false;
    }
}


bool Nepomuk2::Utils::FacetModel::hasChildren( const QModelIndex& parent ) const
{
    // facet terms don't have children
    if( parent.internalPointer() ) {
        return false;
    }
    else {
        return rowCount(parent) > 0;
    }
}


QModelIndex Nepomuk2::Utils::FacetModel::parent( const QModelIndex& index ) const
{
    if( index.internalPointer() ) {
        return d->parentIndexForFacet( static_cast<Facet*>( index.internalPointer() ) );
    }
    else {
        return QModelIndex();
    }
}


int Nepomuk2::Utils::FacetModel::rowCount( const QModelIndex& parent ) const
{
    if( !parent.isValid() ) {
        return d->m_facets.count();
    }
    else if( parent.row() < d->m_facets.count() ) {
        return d->m_facets[parent.row()]->count();
    }
    else {
        return 0;
    }
}


QModelIndex Nepomuk2::Utils::FacetModel::index( int row, int column, const QModelIndex& parent ) const
{
    if(row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent)) {
        return QModelIndex();
    }
    if( parent.isValid() ) {
        return createIndex( row, column, d->m_facets[parent.row()] );
    }
    else {
        return createIndex( row, column );
    }
}


Qt::ItemFlags Nepomuk2::Utils::FacetModel::flags( const QModelIndex& index ) const
{
    if(!index.isValid()) {
        return Qt::NoItemFlags;
    }
    // we do not even allow selection since that does not make much sense for our use case
    Qt::ItemFlags flags = Qt::ItemIsEnabled;
    if( index.internalPointer() ) {
        flags |= Qt::ItemIsUserCheckable;
    }
    return flags;
}


void Nepomuk2::Utils::FacetModel::addFacet( Facet* facet )
{
    d->addFacet( facet );
    reset();
    d->handleFacetsChanged();
    // TODO: do not use reset but the fancy begin/end methods
}


void Nepomuk2::Utils::FacetModel::setFacets( const QList<Facet*>& facets )
{
    clear();
    Q_FOREACH( Facet* facet, facets ) {
        d->addFacet( facet );
    }
    reset();
    d->handleFacetsChanged();
}


void Nepomuk2::Utils::FacetModel::clearSelection()
{
    d->m_blockQueryTermChangedSignal = true;
    Q_FOREACH( Facet* facet, d->m_facets ) {
        facet->clearSelection();
    }
    d->m_blockQueryTermChangedSignal = false;
    d->handleFacetsChanged();
}


void Nepomuk2::Utils::FacetModel::clear()
{
    qDeleteAll(d->m_facets);
    d->m_facets.clear();
    reset();
}


QList<Nepomuk2::Utils::Facet*> Nepomuk2::Utils::FacetModel::facets() const
{
    return d->m_facets;
}


Nepomuk2::Query::Term Nepomuk2::Utils::FacetModel::queryTerm() const
{
    AndTerm term;
    Q_FOREACH( Facet* facet, d->m_facets ) {
        term.addSubTerm( facet->queryTerm() );
    }
    return term.optimized();
}


Nepomuk2::Query::Query Nepomuk2::Utils::FacetModel::extractFacetsFromQuery( const Nepomuk2::Query::Query& query )
{
    // safety net to prevent endless loops
    // ===============================
    if ( query.term() == queryTerm() ) {
        Query::Query rest(query);
        rest.setTerm(Query::Term());
        return rest;
    }

    // we do not want to emit any queryTermChanged() signal during this method
    // as it would confuse client code
    // ================================
    d->m_blockQueryTermChangedSignal = true;

    // reset all facets in the model
    // ================================
    Q_FOREACH( Facet* f, d->m_facets ) {
        f->clearSelection();
    }

    // we extract all facets we can find and leave the rest in the query
    // ================================
    Query::Query restQuery = query.optimized();
    Term restTerm = restQuery.term();

    // Step one is to use the term in question as the client query for all facets
    // this is necessary since otherwise facets like ProxyFacet will ignore all
    // calls to selectFromTerm(). The client query can later be reset by the client.
    // it is not wrong but may be only part of the actual client query.
    setClientQuery( restQuery );

    // first we check if the main term is already a facet term
    // (this way we can also handle facets that use AndTerms)
    // ================================

    // if any of the facets contains the term, set it to selected
    Q_FOREACH( Facet* f, d->m_facets ) {
        if( f->selectFromTerm( restTerm ) ) {
            restTerm = Term();
            break;
        }
    }

    // now go into an AndTerm and check each sub term for facet
    // ================================
    if( restTerm.isAndTerm() ) {
        // we need to cache the facets since we cannot use MatchOne facets more than once
        QList<Facet*> facets = d->m_facets;

        AndTerm restAndTerm;
        foreach( const Term& term, restTerm.toAndTerm().subTerms() ) {
            bool termFound = false;

            Q_FOREACH( Facet* f, facets ) {
                if( f->selectFromTerm( term ) ) {
                    termFound = true;
                    // we can only use MatchAll facets more than once
                    // Example: we have an exclusive facet that can handle both term A and B
                    //          we now call selectFromTerm(A) and selectFromTerm(B)
                    //          both return true but only the latter is the one that sticks.
                    //          thus, we implicitely drop A from the query!
                    // None-exclusive facets are not a problem as they simply add to their
                    // existing selection.
                    if( f->selectionMode() != Facet::MatchAll )
                        facets.removeAll(f);
                    break;
                }
            }

            // we did not find a matching facet
            if( !termFound )
                restAndTerm.addSubTerm( term );
        }

        restTerm = restAndTerm;
    }

    // done with the facet selection
    // ================================
    d->m_blockQueryTermChangedSignal = false;

    d->handleFacetsChanged();

    restQuery.setTerm(restTerm);
    return restQuery.optimized();
}


void Nepomuk2::Utils::FacetModel::setClientQuery( const Nepomuk2::Query::Query& query )
{
    Q_FOREACH( Facet* facet, d->m_facets ) {
        facet->setClientQuery( query );
    }
}

#include "facetmodel.moc"
