/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmessagefilter.h"
#include "qmessagefilter_p.h"
#include "qvariant.h"
#include "qdebug.h"
#include "winhelpers_p.h"
#include "qmessageaccountid_p.h"
#include "qmessagefolderid_p.h"
#include "qmessageid_p.h"
#include "addresshelper_p.h"

// Not sure if this will work on WinCE
#ifndef PR_SMTP_ADDRESS
#define PR_SMTP_ADDRESS 0x39FE001E
#endif

QTM_BEGIN_NAMESPACE

void QDateTimeToFileTime(const QDateTime &dt, FILETIME *ft)
{
    SYSTEMTIME st;
    st.wYear = dt.date().year();
    st.wMonth = dt.date().month();
    st.wDay = dt.date().day();
    st.wHour = dt.time().hour();
    st.wMinute = dt.time().minute();
    st.wSecond = dt.time().second();
    st.wMilliseconds = dt.time().msec();
    SystemTimeToFileTime(&st, ft);
}

void QStringToWCharArray(const QString &str, wchar_t **buffer)
{
    delete *buffer;
    *buffer = new wchar_t[str.length() +1];
    memcpy(*buffer, str.utf16(), str.length() * sizeof(wchar_t));
    (*buffer)[str.length()] = 0;
}


MapiFolderIterator::MapiFolderIterator()
    :_store(0)
{
}

MapiFolderIterator::MapiFolderIterator(MapiStorePtr store, 
                                       MapiFolderPtr root, 
                                       QSet<QMessage::StandardFolder> standardFoldersInclude, 
                                       QSet<QMessage::StandardFolder> standardFoldersExclude,
                                       FolderIdSet parentInclude,
                                       FolderIdSet parentExclude,
                                       FolderIdSet ancestorInclude,
                                       FolderIdSet ancestorExclude)
    :_store(store), 
    _standardFoldersInclude(standardFoldersInclude), 
    _standardFoldersExclude(standardFoldersExclude),
    _parentInclude(parentInclude),
    _parentExclude(parentExclude),
    _ancestorInclude(ancestorInclude),
    _ancestorExclude(ancestorExclude)
{
    QList<MapiFolderPtr> unprocessed;
    if (root && root->isValid())
        unprocessed.append(root);
    while (_store && _store->isValid() && !unprocessed.isEmpty()) {
        MapiFolderPtr parent(unprocessed.takeLast());
        if (parent->isValid()) {
            if (!_store->session()->equal(parent->entryId(), root->entryId()))
                _folders.append(parent);
            QMessageManager::Error ignored(QMessageManager::NoError);
            while (true) {
                MapiFolderPtr folder(parent->nextSubFolder(&ignored));
                if (ignored != QMessageManager::NoError) {
                    qWarning() << "MapiFolderIterator:: Error getting next subfolder";
                    break;
                }
                if (folder && folder->isValid()) {
                    unprocessed.append(folder);
                } else {
                    if (folder) {
                        qWarning() << "MaiFolderIterator:: Invalid subfolder:" << folder->name();
                    }
                    break; // No more subfolders in the current folder
                }
            }
        } else {
            qWarning() << "Invalid folder:" << parent->name();
        }
    }
}

static bool sFolderMatches(MapiFolderPtr folder,
                           QSet<QMessage::StandardFolder> standardFoldersInclude, 
                           QSet<QMessage::StandardFolder> standardFoldersExclude,
                           FolderIdSet parentInclude,
                           FolderIdSet parentExclude,
                           FolderIdSet ancestorInclude,
                           FolderIdSet ancestorExclude)
{
    if (!folder || !folder->isValid()) {
        if (folder) {
            qWarning() << "MaiFolderIterator::next() Invalid subfolder:" << folder->name();
        }
        return false;
    }
    if (!standardFoldersInclude.isEmpty() || !standardFoldersExclude.isEmpty()) {
        QMessage::StandardFolder folderType(folder->standardFolder());
        if (!(standardFoldersInclude.isEmpty() || standardFoldersInclude.contains(folderType))
            || standardFoldersExclude.contains(folderType))
            return false;
    }

    if (!parentInclude.isEmpty() || !parentExclude.isEmpty()) {
        QMessageFolderId parentId(folder->parentId());
#ifndef QSTRING_FOLDER_ID
        if (!(parentInclude.isEmpty() || parentInclude.contains(folder->id()))
            || parentExclude.contains(folder->id()))
            return false;
#else
        QString path(folder->accountId().toString() + "/" + QMessageFolder(folder->id()).path());
        if (!(parentInclude.isEmpty() || parentInclude.contains(path))
            || parentExclude.contains(path))
            return false;
#endif
    }

    if (!ancestorInclude.isEmpty() || !ancestorExclude.isEmpty()) {
        FolderIdSet folderAncestors;
        QMessageFolderId id(folder->parentId());
        while (id.isValid()) {
            QMessageFolder f(id);
#ifndef QSTRING_FOLDER_ID
            folderAncestors.insert(id);
#else
            folderAncestors.insert(f.parentAccountId().toString() + "/" + f.path());
#endif
            id = f.parentFolderId();
        }
        FolderIdSet includeIntersection(ancestorInclude);
        includeIntersection.intersect(folderAncestors);
        FolderIdSet excludeIntersection(ancestorExclude);
        excludeIntersection.intersect(folderAncestors);
        if (!(ancestorInclude.isEmpty() || !includeIntersection.isEmpty())
            || !excludeIntersection.isEmpty()) {
            return false;
        }
    }
    return true;
}

MapiFolderPtr MapiFolderIterator::next()
{
    while (_store && _store->isValid() && !_folders.isEmpty()) {
        MapiFolderPtr folder(_folders.takeFirst());
        if (sFolderMatches(folder, 
                            _standardFoldersInclude, 
                            _standardFoldersExclude, 
                            _parentInclude, 
                            _parentExclude, 
                            _ancestorInclude, 
                            _ancestorExclude)) {
            return folder;
        }
    }

    return MapiFolderPtr();
}

MapiStoreIterator::MapiStoreIterator()
{
}

MapiStoreIterator::MapiStoreIterator(QList<MapiStorePtr> stores, QSet<QMessageAccountId> accountsInclude, QSet<QMessageAccountId> accountsExclude)
    :_stores(stores), _accountsInclude(accountsInclude), _accountsExclude(accountsExclude)
{
}

static bool sAccountIdMatches(QMessageAccountId key, 
                              QSet<QMessageAccountId> accountsInclude, 
                              QSet<QMessageAccountId> accountsExclude)
{
    if ((accountsInclude.isEmpty() || accountsInclude.contains(key))
        && (accountsExclude.isEmpty() || !accountsExclude.contains(key))) {
        return true;
    }
    return false;
}

MapiStorePtr MapiStoreIterator::next()
{
    while (!_stores.isEmpty()) {
        MapiStorePtr store(_stores.takeFirst());
#ifdef _WIN32_WCE
        QMessageAccountId key(QMessageAccountIdPrivate::from(store->entryId()));
#else
        QMessageAccountId key(QMessageAccountIdPrivate::from(store->recordKey()));
#endif
        if (sAccountIdMatches(key, _accountsInclude, _accountsExclude))
            return store;
    }

    return MapiStorePtr();
}

