/*
    Copyright (c) 2014 Daniel Vrátil <dvratil@redhat.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.
*/

#ifndef AKONADI_SEARCHQUERY_H
#define AKONADI_SEARCHQUERY_H

#include <QSharedPointer>

#include "akonadi_export.h"

namespace Akonadi
{

/**
 * Search term represents the actual condition within query.
 *
 * SearchTerm can either have multiple subterms, or can be so-called endterm, when
 * there are no more subterms, but instead the actual condition is specified, that
 * is have key, value and relation between them.
 *
 * @since 4.13
 */
class AKONADI_EXPORT SearchTerm
{
public:
    enum Relation {
        RelAnd,
        RelOr
    };

    enum Condition {
        CondEqual,
        CondGreaterThan,
        CondGreaterOrEqual,
        CondLessThan,
        CondLessOrEqual,
        CondContains
    };

    /**
     * Constructs a term where all subterms will be in given relation
     */
    SearchTerm(SearchTerm::Relation relation = SearchTerm::RelAnd);

    /**
     * Constructs an end term
     */
    SearchTerm(const QString &key, const QVariant &value, SearchTerm::Condition condition = SearchTerm::CondEqual);

    SearchTerm(const SearchTerm &other);
    ~SearchTerm();

    SearchTerm &operator=(const SearchTerm &other);
    bool operator==(const SearchTerm &other) const;

    bool isNull() const;

    /**
     * Returns key of this end term.
     */
    QString key() const;

    /**
     * Returns value of this end term.
     */
    QVariant value() const;

    /**
     * Returns relation between key and value.
     */
    SearchTerm::Condition condition() const;

    /**
     * Adds a new subterm to this term.
     *
     * Subterms will be in relation as specified in SearchTerm constructor.
     *
     * If there are subterms in a term, key, value and condition are ignored.
     */
    void addSubTerm(const SearchTerm &term);

    /**
     * Returns all subterms, or an empty list if this is an end term.
     */
    QList<SearchTerm> subTerms() const;

    /**
     * Returns relation in which all subterms are.
     */
    SearchTerm::Relation relation() const;

    /**
     * Sets whether the entire term is negated.
     */
    void setIsNegated(bool negated);

    /**
     * Returns whether the entire term is negated.
     */
    bool isNegated() const;

private:
    class Private;
    QSharedDataPointer<Private> d;
};

/**
 * @brief A query that can be passed to ItemSearchJob or others.
 *
 * @since 4.13
 */
class AKONADI_EXPORT SearchQuery
{
public:
    /**
     * Constructs query where all added terms will be in given relation
     */
    SearchQuery(SearchTerm::Relation rel = SearchTerm::RelAnd);

    ~SearchQuery();
    SearchQuery(const SearchQuery &other);
    SearchQuery &operator=(const SearchQuery &other);
    bool operator==(const SearchQuery &other) const;

    bool isNull() const;

    /**
     * Adds a new term.
     */
    void addTerm(const QString &key, const QVariant &value, SearchTerm::Condition condition = SearchTerm::CondEqual);

    /**
     * Adds a new term with subterms
     */
    void addTerm(const SearchTerm &term);

    /**
     * Sets the root term
     */
    void setTerm(const SearchTerm &term);

    /**
     * Returns the root term.
     */
    SearchTerm term() const;

    /**
     * Sets the maximum number of results.
     *
     * Note that this limit is only evaluated per search backend,
     * so the total number of results retrieved may be larger.
     */
    void setLimit(int limit);

    /**
     * Returns the maximum number of results.
     *
     * The default value is -1, indicating no limit.
     */
    int limit() const;

    QByteArray toJSON() const;
    static SearchQuery fromJSON(const QByteArray &json);

private:
    class Private;
    QSharedDataPointer<Private> d;

};

/**
 * A search term for an email field.
 *
 * This class can be used to create queries that akonadi email search backends understand.
 *
 * @since 4.13
 */
class AKONADI_EXPORT EmailSearchTerm : public SearchTerm
{
public:

    /**
     * All fields expect a search string unless noted otherwise.
     */
    enum EmailSearchField {
        Unknown,
        Subject,
        Body,
        Message, //Complete message including headers, body and attachment
        Headers, //All headers
        HeaderFrom,
        HeaderTo,
        HeaderCC,
        HeaderBCC,
        HeaderReplyTo,
        HeaderOrganization,
        HeaderListId,
        HeaderResentFrom,
        HeaderXLoop,
        HeaderXMailingList,
        HeaderXSpamFlag,
        HeaderDate, //Expects QDateTime
        HeaderOnlyDate, //Expectes QDate
        MessageStatus, //Expects message flag from Akonadi::MessageFlags. Boolean filter.
        ByteSize, //Expects int
        Attachment, //Textsearch on attachment
        MessageTag
    };

    /**
     * Constructs an email end term
     */
    EmailSearchTerm(EmailSearchField field, const QVariant &value, SearchTerm::Condition condition = SearchTerm::CondEqual);

    /**
     * Translates field to key
     */
    static QString toKey(EmailSearchField);

    /**
     * Translates key to field
     */
    static EmailSearchField fromKey(const QString &key);
};

/**
 * A search term for a contact field.
 *
 * This class can be used to create queries that akonadi contact search backends understand.
 *
 * @since 4.13
 */
class AKONADI_EXPORT ContactSearchTerm : public SearchTerm
{
public:
    enum ContactSearchField {
        Unknown,
        Name,
        Email,
        Nickname,
        Uid,
        All //Special field: matches all contacts.
    };

    ContactSearchTerm(ContactSearchField field, const QVariant &value, SearchTerm::Condition condition = SearchTerm::CondEqual);

    /**
     * Translates field to key
     */
    static QString toKey(ContactSearchField);

    /**
     * Translates key to field
     */
    static ContactSearchField fromKey(const QString &key);
};

}

#endif // AKONADI_SEARCHQUERY_H