QMessageFilter QMessageFilterPrivate::from(QMessageFilterPrivate::Field field, const QVariant &value, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = field;
    result.d_ptr->_value = value;
    result.d_ptr->_comparatorType = Equality;
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

QMessageFilter QMessageFilterPrivate::from(QMessageFilterPrivate::Field field, const QVariant &value, QMessageDataComparator::RelationComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = field;
    result.d_ptr->_value = value;
    result.d_ptr->_comparatorType = Relation;
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

QMessageFilter QMessageFilterPrivate::from(QMessageFilterPrivate::Field field, const QVariant &value, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = field;
    result.d_ptr->_value = value;
    result.d_ptr->_comparatorType = Inclusion;
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

QMessageFilterPrivate* QMessageFilterPrivate::implementation(const QMessageFilter &filter)
{
    return filter.d_ptr;
}

MapiFolderIterator QMessageFilterPrivate::folderIterator(const QMessageFilter &filter, QMessageManager::Error *error, const MapiStorePtr &store)
{
    return MapiFolderIterator(store, 
        store->rootFolder(error),
        filter.d_ptr->_standardFoldersInclude, 
        filter.d_ptr->_standardFoldersExclude,
        filter.d_ptr->_parentInclude, 
        filter.d_ptr->_parentExclude,
        filter.d_ptr->_ancestorInclude, 
        filter.d_ptr->_ancestorExclude);
}

MapiStoreIterator QMessageFilterPrivate::storeIterator(const QMessageFilter &filter, QMessageManager::Error *error, const MapiSessionPtr &session)
{
    return MapiStoreIterator(session->allStores(error), filter.d_ptr->_accountsInclude, filter.d_ptr->_accountsExclude);
}

QList<QMessageFilter> QMessageFilterPrivate::subfilters(const QMessageFilter &filter)
{
    QList<QMessageFilter> result;
    QList<QMessageFilter> queue;
    queue.append(filter);
    while (!queue.isEmpty()) {
        QMessageFilter top(queue.takeFirst());
        if (!top.d_ptr->_complex) {
            result.append(top);
            continue;
        }
        // Complex so must consist of two subfilters or'd together and an empty containerFiltersPart
        queue.append(*top.d_ptr->_left);
        queue.append(*top.d_ptr->_right);
    }
    return result;
}

// Several filters require QMessageManager::queryX to be called to evaluate filter member variables, 
// namely byIds(const QMessageFilter &, ...), byParentAccountId(const QMessageAccountFilter &, ...), 
// byFolderIds(const QMessageFolderFilter &, ...), byAncestorFolderIds(const QMessageFolderFilter &, ...)
QMessageFilter QMessageFilterPrivate::preprocess(QMessageManager::Error *error, MapiSessionPtr session, const QMessageFilter &filter)
{
    QMessageFilter result(filter);
    QMessageFilterPrivate::preprocess(error, session, &result);
    return result;
}

// returns true if filter is modified
bool QMessageFilterPrivate::preprocess(QMessageManager::Error *error, MapiSessionPtr session, QMessageFilter *filter)
{
    if (!filter)
        return false;

    // incl only used if filter->d_ptr->field is == *Filter
    QMessageDataComparator::InclusionComparator incl(static_cast<QMessageDataComparator::InclusionComparator>(filter->d_ptr->_comparatorValue));
    bool inclusion(incl == QMessageDataComparator::Includes);
    QMessageFilter result;
    if (filter->d_ptr->_operator == Not) {
        inclusion = !inclusion;
    }
    if (inclusion) {
        result = ~QMessageFilter();
    }
    if (filter->d_ptr->_field == MessageFilter) {
        if (filter->d_ptr->_messageFilter->isEmpty()) {
            result = ~result;  // match all for include, match none for exclude
        } else {
            QMessageIdList ids(session->queryMessages(error, *filter->d_ptr->_messageFilter));
            result = QMessageFilter::byId(ids, inclusion ? QMessageDataComparator::Includes : QMessageDataComparator::Excludes);
        }
    } else if (filter->d_ptr->_field == AccountFilter) {
        if (filter->d_ptr->_accountFilter->isEmpty()) {
            result = ~result;  // match all for include, match none for exclude
        } else {
            QList<MapiStorePtr> stores(session->filterStores(error, *filter->d_ptr->_accountFilter));
            foreach(MapiStorePtr store, stores) {
                if (inclusion) {
                    result |= QMessageFilter::byParentAccountId(store->id());
                } else {
                    result &= QMessageFilter::byParentAccountId(store->id(), QMessageDataComparator::NotEqual);
                }
            }
        }
    } else if (filter->d_ptr->_field == FolderFilter) {
        if (filter->d_ptr->_folderFilter->isEmpty()) {
            result = ~result;  // match all for include, match none for exclude
        } else {
            QList<MapiFolderPtr> folders(session->filterFolders(error, *filter->d_ptr->_folderFilter));
            foreach(MapiFolderPtr folder, folders) {
                if (inclusion) {
                    result |= QMessageFilter::byParentFolderId(folder->id());
                } else {
                    result &= QMessageFilter::byParentFolderId(folder->id(), QMessageDataComparator::NotEqual);
                }
            }
        }
    } else if (filter->d_ptr->_field == AncestorFilter) {
        QList<MapiFolderPtr> folders(session->filterFolders(error, *filter->d_ptr->_folderFilter));
        foreach(MapiFolderPtr folder, folders) {
            if (inclusion) {
                result |= QMessageFilter::byAncestorFolderIds(folder->id());
            } else {
                result &= QMessageFilter::byAncestorFolderIds(folder->id(), QMessageDataComparator::Excludes);
            }
        }
    } else {
        QMessageFilter *l(filter->d_ptr->_left);
        QMessageFilter *r(filter->d_ptr->_right);
        bool modified(true); //TODO: should default to false but tst_qmessagestorekeys (id list exclusion 3) is failing
        modified |= preprocess(error, session, l);
        modified |= preprocess(error, session, r);

        // It's necessary to recombine bool op filters, because the operands may now have non-empty containerFilter parts,
        // specifically in the case that one of the operands has a *Filter field.
        if (modified) {
            switch (filter->d_ptr->_operator) {
            case And:
                *filter = filter->d_ptr->containerFiltersPart() & (*l & *r);
                break;
            case Nand:
                *filter = filter->d_ptr->containerFiltersPart() &  ~(*l & *r);
                break;
            case Or:
                *filter = filter->d_ptr->containerFiltersPart() &  (*l | *r);
                break;
            case Nor:
                *filter = filter->d_ptr->containerFiltersPart() &  ~(*l | *r);
                break;
            }
        }
        return modified;
    }
    *filter = filter->d_ptr->containerFiltersPart() & result; // Must preserve container filters part
    return true;
}

bool QMessageFilterPrivate::restrictionPermitted(const QMessageFilter &filter)
{
    bool result(filter.d_ptr->_restrictionPermitted);
    if (filter.d_ptr->_left)
        result &= QMessageFilterPrivate::restrictionPermitted(*filter.d_ptr->_left);
    if (filter.d_ptr->_right)
        result &= QMessageFilterPrivate::restrictionPermitted(*filter.d_ptr->_right);
    return result;
}

bool QMessageFilterPrivate::matchesMessageRequired(const QMessageFilter &filter)
{
    bool result(filter.d_ptr->_matchesRequired);
    if (filter.d_ptr->_left)
        result |= QMessageFilterPrivate::matchesMessageRequired(*filter.d_ptr->_left);
    if (filter.d_ptr->_right)
        result |= QMessageFilterPrivate::matchesMessageRequired(*filter.d_ptr->_right);
    return result;
}

bool QMessageFilterPrivate::containsSenderSubfilter(const QMessageFilter &filter)
{
    bool result(filter.d_ptr->_field == QMessageFilterPrivate::Sender);
    if (filter.d_ptr->_left)
        result |= QMessageFilterPrivate::containsSenderSubfilter(*filter.d_ptr->_left);
    if (filter.d_ptr->_right)
        result |= QMessageFilterPrivate::containsSenderSubfilter(*filter.d_ptr->_right);
    return result;
}

bool QMessageFilterPrivate::isNonMatching(const QMessageFilter &filter)
{
    return (filter.d_ptr->containerFiltersAreEmpty() 
        && (filter.d_ptr->_field == QMessageFilterPrivate::None)
        && (filter.d_ptr->_operator == QMessageFilterPrivate::Not));
}


bool QMessageFilterPrivate::matchesMessage(const QMessageFilter &filter, const QMessage &message, MapiStore *store)
{
    if (filter.isEmpty())
        return true;

    QMessageAccountId accountId(message.parentAccountId());
    if (!sAccountIdMatches(accountId, filter.d_ptr->_accountsInclude, filter.d_ptr->_accountsExclude))
        return false;

    QMessageManager::Error ignoredError(QMessageManager::NoError);
#ifdef _WIN32_WCE
    MapiFolderPtr folder = store->openFolder(&ignoredError, QMessageIdPrivate::folderRecordKey(message.id()));
#else
    MapiFolderPtr folder = store->openFolderWithKey(&ignoredError, QMessageIdPrivate::folderRecordKey(message.id()));
#endif
    if (ignoredError != QMessageManager::NoError)
        return false;
    if (!sFolderMatches(folder, 
        filter.d_ptr->_standardFoldersInclude, 
        filter.d_ptr->_standardFoldersExclude,
        filter.d_ptr->_parentInclude, 
        filter.d_ptr->_parentExclude,
        filter.d_ptr->_ancestorInclude, 
        filter.d_ptr->_ancestorExclude))
        return false;
    return matchesMessageSimple(filter, message);
}


// Simple matchesMessage function, filter must have an empty nonContainerFiltersPart
bool QMessageFilterPrivate::matchesMessageSimple(const QMessageFilter &filter, const QMessage &message)
{
    bool negate(false);
    bool result(true);

    if (!filter.d_ptr->_valid) {
        qWarning("matchesMessage: Invalid filter application attempted.");
        return false;
    }

    if (filter.d_ptr->_matchFlags & QMessageDataComparator::MatchFullWord) {
        // TODO: Document Full word matching is not supported on MAPI
        qWarning("matchesMessage: Full word matching not supported on MAPI platforms.");
        return false;
    }

    switch (filter.d_ptr->_operator) {
    case QMessageFilterPrivate::Not: // fall through
    case QMessageFilterPrivate::Nand: // fall through
    case QMessageFilterPrivate::Nor:
        negate = true;
        break;
    default:
        break;
    } //end switch

    switch (filter.d_ptr->_operator) {
    case QMessageFilterPrivate::And: // fall through
    case QMessageFilterPrivate::Nand:
        result = matchesMessageSimple(*filter.d_ptr->_left, message) 
            && matchesMessageSimple(*filter.d_ptr->_right, message);
        break;
    case QMessageFilterPrivate::Nor: // fall through
    case QMessageFilterPrivate::Or:
        result = matchesMessageSimple(*filter.d_ptr->_left, message) 
            || matchesMessageSimple(*filter.d_ptr->_right, message);
        break;
    case QMessageFilterPrivate::Not: // fall through
    case QMessageFilterPrivate::Identity: {
        switch (filter.d_ptr->_field) {
        case QMessageFilterPrivate::None:
            break;
        case QMessageFilterPrivate::Recipients: // fall through
        case QMessageFilterPrivate::Sender: // fall through
        case QMessageFilterPrivate::Subject: {
            QString value(filter.d_ptr->_value.toString());
            QStringList messageStrings;
            QString tmp;
            bool caseSensitive(filter.d_ptr->_matchFlags & QMessageDataComparator::MatchCaseSensitive);
            if (!caseSensitive) {
                value = value.toLower();
            }

            if (filter.d_ptr->_field == QMessageFilterPrivate::Recipients) {
                QMessageAddressList addresses(message.to() + message.cc() + message.bcc());
                foreach(QMessageAddress address, addresses) {
                    tmp = address.addressee();
                    if (!caseSensitive) {
                        tmp = tmp.toLower();
                    }
                    messageStrings.append(tmp);
                }
            } else if (filter.d_ptr->_field == QMessageFilterPrivate::Sender) {
                tmp = message.from().addressee();
                if (!caseSensitive) {
                    tmp = tmp.toLower();
                }
                messageStrings.append(tmp);
            } else { // Subject
                tmp = message.subject();
                if (!caseSensitive) {
                    tmp = tmp.toLower();
                }
                messageStrings.append(tmp);
            }

            if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Relation) {
                qWarning("matchesMessage: Unhandled restriction criteria, comparator type relation");
                break;
            } else if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
                if (static_cast<QMessageDataComparator::EqualityComparator>(filter.d_ptr->_comparatorValue) != QMessageDataComparator::Equal) {
                    negate = !negate;
                }
            } else if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Inclusion) {
                if (static_cast<QMessageDataComparator::InclusionComparator>(filter.d_ptr->_comparatorValue) == QMessageDataComparator::Excludes) {
                    negate = !negate;
                }
            } else { // Unknown comparator type
                qWarning("matchesMessage: Unhandled restriction criteria, string comparator type unknown");
                break;
            }

            result = false;
            foreach(QString str, messageStrings) {
                if ((filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality)
                    && (str == value)) {
                    result = true;
                    break;
                } else if ((filter.d_ptr->_comparatorType == QMessageFilterPrivate::Inclusion)
                    && (str.contains(value))) {
                    result = true;
                    break;
                }
            }
            break;
        }
        case QMessageFilterPrivate::TimeStamp: // fall through
        case QMessageFilterPrivate::ReceptionTimeStamp: {
            QDateTime value(filter.d_ptr->_value.toDateTime());
            QDateTime date;
            if (filter.d_ptr->_field == QMessageFilterPrivate::TimeStamp) {
                date = message.date();
            } else { // ReceptionTimeStamp
                date = message.receivedDate();
            }

            if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Relation) {
                QMessageDataComparator::RelationComparator cmp(static_cast<QMessageDataComparator::RelationComparator>(filter.d_ptr->_comparatorValue));
                switch (cmp) {
                case QMessageDataComparator::LessThan:
                    result = (date < value);
                    break;
                case QMessageDataComparator::LessThanEqual:
                    result = (date <= value);
                    break;
                case QMessageDataComparator::GreaterThan:
                    result = (date > value);
                    break;
                case QMessageDataComparator::GreaterThanEqual:
                    result = (date >= value);
                    break;
                }
            } else if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
                if (static_cast<QMessageDataComparator::EqualityComparator>(filter.d_ptr->_comparatorValue) != QMessageDataComparator::Equal) {
                    negate = !negate;
                }
                result = (date == value);
            } else { // Inclusion
                qWarning("matchesMessage: Unhandled restriction criteria, timestamp comparator type");
                break;
            }

            break;
        }
        case QMessageFilterPrivate::Priority: {
            QMessage::Priority priority(static_cast<QMessage::Priority>(filter.d_ptr->_value.toInt()));
            if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
                if (static_cast<QMessageDataComparator::EqualityComparator>(filter.d_ptr->_comparatorValue) != QMessageDataComparator::Equal) {
                    negate = !negate;
                }
                result = (message.priority() == priority);
            } else { // Not equality
                qWarning("matchesMessage: Unhandled restriction criteria, priority comparator type");
            }
            break;
        }
        case QMessageFilterPrivate::Size: {
            int value(filter.d_ptr->_value.toInt());
            int size(message.size());

            if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Relation) {
                QMessageDataComparator::RelationComparator cmp(static_cast<QMessageDataComparator::RelationComparator>(filter.d_ptr->_comparatorValue));
                switch (cmp) {
                case QMessageDataComparator::LessThan:
                    result = (size < value);
                    break;
                case QMessageDataComparator::LessThanEqual:
                    result = (size <= value);
                    break;
                case QMessageDataComparator::GreaterThan:
                    result = (size > value);
                    break;
                case QMessageDataComparator::GreaterThanEqual:
                    result = (size >= value);
                    break;
                }
            } else if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
                if (static_cast<QMessageDataComparator::EqualityComparator>(filter.d_ptr->_comparatorValue) != QMessageDataComparator::Equal) {
                    negate = !negate;
                }
                result = (size == value);
            } else { // Inclusion
                qWarning("matchesMessage: Unhandled restriction criteria, size comparator type");
                break;
            }

            break;
        }
        case QMessageFilterPrivate::Status: {
            QMessage::Status value(static_cast<QMessage::Status>(filter.d_ptr->_value.toUInt()));
            QMessage::StatusFlags statusFlags(message.status());

            if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
                if (static_cast<QMessageDataComparator::EqualityComparator>(filter.d_ptr->_comparatorValue) != QMessageDataComparator::Equal) {
                    negate = !negate;
                }
                result = (statusFlags == value);
            } else if (filter.d_ptr->_comparatorType == QMessageFilterPrivate::Inclusion) {
                if (static_cast<QMessageDataComparator::InclusionComparator>(filter.d_ptr->_comparatorValue) == QMessageDataComparator::Excludes) {
                    negate = !negate;
                }
                result = (statusFlags & value);
            } else {
                qWarning("matchesMessage: Unhandled restriction criteria, status comparator type");
                break;
            }
            break;
        }
        case QMessageFilterPrivate::Id: {
            QStringList strIds(filter.d_ptr->_value.toStringList());
            QMessageId messageId(message.id());
            QMessageIdList ids;
            foreach(QString str, strIds) {
                ids.append(QMessageId(str));
            }
            result = ids.contains(messageId);
            if (filter.d_ptr->_comparatorType != QMessageFilterPrivate::Inclusion) {
                qWarning("matchesMessage: Unhandled restriction criteria, id comparator type");
            }
            if (static_cast<QMessageDataComparator::InclusionComparator>(filter.d_ptr->_comparatorValue) == QMessageDataComparator::Excludes) {
                result = !result;
            }
            break;
        }
        case QMessageFilterPrivate::ParentAccountId: // fall through
        case QMessageFilterPrivate::Type: // fall through
        case QMessageFilterPrivate::ParentFolderId: // fall through
        case QMessageFilterPrivate::AncestorFolderIds: // fall through
            // These should all be satisfied (and toplevel with complements fully distributed hence no negation)
            return true;
            break;
        }
    }
    } //end switch

    if (negate)
        result = !result;
    return result;
}

MapiRestriction::MapiRestriction(const QMessageFilter &aFilter)
    :_notRestriction(0),
     _recipientRestriction(0),
     _keyProps(0),
     _restrictions(0),
     _recordKeys(0),
     _buffer(0),
     _buffer2(0),
     _valid(false),
     _empty(false),
     _left(0),
     _right(0)
{
    QMessageFilter filter;
    QMessageFilterPrivate *d_ptr(QMessageFilterPrivate::implementation(aFilter));

    if (!d_ptr->_valid) {
        qWarning("Invalid filter application ignored.");
        return;
    }
    if (d_ptr->_matchFlags & QMessageDataComparator::MatchFullWord) {
        qWarning("Full word matching not supported on MAPI platforms.");
        return;
    }

    if (!QMessageFilterPrivate::restrictionPermitted(aFilter)) {
        _valid = true;
        _empty = true;
        return;
    }

    if (d_ptr->_field == QMessageFilterPrivate::Sender) {
        QString value(d_ptr->_value.toString());
        switch (d_ptr->_comparatorType) {
        case QMessageFilterPrivate::Equality: {
            filter = QMessageFilterPrivate::bySender(value, static_cast<QMessageDataComparator::EqualityComparator>(d_ptr->_comparatorValue));
            break;
        }
        case QMessageFilterPrivate::Inclusion: {
            filter = QMessageFilterPrivate::bySender(value, static_cast<QMessageDataComparator::InclusionComparator>(d_ptr->_comparatorValue));
            break;
        }
        default: { // Relation
            qWarning("Unhandled restriction criteria");
            return;
        }
        }
        if (d_ptr->_operator == QMessageFilterPrivate::Not) {
            // Can't complement sender restriction
            qWarning("Unhandled restriction criteria");
            return;
        }
        d_ptr = QMessageFilterPrivate::implementation(filter);
    } else {
        filter = aFilter;
    }

    if (d_ptr->_operator != QMessageFilterPrivate::Identity) {
        switch (d_ptr->_operator) {
        case QMessageFilterPrivate::Not: // fall through
        case QMessageFilterPrivate::Nand: // fall through
        case QMessageFilterPrivate::Nor:
            _notRestriction = new SRestriction;
            _notRestriction->rt = RES_NOT;
            _notRestriction->res.resNot.ulReserved = 0;
            _notRestriction->res.resNot.lpRes = &_restriction;
            break;
        default:
            break;
        } //end switch
        switch (d_ptr->_operator) {
        case QMessageFilterPrivate::Not: {
            if (d_ptr->_field == QMessageFilterPrivate::None) {
                _restriction.rt = RES_EXIST;
                _restriction.res.resExist.ulReserved1 = 0;
                _restriction.res.resExist.ulPropTag = PR_ENTRYID; // Should match all, so not this is a non-matching filter
                _restriction.res.resExist.ulReserved2 = 0;
                _valid = true;
                return;
            }
            break;
        }
        case QMessageFilterPrivate::Nand: // fall through
        case QMessageFilterPrivate::And: {
            Q_ASSERT(d_ptr->_left);
            Q_ASSERT(d_ptr->_right);
            if (!d_ptr->_left || !d_ptr->_right)
                return;
            _left = new MapiRestriction(*d_ptr->_left);
            _right = new MapiRestriction(*d_ptr->_right);
            _subRestriction[0] = *_left->sRestriction();
            _subRestriction[1] = *_right->sRestriction();
            _restriction.rt = RES_AND;
            _restriction.res.resAnd.cRes = 2;
            _restriction.res.resAnd.lpRes = &_subRestriction[0];
            _valid = true;
            return;
        }
        case QMessageFilterPrivate::Nor: // fall through
        case QMessageFilterPrivate::Or: {
            Q_ASSERT(d_ptr->_left);
            Q_ASSERT(d_ptr->_right);
            if (!d_ptr->_left || !d_ptr->_right)
                return;
            _left = new MapiRestriction(*d_ptr->_left);
            _right = new MapiRestriction(*d_ptr->_right);
            _subRestriction[0] = *_left->sRestriction();
            _subRestriction[1] = *_right->sRestriction();
            _restriction.rt = RES_OR;
            _restriction.res.resOr.cRes = 2;
            _restriction.res.resOr.lpRes = &_subRestriction[0];
            _valid = true;
            return;
        }
        default:
            Q_ASSERT(false);
            qWarning("Unsupported filter boolean algebra case.");
        } // end switch
    } else { // identity operator
        if (d_ptr->_field == QMessageFilterPrivate::None) {
            _valid = true;
            _empty = true;
            return;
        }
    }

    // Identity or Not filter type

    // Handle Recipients as a special case
#if 0 // This MAPI Restriction is producing incorrect results
    if ((d_ptr->_field == QMessageFilterPrivate::RecipientName)
       || (d_ptr->_field == QMessageFilterPrivate::RecipientAddress)) {

        _restriction.rt = RES_SUBRESTRICTION;
        _restriction.res.resSub.ulSubObject = PR_MESSAGE_RECIPIENTS;
        _restriction.res.resSub.lpRes = _recipientRestriction;
        _recipientRestriction = new SRestriction;
        _recipientRestriction->rt = RES_AND;
        _recipientRestriction->res.resAnd.cRes = 2;
        _recipientRestriction->res.resAnd.lpRes = &_subRestriction[0];
        _restriction.res.resSub.lpRes = _recipientRestriction;

        _subRestriction[0].rt = RES_EXIST;
        _subRestriction[0].res.resExist.ulReserved1 = 0;
        _subRestriction[0].res.resExist.ulReserved2 = 0;
   
        _subRestriction[1].rt = RES_CONTENT;
        _subRestriction[1].res.resContent.lpProp = &_keyProp;
        if (d_ptr->_field == QMessageFilterPrivate::RecipientName) {
            _subRestriction[0].res.resExist.ulPropTag = PR_DISPLAY_NAME;
            _subRestriction[1].res.resContent.ulPropTag = PR_DISPLAY_NAME;
            _keyProp.ulPropTag = PR_DISPLAY_NAME;
        } else { // RecipientsAddress
            _subRestriction[0].res.resExist.ulPropTag = PR_SMTP_ADDRESS;
            _subRestriction[1].res.resContent.ulPropTag = PR_SMTP_ADDRESS; // PR_EMAIL_ADDRESS returns unsatisfactory results
            _keyProp.ulPropTag = PR_SMTP_ADDRESS;
        }

        QStringToWCharArray(d_ptr->_value.toString(), &_buffer); 
        _keyProp.Value.LPSZ = _buffer;

        bool negation(false);
        switch (d_ptr->_comparatorType) {
        case QMessageFilterPrivate::Equality: {
            _subRestriction[1].res.resContent.ulFuzzyLevel = FL_FULLSTRING;
            if (static_cast<QMessageDataComparator::EqualityComparator>(d_ptr->_comparatorValue) != QMessageDataComparator::Equal)
                negation = true;
            break;
        }
        case QMessageFilterPrivate::Inclusion: {
            // There is a flaw for Inclusion/Exclusion here,
            // If the search string includes a '<' so that both name and address are specified
            // FL_PREFIX should be used for the address, and (a non-existent) Fl_SUFFIX for name.
            _subRestriction[1].res.resContent.ulFuzzyLevel = FL_SUBSTRING;
            if (static_cast<QMessageDataComparator::InclusionComparator>(d_ptr->_comparatorValue) == QMessageDataComparator::Excludes)
                negation = true;
            break;
        }
        default: { // Relation
            qWarning("Unhandled restriction criteria");
            return;
        }
        }
        if (negation) {
            complement();
        }

        if ((d_ptr->_matchFlags & QMessageDataComparator::MatchCaseSensitive) == 0) {
            _subRestriction[1].res.resContent.ulFuzzyLevel |= FL_IGNORECASE;
        }

        _valid = true;
        return;
    }
#endif

    // Handle Subject and/or Sender as a special case
    if ((d_ptr->_field == QMessageFilterPrivate::Subject)
        || (d_ptr->_field == QMessageFilterPrivate::SenderName)
        || (d_ptr->_field == QMessageFilterPrivate::SenderAddress)) {

        _restriction.rt = RES_CONTENT;
        _restriction.res.resContent.lpProp = &_keyProp;

        if (d_ptr->_field == QMessageFilterPrivate::Subject) {
            _restriction.res.resContent.ulPropTag = PR_SUBJECT;
            _keyProp.ulPropTag = PR_SUBJECT;
        } else if (d_ptr->_field == QMessageFilterPrivate::SenderName) {
            _restriction.res.resContent.ulPropTag = PR_SENDER_NAME;
            _keyProp.ulPropTag = PR_SENDER_NAME;
        } else { // SenderAddress
            _restriction.res.resContent.ulPropTag = PR_SENDER_EMAIL_ADDRESS;
            _keyProp.ulPropTag = PR_SENDER_EMAIL_ADDRESS;
        }

        QString subj(d_ptr->_value.toString());
        QStringToWCharArray(subj, &_buffer); 
        _keyProp.Value.LPSZ = _buffer;

        bool negation(false);
        switch (d_ptr->_comparatorType) {
        case QMessageFilterPrivate::Equality: {
            _restriction.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
            if (static_cast<QMessageDataComparator::EqualityComparator>(d_ptr->_comparatorValue) != QMessageDataComparator::Equal)
                negation = true;
            break;
        }
        case QMessageFilterPrivate::Inclusion: {
            _restriction.res.resContent.ulFuzzyLevel = FL_SUBSTRING;
            if (static_cast<QMessageDataComparator::InclusionComparator>(d_ptr->_comparatorValue) == QMessageDataComparator::Excludes)
                negation = true;
            break;
        }
        default: { // Relation
            qWarning("Unhandled restriction criteria");
            return;
        }
        }

        if (negation) {
            complement();
        }

        if ((d_ptr->_matchFlags & QMessageDataComparator::MatchCaseSensitive) == 0)
            _restriction.res.resContent.ulFuzzyLevel |= FL_IGNORECASE;

        _valid = true;
        return;
    }

    switch (d_ptr->_comparatorType) {
    case QMessageFilterPrivate::Equality:
    case QMessageFilterPrivate::Relation: {
        _restriction.rt = RES_PROPERTY;
        if (d_ptr->_comparatorType == QMessageFilterPrivate::Equality) {
            QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(d_ptr->_comparatorValue));
            if (cmp == QMessageDataComparator::Equal)
                _restriction.res.resProperty.relop = RELOP_EQ;
            else
                _restriction.res.resProperty.relop = RELOP_NE;
        } else { // Relation
            QMessageDataComparator::RelationComparator cmp(static_cast<QMessageDataComparator::RelationComparator>(d_ptr->_comparatorValue));
            switch (cmp) {
            case QMessageDataComparator::LessThan:
                _restriction.res.resProperty.relop = RELOP_LT;
                break;
            case QMessageDataComparator::LessThanEqual:
                _restriction.res.resProperty.relop = RELOP_LE;
                break;
            case QMessageDataComparator::GreaterThan:
                _restriction.res.resProperty.relop = RELOP_GT;
                break;
            case QMessageDataComparator::GreaterThanEqual:
                _restriction.res.resProperty.relop = RELOP_GE;
                break;
            }
        }
        _restriction.res.resProperty.lpProp = &_keyProp;
        switch (d_ptr->_field) {
        case QMessageFilterPrivate::Size: {
            _restriction.res.resProperty.ulPropTag = PR_MESSAGE_SIZE;
            _keyProp.ulPropTag = PR_MESSAGE_SIZE;
            _keyProp.Value.l = d_ptr->_value.toInt();
            _valid = true;
            break;
        }
        case QMessageFilterPrivate::ReceptionTimeStamp: {
            _restriction.res.resProperty.ulPropTag = PR_MESSAGE_DELIVERY_TIME;
            _keyProp.ulPropTag = PR_MESSAGE_DELIVERY_TIME;
            QDateTime dt(d_ptr->_value.toDateTime());
            QDateTimeToFileTime(dt, &_keyProp.Value.ft);
            _valid = true;
            break;
        }
        case QMessageFilterPrivate::TimeStamp: {
            _restriction.res.resProperty.ulPropTag = PR_CLIENT_SUBMIT_TIME;
            _keyProp.ulPropTag = PR_CLIENT_SUBMIT_TIME;
            QDateTime dt(d_ptr->_value.toDateTime());
            QDateTimeToFileTime(dt, &_keyProp.Value.ft);
            _valid = true;
            break;
        }
        case QMessageFilterPrivate::Priority: {
            _restriction.res.resProperty.ulPropTag = PR_PRIORITY;
            _keyProp.ulPropTag = PR_PRIORITY;
            QMessage::Priority priority(static_cast<QMessage::Priority>(d_ptr->_value.toInt()));
            switch (priority) { // TODO: Double check that priority filtering is working
            case QMessage::HighPriority:
                _keyProp.Value.l = PRIO_URGENT;
                break;
            case QMessage::NormalPriority:
                _keyProp.Value.l = PRIO_NORMAL;
                break;
            case QMessage::LowPriority:
                _keyProp.Value.l = PRIO_NONURGENT;
                break;
            default:
                qWarning("Unknown priority encountered during filter processing");
                return;
            }
            _valid = true;
            break;
        }
        default:
            qWarning("Unhandled restriction criteria");
        }
        break;
    }
    case QMessageFilterPrivate::Inclusion: {
        QMessageDataComparator::InclusionComparator cmp(static_cast<QMessageDataComparator::InclusionComparator>(d_ptr->_comparatorValue));
        if (cmp == QMessageDataComparator::Excludes) {
            complement();
        }
        if (d_ptr->_field == QMessageFilterPrivate::Id) {
            QStringList ids(d_ptr->_value.toStringList());
#ifdef _WIN32_WCE
            _recordKeys = new MapiEntryId[ids.count()];
#else
            _recordKeys = new MapiRecordKey[ids.count()];
#endif
            _keyProps = new SPropValue[ids.count()];
            _restrictions = new SRestriction[ids.count()];

            _restriction.rt = RES_OR;
            _restriction.res.resOr.cRes = ids.count();
            _restriction.res.resOr.lpRes = &_restrictions[0];
            for (int i = 0; i < ids.count(); ++i) {
#ifdef _WIN32_WCE
                _recordKeys[i] = QMessageIdPrivate::entryId(QMessageId(ids[i]));
#else
                _recordKeys[i] = QMessageIdPrivate::messageRecordKey(QMessageId(ids[i]));
#endif
                _keyProps[i].ulPropTag = PR_RECORD_KEY;
                _keyProps[i].Value.bin.cb = _recordKeys[i].count();
                _keyProps[i].Value.bin.lpb = reinterpret_cast<LPBYTE>(const_cast<char*>(_recordKeys[i].data()));
                _restrictions[i].rt = RES_PROPERTY;
                _restrictions[i].res.resProperty.relop = RELOP_EQ;
                _restrictions[i].res.resProperty.ulPropTag = PR_RECORD_KEY;
                _restrictions[i].res.resProperty.lpProp = &_keyProps[i];
            }
            _valid = true;
            return;
        }
        if (d_ptr->_field == QMessageFilterPrivate::Status) {
            _restriction.rt = RES_BITMASK;
            QMessage::Status status(static_cast<QMessage::Status>(d_ptr->_value.toUInt()));
            switch (status) {
            case QMessage::Incoming:
                // Restriction is not possible in this case
                _valid = false;
                return;
            case QMessage::Read:
                _restriction.res.resBitMask.relBMR = BMR_NEZ;
                _restriction.res.resBitMask.ulPropTag = PR_MESSAGE_FLAGS;
                _restriction.res.resBitMask.ulMask = MSGFLAG_READ;
                _valid = true;
                return;
            case QMessage::Removed:
                _restriction.res.resBitMask.relBMR = BMR_NEZ;
                _restriction.res.resBitMask.ulPropTag = PR_MSG_STATUS;
                _restriction.res.resBitMask.ulMask = MSGSTATUS_DELMARKED; // Untested
                _valid = true;
                return;
            case QMessage::HasAttachments:
#ifdef _WIN32_WCE
                _restriction.rt = RES_EXIST;
                _restriction.res.resExist.ulReserved1 = 0;
                _restriction.res.resExist.ulPropTag = PR_HASATTACH;
                _restriction.res.resExist.ulReserved2 = 0;
#else
                _restriction.res.resBitMask.relBMR = BMR_NEZ;
                _restriction.res.resBitMask.ulPropTag = PR_MESSAGE_FLAGS;
                _restriction.res.resBitMask.ulMask = MSGFLAG_HASATTACH;
#endif
                _valid = true;
                return;
            default:
                qWarning("Unimplemented status filter"); // Has attachments not done
                return;
            }
        }
        qWarning("Unhandled restriction criteria");
        break;
    }
    }
}

void MapiRestriction::complement()
{
    if (_notRestriction) { // double negative
        delete _notRestriction;
        _notRestriction = 0;
    } else {
        _notRestriction = new SRestriction;
        _notRestriction->rt = RES_NOT;
        _notRestriction->res.resNot.ulReserved = 0;
        _notRestriction->res.resNot.lpRes = &_restriction;
    }

}

MapiRestriction::~MapiRestriction()
{
    delete _notRestriction;
    _notRestriction = 0;
    delete _recipientRestriction;
    _recipientRestriction = 0;
    delete [] _recordKeys;
    _recordKeys = 0;
    delete [] _keyProps;
    _keyProps = 0;
    delete [] _restrictions;
    _restrictions = 0;
    delete _left;
    _left = 0;
    delete _right;
    _right = 0;
    delete _buffer;
    _buffer = 0;
    delete _buffer2;
    _buffer2 = 0;
}

SRestriction *MapiRestriction::sRestriction()
{
    if (_notRestriction)
        return _notRestriction;
    return &_restriction;
}

QMessageFilterPrivate::QMessageFilterPrivate(QMessageFilter *messageFilter)
    :q_ptr(messageFilter),
     _field(None),
     _comparatorType(Equality),
     _comparatorValue(QMessageDataComparator::Equal),
     _operator(Identity),
     _left(0),
     _right(0),
     _valid(true),
#ifdef _WIN32_WCE
     _matchesRequired(true),
     _restrictionPermitted(false),
#else
     _matchesRequired(false),
     _restrictionPermitted(true),
#endif
    _messageFilter(0),
    _accountFilter(0),
    _folderFilter(0),
     _complex(false)
{
}

QMessageFilterPrivate::~QMessageFilterPrivate()
{
    delete _left;
    _left = 0;
    delete _right;
    _right = 0;
    delete _messageFilter;
    _messageFilter = 0;
    delete _accountFilter;
    _accountFilter = 0;
    delete _folderFilter;
    _folderFilter = 0;
}

bool QMessageFilterPrivate::containerFiltersAreEmpty()
{
    return (_standardFoldersInclude.isEmpty() 
            && _standardFoldersExclude.isEmpty() 
            && _accountsInclude.isEmpty() 
            && _accountsExclude.isEmpty()
            && _parentInclude.isEmpty() 
            && _parentExclude.isEmpty()
            && _ancestorInclude.isEmpty() 
            && _ancestorExclude.isEmpty());
}

bool QMessageFilterPrivate::nonContainerFiltersAreEmpty()
{
    return ((_field == QMessageFilterPrivate::None) 
        && (_operator == QMessageFilterPrivate::Identity));
}

QMessageFilter QMessageFilterPrivate::containerFiltersPart()
{
    QMessageFilter result;
    result.d_ptr->_standardFoldersInclude = _standardFoldersInclude;
    result.d_ptr->_standardFoldersExclude = _standardFoldersExclude;
    result.d_ptr->_accountsInclude = _accountsInclude;
    result.d_ptr->_accountsExclude = _accountsExclude;
    result.d_ptr->_parentInclude = _parentInclude;
    result.d_ptr->_parentExclude = _parentExclude;
    result.d_ptr->_ancestorInclude = _ancestorInclude;
    result.d_ptr->_ancestorExclude = _ancestorExclude;
    return result;
}

QMessageFilter QMessageFilterPrivate::nonContainerFiltersPart()
{
    QMessageFilter result;
    result.d_ptr->_matchFlags = _matchFlags;
    result.d_ptr->_field = _field;
    result.d_ptr->_value = _value;
    result.d_ptr->_comparatorType = _comparatorType;
    result.d_ptr->_comparatorValue = _comparatorValue;
    result.d_ptr->_operator = _operator;
    if (_left)
        result.d_ptr->_left = new QMessageFilter(*_left);
    if (_right)
        result.d_ptr->_right = new QMessageFilter(*_right);
    result.d_ptr->_matchesRequired = _matchesRequired;
    result.d_ptr->_restrictionPermitted = _restrictionPermitted;
    if (_messageFilter)
        result.d_ptr->_messageFilter = new QMessageFilter(*_messageFilter);
    if (_accountFilter)
        result.d_ptr->_accountFilter = new QMessageAccountFilter(*_accountFilter);
    if (_folderFilter)
        result.d_ptr->_folderFilter = new QMessageFolderFilter(*_folderFilter);
    result.d_ptr->_valid = _valid;
    result.d_ptr->_complex = _complex;
    return result;
}

QMessageFilter::QMessageFilter()
    :d_ptr(new QMessageFilterPrivate(this))
{
}

QMessageFilter::QMessageFilter(const QMessageFilter &other)
    :d_ptr(new QMessageFilterPrivate(this))
{
    this->operator=(other);
}

QMessageFilter::~QMessageFilter()
{
    delete d_ptr;
    d_ptr = 0;
}

QMessageFilter& QMessageFilter::operator=(const QMessageFilter& other)
{
    if (&other == this)
        return *this;
    delete d_ptr->_left;
    d_ptr->_left = 0;
    delete d_ptr->_right;
    d_ptr->_right = 0;
    d_ptr->_matchFlags = other.d_ptr->_matchFlags;
    d_ptr->_field = other.d_ptr->_field;
    d_ptr->_value = other.d_ptr->_value;
    d_ptr->_comparatorType = other.d_ptr->_comparatorType;
    d_ptr->_comparatorValue = other.d_ptr->_comparatorValue;
    d_ptr->_operator = other.d_ptr->_operator;
    d_ptr->_valid = other.d_ptr->_valid;
    d_ptr->_standardFoldersInclude = other.d_ptr->_standardFoldersInclude;
    d_ptr->_standardFoldersExclude = other.d_ptr->_standardFoldersExclude;
    d_ptr->_accountsInclude = other.d_ptr->_accountsInclude;
    d_ptr->_accountsExclude = other.d_ptr->_accountsExclude;
    d_ptr->_parentInclude = other.d_ptr->_parentInclude;
    d_ptr->_parentExclude = other.d_ptr->_parentExclude;
    d_ptr->_ancestorInclude = other.d_ptr->_ancestorInclude;
    d_ptr->_ancestorExclude = other.d_ptr->_ancestorExclude;
    d_ptr->_complex = other.d_ptr->_complex;
    d_ptr->_matchesRequired = other.d_ptr->_matchesRequired;
    d_ptr->_restrictionPermitted = other.d_ptr->_restrictionPermitted;
    delete d_ptr->_messageFilter;
    d_ptr->_messageFilter = 0;
    delete d_ptr->_accountFilter;
    d_ptr->_accountFilter = 0;
    delete d_ptr->_folderFilter;
    d_ptr->_folderFilter = 0;
    if (other.d_ptr->_messageFilter)
        d_ptr->_messageFilter = new QMessageFilter(*other.d_ptr->_messageFilter);
    if (other.d_ptr->_accountFilter)
        d_ptr->_accountFilter = new QMessageAccountFilter(*other.d_ptr->_accountFilter);
    if (other.d_ptr->_folderFilter)
        d_ptr->_folderFilter = new QMessageFolderFilter(*other.d_ptr->_folderFilter);

    if (other.d_ptr->_left)
        d_ptr->_left = new QMessageFilter(*other.d_ptr->_left);
    if (other.d_ptr->_right)
        d_ptr->_right = new QMessageFilter(*other.d_ptr->_right);
    return *this;
}

void QMessageFilter::setMatchFlags(QMessageDataComparator::MatchFlags matchFlags)
{
    d_ptr->_matchFlags = matchFlags;
    if (d_ptr->_matchFlags & QMessageDataComparator::MatchFullWord) {
        qWarning("Full word matching not supported on MAPI platforms.");
        d_ptr->_valid = false;
    } else {
        if (d_ptr->_left)
            d_ptr->_left->setMatchFlags(matchFlags);
        if (d_ptr->_right)
            d_ptr->_right->setMatchFlags(matchFlags);
    }
}

QMessageDataComparator::MatchFlags QMessageFilter::matchFlags() const
{
    return d_ptr->_matchFlags;
}

bool QMessageFilter::isEmpty() const
{
    return d_ptr->nonContainerFiltersAreEmpty() && d_ptr->containerFiltersAreEmpty();
}

bool QMessageFilter::isSupported() const
{
    return d_ptr->_valid;
}

QMessageFilter QMessageFilter::operator~() const
{
    QMessageFilter result;
    if (d_ptr->containerFiltersAreEmpty() && !d_ptr->_complex) {
        // Simple case of a native filter
        result = *this;
        int op = static_cast<int>(d_ptr->_operator) + static_cast<int>(QMessageFilterPrivate::Not);
        op = op % static_cast<int>(QMessageFilterPrivate::OperatorEnd);
        result.d_ptr->_operator = static_cast<QMessageFilterPrivate::Operator>(op);
        if (QMessageFilterPrivate::containsSenderSubfilter(*this)) {
            result.d_ptr->_restrictionPermitted = false; // Can't simply complement sender restriction
        }
    } else if (d_ptr->_complex) {
        // A filter can be in one of two forms, either
        // 1) An account and/or standard folder restriction &'d with a 'native' filter part 
        //  that can be evaluated using matchesMessage (or better a MAPI SRestriction).
        // or 2) a 'complex' filter part that consists of two subparts |'d together
        //     at least one of which is non-native. That is at least one subpart is either
        //     complex itself, or has an account and/or standard folder restriction.
        //
        // On Windows, only account and folder filters are non-native, i.e. can't be evaluated 
        //  using a MAPI SRestriction.
        // 
        // So ~(X|Y)) -> ~X&~Y -> and & will transform further

        if (!d_ptr->containerFiltersAreEmpty())
            qWarning("Complex filter has non empty container filter part");
        result = ~*d_ptr->_left & ~*d_ptr->_right;
    } else {
        QMessageFilter tmp;
        result = ~QMessageFilter();
        if (!d_ptr->_standardFoldersInclude.isEmpty() || !d_ptr->_standardFoldersExclude.isEmpty()) {
            tmp.d_ptr->_standardFoldersInclude = d_ptr->_standardFoldersExclude;
            tmp.d_ptr->_standardFoldersExclude = d_ptr->_standardFoldersInclude;
            result |= tmp;
            tmp = QMessageFilter();
        }
        if (!d_ptr->_accountsInclude.isEmpty() || !d_ptr->_accountsExclude.isEmpty()) {
            tmp.d_ptr->_accountsInclude = d_ptr->_accountsExclude;
            tmp.d_ptr->_accountsExclude = d_ptr->_accountsInclude;
            result |= tmp;
            tmp = QMessageFilter();
        }
        if (!d_ptr->_parentInclude.isEmpty() || !d_ptr->_parentExclude.isEmpty()) {
            tmp.d_ptr->_parentInclude = d_ptr->_parentExclude;
            tmp.d_ptr->_parentExclude = d_ptr->_parentInclude;
            result |= tmp;
            tmp = QMessageFilter();
        }
        if (!d_ptr->_ancestorInclude.isEmpty() || !d_ptr->_ancestorExclude.isEmpty()) {
            tmp.d_ptr->_ancestorInclude = d_ptr->_ancestorExclude;
            tmp.d_ptr->_ancestorExclude = d_ptr->_ancestorInclude;
            result |= tmp;
        }
        result |= ~d_ptr->nonContainerFiltersPart();
    }
    return result;
}

QMessageFilter QMessageFilter::operator&(const QMessageFilter& other) const
{
    QMessageFilter result(*this);
    result &= other;
    return result;
}

QMessageFilter QMessageFilter::operator|(const QMessageFilter& other) const
{
    QMessageFilter result(*this);
    result |= other;
    return result;
}

const QMessageFilter& QMessageFilter::operator&=(const QMessageFilter& other)
{
    QMessageFilter result;
    if (!d_ptr->_valid || !other.d_ptr->_valid) {
        result.d_ptr->_valid = false;
        *this = result;
        return *this;
    }
    if (&other == this)
        return *this;
    if (isEmpty()) {
        *this = other;
        return *this;
    }
    if (other.isEmpty())
        return *this;
    if (QMessageFilterPrivate::isNonMatching(*this)) {
        return *this;
    }
    if (QMessageFilterPrivate::isNonMatching(other)) {
        *this = other;
        return *this;
    }

    if (!d_ptr->_complex && !other.d_ptr->_complex) {
        // A&B
        // intersect includes and union excludes

        // empty include set means include all
        if (this->d_ptr->_standardFoldersInclude.isEmpty()) {
            result.d_ptr->_standardFoldersInclude = other.d_ptr->_standardFoldersInclude;
        } else {
            result.d_ptr->_standardFoldersInclude = this->d_ptr->_standardFoldersInclude;
            if (!other.d_ptr->_standardFoldersInclude.isEmpty()) {
                result.d_ptr->_standardFoldersInclude &= other.d_ptr->_standardFoldersInclude;
                if (result.d_ptr->_standardFoldersInclude.isEmpty()) {  // non-matching
                    *this = ~QMessageFilter();
                    return *this;
                }
            }
        }
        result.d_ptr->_standardFoldersExclude = this->d_ptr->_standardFoldersExclude;
        result.d_ptr->_standardFoldersExclude |= other.d_ptr->_standardFoldersExclude;

        if (this->d_ptr->_accountsInclude.isEmpty()) {
            result.d_ptr->_accountsInclude = other.d_ptr->_accountsInclude;
        } else {
            result.d_ptr->_accountsInclude = this->d_ptr->_accountsInclude;
            if (!other.d_ptr->_accountsInclude.isEmpty()) {
                result.d_ptr->_accountsInclude &= other.d_ptr->_accountsInclude;
                if (result.d_ptr->_accountsInclude.isEmpty()) { // non-matching
                    *this = ~QMessageFilter();
                    return *this;
                }
            }
        }
        result.d_ptr->_accountsExclude = this->d_ptr->_accountsExclude;
        result.d_ptr->_accountsExclude |= other.d_ptr->_accountsExclude;

        if (this->d_ptr->_parentInclude.isEmpty()) {
            result.d_ptr->_parentInclude = other.d_ptr->_parentInclude;
        } else {
            result.d_ptr->_parentInclude = this->d_ptr->_parentInclude;
            if (!other.d_ptr->_parentInclude.isEmpty()) {
                result.d_ptr->_parentInclude &= other.d_ptr->_parentInclude;
                if (result.d_ptr->_parentInclude.isEmpty()) { // non-matching
                    *this = ~QMessageFilter();
                    return *this;
                }
            }
        }
        result.d_ptr->_parentExclude = this->d_ptr->_parentExclude;
        result.d_ptr->_parentExclude |= other.d_ptr->_parentExclude;

        if (this->d_ptr->_ancestorInclude.isEmpty()) {
            result.d_ptr->_ancestorInclude = other.d_ptr->_ancestorInclude;
        } else {
            result.d_ptr->_ancestorInclude = this->d_ptr->_ancestorInclude;
            if (!other.d_ptr->_ancestorInclude.isEmpty()) {
                result.d_ptr->_ancestorInclude &= other.d_ptr->_ancestorInclude;
                if (result.d_ptr->_ancestorInclude.isEmpty()) { // non-matching
                    *this = ~QMessageFilter();
                    return *this;
                }
            }
        }
        result.d_ptr->_ancestorExclude = this->d_ptr->_ancestorExclude;
        result.d_ptr->_ancestorExclude |= other.d_ptr->_ancestorExclude;

        if (this->d_ptr->nonContainerFiltersPart().isEmpty() 
            || other.d_ptr->nonContainerFiltersPart().isEmpty()) {
            // Degenerate case, empty this or other nonContainerFiltersPart can be thrown away
            if (this->d_ptr->nonContainerFiltersPart().isEmpty()) {
                *this = other;
            } // else throw away empty other nonContainerFiltersPart

            // Just update the containerFiltersPart
            d_ptr->_standardFoldersInclude = result.d_ptr->_standardFoldersInclude;
            d_ptr->_standardFoldersExclude = result.d_ptr->_standardFoldersExclude;
            d_ptr->_accountsInclude = result.d_ptr->_accountsInclude;
            d_ptr->_accountsExclude = result.d_ptr->_accountsExclude;
            d_ptr->_parentInclude = result.d_ptr->_parentInclude;
            d_ptr->_parentExclude = result.d_ptr->_parentExclude;
            d_ptr->_ancestorInclude = result.d_ptr->_ancestorInclude;
            d_ptr->_ancestorExclude = result.d_ptr->_ancestorExclude;
        } else {
            QMessageFilter *left(new QMessageFilter(this->d_ptr->nonContainerFiltersPart()));
            QMessageFilter *right(new QMessageFilter(other.d_ptr->nonContainerFiltersPart()));
            *this = result;
            d_ptr->_left = left;
            d_ptr->_right = right;
            d_ptr->_operator = QMessageFilterPrivate::And;
        }
    } else if (d_ptr->_complex) { // other maybe complex
        // (X|Y)&Z -> X&Z | Y&Z  Query optimizer is not a priority
        result.d_ptr->_left = new QMessageFilter(*this->d_ptr->_left & other); // recursive evaluation
        result.d_ptr->_right = new QMessageFilter(*this->d_ptr->_right & other); // recursive evaluation
        result.d_ptr->_operator = QMessageFilterPrivate::Or;
        result.d_ptr->_complex = true;
        *this = result;
    } else { // this is not complex, other is complex
        // A&(X|Y) -> A&X | A&Y  Query optimizer is not a priority
        result.d_ptr->_left = new QMessageFilter(*this & *other.d_ptr->_left); // recursive evaluation
        result.d_ptr->_right = new QMessageFilter(*this & *other.d_ptr->_right); // recursive evaluation
        result.d_ptr->_operator = QMessageFilterPrivate::Or;
        result.d_ptr->_complex = true;
        *this = result;
    }
    return *this;
}

const QMessageFilter& QMessageFilter::operator|=(const QMessageFilter& other)
{
    QMessageFilter result;
    if (!d_ptr->_valid || !other.d_ptr->_valid) {
        result.d_ptr->_valid = false;
        *this = result;
        return *this;
    }
    if (&other == this)
        return *this;
    if (isEmpty())
        return *this;
    if (other.isEmpty()) {
        *this = other;
        return *this;
    }
    if (QMessageFilterPrivate::isNonMatching(*this)) {
        *this = other;
        return *this;
    }
    if (QMessageFilterPrivate::isNonMatching(other)) {
        return *this;
    }

    if (!d_ptr->_complex 
        && !other.d_ptr->_complex 
        && (d_ptr->containerFiltersPart() == other.d_ptr->containerFiltersPart())) {
        // F&A|F&B - > F&(A|B)
        QMessageFilter *left(new QMessageFilter(this->d_ptr->nonContainerFiltersPart()));
        QMessageFilter *right(new QMessageFilter(other.d_ptr->nonContainerFiltersPart()));
        result.d_ptr->_standardFoldersInclude = d_ptr->_standardFoldersInclude;
        result.d_ptr->_standardFoldersExclude = d_ptr->_standardFoldersExclude;
        result.d_ptr->_accountsInclude = d_ptr->_accountsInclude;
        result.d_ptr->_accountsExclude = d_ptr->_accountsExclude;
        result.d_ptr->_parentInclude = d_ptr->_parentInclude;
        result.d_ptr->_parentExclude = d_ptr->_parentExclude;
        result.d_ptr->_ancestorInclude = d_ptr->_ancestorInclude;
        result.d_ptr->_ancestorExclude = d_ptr->_ancestorExclude;
        *this = result;
        d_ptr->_left = left;
        d_ptr->_right = right;
        d_ptr->_operator = QMessageFilterPrivate::Or;
    // } else { Could have special case for neither complex and for both nonContainerFiltersIsEmpty is true
    } else {
        // X|Y
        QMessageFilter *left(new QMessageFilter(*this));
        QMessageFilter *right(new QMessageFilter(other));
        *this = result;
        d_ptr->_left = left;
        d_ptr->_right = right;
        d_ptr->_operator = QMessageFilterPrivate::Or;
        d_ptr->_complex = true;
    }

    return *this;
}

bool QMessageFilter::operator==(const QMessageFilter& other) const
{
    if (d_ptr->_standardFoldersInclude != other.d_ptr->_standardFoldersInclude)
        return false;
    if (d_ptr->_standardFoldersExclude != other.d_ptr->_standardFoldersExclude)
        return false;
    if (d_ptr->_accountsInclude != other.d_ptr->_accountsInclude)
        return false;
    if (d_ptr->_accountsExclude != other.d_ptr->_accountsExclude)
        return false;
    if (d_ptr->_parentInclude != other.d_ptr->_parentInclude)
        return false;
    if (d_ptr->_parentExclude != other.d_ptr->_parentExclude)
        return false;
    if (d_ptr->_ancestorInclude != other.d_ptr->_ancestorInclude)
        return false;
    if (d_ptr->_ancestorExclude != other.d_ptr->_ancestorExclude)
        return false;
    if (d_ptr->_complex != other.d_ptr->_complex)
        return false;

    if (other.d_ptr->_operator != d_ptr->_operator)
        return false;

    if (other.d_ptr->_matchesRequired != d_ptr->_matchesRequired)
        return false;
    if (other.d_ptr->_restrictionPermitted != d_ptr->_restrictionPermitted)
        return false;
    if (other.d_ptr->_messageFilter || d_ptr->_messageFilter) {
        if (!other.d_ptr->_messageFilter
            || !d_ptr->_messageFilter
            || !(*other.d_ptr->_messageFilter == *d_ptr->_messageFilter))
            return false;
    }
    if (other.d_ptr->_accountFilter || d_ptr->_accountFilter) {
        if (!other.d_ptr->_accountFilter
            || !d_ptr->_accountFilter
            || !(*other.d_ptr->_accountFilter == *d_ptr->_accountFilter))
            return false;
    }
    if (other.d_ptr->_folderFilter || d_ptr->_folderFilter) {
        if (!other.d_ptr->_folderFilter
            || !d_ptr->_folderFilter
            || !(*other.d_ptr->_folderFilter == *d_ptr->_folderFilter))
            return false;
    }

    if (d_ptr->_operator == QMessageFilterPrivate::Identity) {
        if (other.d_ptr->_operator != QMessageFilterPrivate::Identity)
            return false;
        return (d_ptr->_field == other.d_ptr->_field
            && d_ptr->_value == other.d_ptr->_value
            && d_ptr->_comparatorType == other.d_ptr->_comparatorType
            && d_ptr->_comparatorValue == other.d_ptr->_comparatorValue);
    }

    if (d_ptr->_left == other.d_ptr->_left 
        && d_ptr->_right == other.d_ptr->_right)
        return true;
    if (d_ptr->_left == other.d_ptr->_right 
        && d_ptr->_right == other.d_ptr->_left)
        return true; // Commutativity

    // TODO: For a system of determining equality of boolean algebra expressions see:
    // TODO:  Completely distributed normal form http://wikpedia.org/wiki/Boolean_algebra(logic)
    return false;
}

QMessageFilter QMessageFilter::byId(const QMessageId &id, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageDataComparator::InclusionComparator inclCmp(QMessageDataComparator::Includes);
    if (cmp == QMessageDataComparator::NotEqual)
        inclCmp = QMessageDataComparator::Excludes;
    QMessageIdList ids;
    ids << id;

    return QMessageFilter::byId(ids, inclCmp);
}

QMessageFilter QMessageFilter::byId(const QMessageIdList &ids, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    if (QMessageDataComparator::Includes == cmp)
        result = ~QMessageFilter();

    if (ids.isEmpty()) {
        return result;
    }

#ifdef _WIN32_WCE
    QMap<MapiEntryId, QStringList> storeIds;
    foreach(QMessageId id, ids)
        storeIds[QMessageIdPrivate::storeRecordKey(id)].append(id.toString());

    QMapIterator<MapiEntryId, QStringList> i(storeIds);
#else
    QMap<MapiRecordKey, QStringList> storeIds;
    foreach(QMessageId id, ids)
        storeIds[QMessageIdPrivate::storeRecordKey(id)].append(id.toString());

    QMapIterator<MapiRecordKey, QStringList> i(storeIds);
#endif
    while (i.hasNext()) {
        i.next();
        if (QMessageDataComparator::Includes == cmp) {
            QMessageFilter tmp(QMessageFilter::byParentAccountId(QMessageAccountIdPrivate::from(i.key())));
            tmp &= QMessageFilterPrivate::from(QMessageFilterPrivate::Id, QVariant(i.value()), cmp);
            result |= tmp;
        } else {
            QMessageFilter tmp(QMessageFilter::byParentAccountId(QMessageAccountIdPrivate::from(i.key()), QMessageDataComparator::NotEqual));
            tmp |= QMessageFilterPrivate::from(QMessageFilterPrivate::Id, QVariant(i.value()), QMessageDataComparator::Excludes);
            result &= tmp;
        }
    }
    
    return result;
}

QMessageFilter QMessageFilter::byId(const QMessageFilter &filter, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = QMessageFilterPrivate::MessageFilter;
    result.d_ptr->_messageFilter = new QMessageFilter(filter);
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

// For the type filters the assumption is made that there is one store, default SMS store (QMessageAccount) that contains
//  only SMS messages, and all other stores (QMessageAccounts) contain only email messages.
QMessageFilter QMessageFilter::byType(QMessage::Type type, QMessageDataComparator::EqualityComparator cmp)
{
    if (cmp == QMessageDataComparator::Equal)
        return QMessageFilter::byType(type, QMessageDataComparator::Includes);
    return QMessageFilter::byType(type, QMessageDataComparator::Excludes);
}

QMessageFilter QMessageFilter::byType(QMessage::TypeFlags aType, QMessageDataComparator::InclusionComparator cmp)
{
    QMessage::TypeFlags type(aType & (QMessage::Sms | QMessage::Email)); // strip Mms, InstantMessage
    if (type == QMessage::Sms) {
        if (cmp == QMessageDataComparator::Includes) {
            return QMessageFilter::byParentAccountId(QMessageAccount::defaultAccount(QMessage::Sms), QMessageDataComparator::Equal);
        } else {
            return QMessageFilter::byParentAccountId(QMessageAccount::defaultAccount(QMessage::Sms), QMessageDataComparator::NotEqual);
        }
    }
    if (type == QMessage::Email) {
        if (cmp == QMessageDataComparator::Includes) {
            return QMessageFilter::byParentAccountId(QMessageAccount::defaultAccount(QMessage::Sms), QMessageDataComparator::NotEqual);
        } else {
            return QMessageFilter::byParentAccountId(QMessageAccount::defaultAccount(QMessage::Sms), QMessageDataComparator::Equal);
        }
    }
    if (type == (QMessage::Sms | QMessage::Email)) {
        if (cmp == QMessageDataComparator::Includes)
            return QMessageFilter(); // inclusion, match all
        return ~QMessageFilter(); // exclusion, match none
    }
    // Mms/InstantMessage only
    if (cmp == QMessageDataComparator::Includes)
        return ~QMessageFilter(); // mms only inclusion, match none
    return QMessageFilter(); // mms only exclusion, match all
}

QMessageFilter QMessageFilter::bySender(const QString &value, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::Sender, QVariant(value), cmp));
    if (cmp != QMessageDataComparator::Equal)
        result.d_ptr->_restrictionPermitted = false;
    result.d_ptr->_matchesRequired = true;
    return result;
}

QMessageFilter QMessageFilterPrivate::bySender(const QString &value, QMessageDataComparator::EqualityComparator cmp)
{
    // Note: An additional matchesMessages check is reqruired, this rule is too liberal and results in a superset or the correct result set being returned
    QString sender(value);
    QString name;
    QString address;
    QString suffix;
    bool startDelimeterFound;
    QMessageAddress::parseEmailAddress(sender, &name, &address, &suffix, &startDelimeterFound);
    if (startDelimeterFound) {
        QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(address), QMessageDataComparator::Equal));
        // An exact match requires a Suffix comparison (which is not supported by MAPI) rather than just an Includes
        // Furthermore this seems to trigger some kind of MAPI restriction bug, results are being missed, so comment out for now
        // result &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderName, QVariant(name), QMessageDataComparator::Equal);
        if (cmp == QMessageDataComparator::Equal) {
            return result;
        } else {
            return ~result;
        }
    } else {
        // value could be name or address, both are set by parseEmailAddress to the same value
        QMessageFilter result1(QMessageFilterPrivate::from(QMessageFilterPrivate::SenderName, QVariant(name), QMessageDataComparator::Equal));
        // Seems to trigger some kind of MAPI restriction bug, results are being missed, so comment out for now
        //result1 &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(""), QMessageDataComparator::Equal);
        QMessageFilter result2(QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(address), QMessageDataComparator::Equal));
        // Seems to trigger some kind of MAPI restriction bug, results are being missed, so comment out for now
        //result2 &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderName, QVariant(""), QMessageDataComparator::Equal);
        if (cmp == QMessageDataComparator::Equal) {
            return result1 | result2;
        } else {
            return ~(result1 | result2);
        }
    }
}

QMessageFilter QMessageFilter::bySender(const QString &value, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::Sender, QVariant(value), cmp));
    result.d_ptr->_matchesRequired = true;
    if (cmp != QMessageDataComparator::Includes)
        result.d_ptr->_restrictionPermitted = false;
    return result;
}

QMessageFilter QMessageFilterPrivate::bySender(const QString &value, QMessageDataComparator::InclusionComparator cmp)
{
    // Note: An additional matchesMessages check is reqruired, this rule is too liberal and results in a superset or the correct result set being returned
    if (value.isEmpty()) {
        if (cmp == QMessageDataComparator::Includes)
            return QMessageFilter();
        return ~QMessageFilter();
    }

    QString sender(value);
    QString name;
    QString address;
    QString suffix;
    bool startDelimeterFound;
    bool endDelimeterFound;
    QMessageAddress::parseEmailAddress(sender, &name, &address, &suffix, &startDelimeterFound, &endDelimeterFound);
    if (startDelimeterFound) {

        QMessageFilter result;
        if (endDelimeterFound) {
            result &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(address), QMessageDataComparator::Equal);
        } else {
            result &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(address), QMessageDataComparator::Includes);
        }

        // An exact match requires a Suffix comparison (which is not supported by MAPI) rather than just an Includes
        // Furthermore this seems to trigger some kind of MAPI restriction bug, results are being missed, so comment out for now
        // result &= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderName, QVariant(name), QMessageDataComparator::Includes);

        if (cmp == QMessageDataComparator::Includes) {
            return result;
        } else {
            return ~result;
        }
    } else {
        QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::SenderName, QVariant(name), QMessageDataComparator::Includes));
        result |= QMessageFilterPrivate::from(QMessageFilterPrivate::SenderAddress, QVariant(address), QMessageDataComparator::Includes);
        if (cmp == QMessageDataComparator::Includes) {
            return result;
        } else {
            return ~result;
        }
    }
}

QMessageFilter QMessageFilter::byRecipients(const QString &value, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::Recipients, QVariant(value), cmp));
    // Can't evaluate recipients filter using native MAPI restriction
    result.d_ptr->_restrictionPermitted = false;
    result.d_ptr->_matchesRequired = true;
    return result;

    // Unable to get sensible results yet from code path below
#if 0
    if (value.isEmpty()) {
        if (cmp == QMessageDataComparator::Includes)
            return QMessageFilter();
        return ~QMessageFilter();
    }

    QString recipient(value);
    QString name;
    QString address;
    QString suffix;
    bool startDelimeterFound;
    bool endDelimeterFound;
    QMessageAddress::parseEmailAddress(recipient, &name, &address, &suffix, &startDelimeterFound, &endDelimeterFound);
    if (startDelimeterFound) {

        QMessageFilter result;
        if (endDelimeterFound) {
            result &= QMessageFilterPrivate::from(QMessageFilterPrivate::RecipientAddress, QVariant(address), QMessageDataComparator::Equal);
        } else {
            result &= QMessageFilterPrivate::from(QMessageFilterPrivate::RecipientAddress, QVariant(address), QMessageDataComparator::Includes);
        }

        // Need additional matches check, this should be a Suffix comparison rather than just an Includes
        result &= QMessageFilterPrivate::from(QMessageFilterPrivate::RecipientName, QVariant(name), QMessageDataComparator::Includes);

        if (cmp == QMessageDataComparator::Includes) {
            return result;
        } else {
            return ~result;
        }
    } else {
        QMessageFilter result(QMessageFilterPrivate::from(QMessageFilterPrivate::RecipientName, QVariant(name), QMessageDataComparator::Includes));
        result |= QMessageFilterPrivate::from(QMessageFilterPrivate::RecipientAddress, QVariant(address), QMessageDataComparator::Includes);
        if (cmp == QMessageDataComparator::Includes) {
            return result;
        } else {
            return ~result;
        }
    }
#endif
}

QMessageFilter QMessageFilter::bySubject(const QString &value, QMessageDataComparator::EqualityComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Subject, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::bySubject(const QString &value, QMessageDataComparator::InclusionComparator cmp)
{
    if (value.isEmpty()) {
        if (cmp == QMessageDataComparator::Includes)
            return QMessageFilter();
        return ~QMessageFilter();
    }
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Subject, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byTimeStamp(const QDateTime &value, QMessageDataComparator::EqualityComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::TimeStamp, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byTimeStamp(const QDateTime &value, QMessageDataComparator::RelationComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::TimeStamp, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byReceptionTimeStamp(const QDateTime &value, QMessageDataComparator::EqualityComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::ReceptionTimeStamp, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byReceptionTimeStamp(const QDateTime &value, QMessageDataComparator::RelationComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::ReceptionTimeStamp, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byStatus(QMessage::Status value, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageDataComparator::InclusionComparator comparator(QMessageDataComparator::Excludes);
    if (cmp == QMessageDataComparator::Equal)
        comparator = QMessageDataComparator::Includes;
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Status, QVariant(value), comparator);
}

QMessageFilter QMessageFilter::byStatus(QMessage::StatusFlags mask, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    QMessageDataComparator::EqualityComparator comparator(QMessageDataComparator::NotEqual);
    if (cmp == QMessageDataComparator::Includes) {
        comparator = QMessageDataComparator::Equal;
    }
    if (mask & QMessage::Incoming) {
        result &= QMessageFilter::byStatus(QMessage::Incoming, comparator);

        // We can't use a restriction to enforce this filter
        result.d_ptr->_restrictionPermitted = false;
        result.d_ptr->_matchesRequired = true;
    }
    if (mask & QMessage::Read) {
        result &= QMessageFilter::byStatus(QMessage::Read, comparator);
    }
    if (mask & QMessage::Removed) {
        result &= QMessageFilter::byStatus(QMessage::Removed, comparator);
    }
    if (mask & QMessage::HasAttachments) {
        result &= QMessageFilter::byStatus(QMessage::HasAttachments, comparator);
    }
    if (result.isEmpty()) // Be consistent with QMF, but seems wrong. TODO verify correctness
        return ~result;
    return result;
}

QMessageFilter QMessageFilter::byPriority(QMessage::Priority value, QMessageDataComparator::EqualityComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Priority, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::bySize(int value, QMessageDataComparator::EqualityComparator cmp)
{
    // TODO: Test this filter
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Size, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::bySize(int value, QMessageDataComparator::RelationComparator cmp)
{
    return QMessageFilterPrivate::from(QMessageFilterPrivate::Size, QVariant(value), cmp);
}

QMessageFilter QMessageFilter::byParentAccountId(const QMessageAccountId &id, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageFilter result;
    if (cmp == QMessageDataComparator::Equal)
        result.d_ptr->_accountsInclude += id;
    else
        result.d_ptr->_accountsExclude += id;
    return result;
}

QMessageFilter QMessageFilter::byParentAccountId(const QMessageAccountFilter &filter, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = QMessageFilterPrivate::AccountFilter;
    result.d_ptr->_accountFilter = new QMessageAccountFilter(filter);
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

QMessageFilter QMessageFilter::byStandardFolder(QMessage::StandardFolder folder, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageFilter result;
    if (cmp == QMessageDataComparator::Equal)
        result.d_ptr->_standardFoldersInclude += folder;
    else
        result.d_ptr->_standardFoldersExclude += folder;
    return result;
}

QMessageFilter QMessageFilter::byParentFolderId(const QMessageFolderId &id, QMessageDataComparator::EqualityComparator cmp)
{
    QMessageFilter result;
#ifndef QSTRING_FOLDER_ID
    result.d_ptr->_parentInclude += id;
#else
    QMessageFolder folder(id);
    result.d_ptr->_parentInclude += folder.parentAccountId().toString() + "/" + folder.path();
#endif
    // An invalid id is a special case, assume it means any top-level folder (with root as parent) in any MAPI store
    if (id.isValid()) {
        result &= QMessageFilter::byParentAccountId(QMessageAccountIdPrivate::from(QMessageFolderIdPrivate::storeRecordKey(id)));
    }
    if (cmp != QMessageDataComparator::Equal) {
        result = ~result;
    }
    return result;
}

QMessageFilter QMessageFilter::byParentFolderId(const QMessageFolderFilter &filter, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = QMessageFilterPrivate::FolderFilter;
    result.d_ptr->_folderFilter = new QMessageFolderFilter(filter);
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

QMessageFilter QMessageFilter::byAncestorFolderIds(const QMessageFolderId &id, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    if (id.isValid()) {
#ifndef QSTRING_FOLDER_ID
        result.d_ptr->_ancestorInclude += id;
#else
        QMessageFolder folder(id);
        result.d_ptr->_ancestorInclude += folder.parentAccountId().toString() + "/" + folder.path();
#endif
    } else {
        result = ~result;
    }
    if (cmp != QMessageDataComparator::Includes) {
        result = ~result;
    }
    return result;
}

QMessageFilter QMessageFilter::byAncestorFolderIds(const QMessageFolderFilter &filter, QMessageDataComparator::InclusionComparator cmp)
{
    QMessageFilter result;
    result.d_ptr->_field = QMessageFilterPrivate::AncestorFilter;
    result.d_ptr->_folderFilter = new QMessageFolderFilter(filter);
    result.d_ptr->_comparatorValue = static_cast<int>(cmp);
    return result;
}

void QMessageFilterPrivate::debug(const QMessageFilter &filter, const QString &indent)
{
    QString operatorStr;
    QString fieldStr;
    QStringList standardFoldersInclude;
    QStringList standardFoldersExclude;
    QStringList accountsInclude;
    QStringList accountsExclude;
    QStringList fields;
    fields << "None" << "Id" << "Type" << "Sender" << "SenderName" << "SenderAddress" << "Recipients" << "RecipientName" << "RecipientAddress" << "Subject" << "TimeStamp" << "ReceptionTimeStamp" << "Status" << "Priority" << "Size" << "ParentAccountId" << "ParentFolderId" << "AncestorFolderIds" << "MessageFilter" << "AccountFilter" << "FolderFilter" << "AncestorFilter";
    QStringList operators;
    operators << "Identity" << "And" << "Or" << "Not" << "Nand" << "Nor";
    qDebug() << indent << "field" << fields[filter.d_ptr->_field] << "operator" << operators[filter.d_ptr->_operator] << "complex" << filter.d_ptr->_complex;

    qDebug() << indent << "parentInclude" << filter.d_ptr->_parentInclude.count() << "parentExclude" << filter.d_ptr->_parentExclude.count()
        << "accountsInclude" << filter.d_ptr->_accountsInclude.count() << "accountsExclude" << filter.d_ptr->_accountsExclude.count()
        << "ancestorInclude" << filter.d_ptr->_ancestorInclude.count() << "ancestorExclude" << filter.d_ptr->_ancestorExclude.count()
        << "standardFoldersIncludeInclude" << filter.d_ptr->_standardFoldersInclude.count() << "standardFoldersIncludeExclude" << filter.d_ptr->_standardFoldersExclude.count();

    QStringList standards;
    standards << "InboxFolder" << "OutboxFolder" << "DraftsFolder" << "SentFolder" << "TrashFolder";
    QStringList sfi, sfe;
    foreach(QMessage::StandardFolder std, filter.d_ptr->_standardFoldersInclude)
        sfi.append(standards[std-1]);
    foreach(QMessage::StandardFolder std, filter.d_ptr->_standardFoldersExclude)
        sfe.append(standards[std-1]);
    if (!sfi.isEmpty() || !sfe.isEmpty())
        qDebug() << indent << "sfi" << sfi << "sfe" << sfe;
    if (filter.d_ptr->_left) {
        qDebug() << indent << "left";
        QMessageFilterPrivate::debug(*filter.d_ptr->_left, indent + " ");
    }
    if (filter.d_ptr->_right) {
        qDebug() << indent << "right";
        QMessageFilterPrivate::debug(*filter.d_ptr->_right, indent + " ");
    }
}

QTM_END_NAMESPACE
