/* -*- c++ -*-
 *
 * searchquery.cpp
 *
 * Copyright (C) 2003-2004 Petter Stokke <ummo@hellokitty.com>
 * Copyright (C) 2003-2004 Sebastian Sauer <mail@dipe.org>
 * Copyright (C) 2010 Gioacchino Mazzurco <gmazzurco89@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "searchquery.h"
#include "donkeymessage.h"

#include <QRegExp>
#include <QStringList>
#include <kdebug.h>
#include "donkeytypes.h"

/*** SearchQuery ***/

SearchQuery::SearchQuery(Operation op)
{
    setOperation(op);
}

SearchQuery::~SearchQuery()
{
}

SearchQuery::Operation SearchQuery::operation()
{
    return Op;
}

void SearchQuery::setOperation(Operation op)
{
    Op = op;
}

const QString SearchQuery::getQuerystring()
{
    kDebug() << "SearchQuery::getQuerystring() NOT IMPLEMENTED !!!";
    return QString::null;
}

void SearchQuery::writeQuery(DonkeyMessage& msg)
{
    msg.writeInt8((int8)Op);
}

SearchQuery* SearchQuery::getQuery(DonkeyMessage* msg)
{
    int op = msg->readInt8();
    switch (op) {
        case And: {
            QueryAnd* q = new QueryAnd();
            int i, j = msg->readInt16();
            for (i=0; i<j; i++) q->append( getQuery(msg) );
            return q;
        } break;
        case Or: {
            QueryOr* q = new QueryOr();
            int i, j = msg->readInt16();
            for (i=0; i<j; i++) q->append( getQuery(msg) );
            return q;
        } break;
        case Hidden: {
            QueryHidden* q = new QueryHidden();
            int i, j = msg->readInt16();
            for (i=0; i<j; i++) q->append( getQuery(msg) );
            return q;
        } break;
        case AndNot: {
            return new QueryAndNot(getQuery(msg), getQuery(msg));
        } break;
        case Module: {
            QString s = msg->readString();
            SearchQuery* q = getQuery(msg);
            return new QueryModule(s, q);
        } break;
        case Keywords: {
            return new QueryKeywords(msg->readString(), msg->readString());
        } break;
        case MinSize: {
            return new QueryMinSize(msg->readString(), msg->readString());
        } break;
        case MaxSize: {
            return new QueryMaxSize(msg->readString(), msg->readString());
        } break;
        case Format: {
            return new QueryFormat(msg->readString(), msg->readString());
        } break;
        case Media: {
            return new QueryMedia(msg->readString(), msg->readString());
        } break;
        case Mp3Artist: {
            return new QueryMp3Artist(msg->readString(), msg->readString());
        } break;
        case Mp3Title: {
            return new QueryMp3Title(msg->readString(), msg->readString());
        } break;
        case Mp3Album: {
            return new QueryMp3Album(msg->readString(), msg->readString());
        } break;
        case Mp3Bitrate: {
            return new QueryMp3Bitrate(msg->readString(), msg->readString());
        } break;
        default: {
            return 0;
        } break;
    }
}

SearchQuery* SearchQuery::getQuery(const QString& querystring)
{
    // First seek for SearchQueryList's
    QRegExp rxlist("(\\(.*\\))[\\s]*(AND|AND[\\s]*NOT|OR)[\\s]*(\\(.*\\))");
    int pos = rxlist.indexIn(querystring);
    if(pos >= 0) {
        QString op = rxlist.cap(2).trimmed().simplified();
        if(op == "AND") {
            QueryAnd* q = new QueryAnd();
            q->append( getQuery(rxlist.cap(1)) );
            q->append( getQuery(rxlist.cap(3)) );
            return q;
        }
        if(op == "OR") {
            QueryOr* q = new QueryOr();
            q->append( getQuery(rxlist.cap(1)) );
            q->append( getQuery(rxlist.cap(3)) );
            return q;
        }
        if(op == "AND NOT")
            return new QueryAndNot(getQuery(rxlist.cap(1)), getQuery(rxlist.cap(3)));

        kDebug() << "SearchQuery::getQuery() Unknown list-operator " << op << " in " << querystring;
        return 0;
    }

    // Clean the querystring
    QRegExp rxq("^[\\s]*[\\(]+(.*)[\\)]+[\\s]*$");
    QString qs = (rxq.indexIn(querystring) >= 0) ? rxq.cap(1) : querystring;

    // Look for [...]>0 or [...]<0
    QRegExp rx1("\\[(.*)\\][\\s]*(>|<)[\\s]*\\[([0-9]+)\\]");
    if(rx1.indexIn(qs) >= 0) {
        QString op = rx1.cap(1).trimmed();
        if(op == "size") {
            QString sizeop = rx1.cap(2).trimmed();
            if(sizeop == ">")
                return new QueryMinSize(QString::null, rx1.cap(3));
            else if(sizeop == "<")
                return new QueryMaxSize(QString::null, rx1.cap(3));
            else
                kDebug() << "SearchQuery::getQuery() Unknown size-operator " << sizeop << " in " << qs;
        }
        if(op == "bitrate")
            return new QueryMp3Bitrate(QString::null, rx1.cap(3));

        kDebug() << "SearchQuery::getQuery() Unknown integer-operator " << op << " in " << qs;
        return 0;
    }

    // look for [...]CONTAINS[...]
    QRegExp rx2("\\[(.*)\\][\\s]*CONTAINS[\\s]*\\[(.*)\\]");
    if(rx2.indexIn(qs) >= 0) {
        QString op = rx2.cap(1).trimmed();
        if(op == "format")
            return new QueryFormat(QString::null, rx2.cap(2));
        if(op == "type")
            return new QueryMedia(QString::null, rx2.cap(2));
        if(op == "artist")
            return new QueryMp3Artist(QString::null, rx2.cap(2));
        if(op == "album")
            return new QueryMp3Album(QString::null, rx2.cap(2));
        if(op == "title")
            return new QueryMp3Title(QString::null, rx2.cap(2));

        kDebug() << "SearchQuery::getQuery() Unknown contains-operator " << op << " in " << qs;
        return 0;
    }

    // and finally look for CONTAINS[...]
    QRegExp rx3("CONTAINS[\\s]*\\[(.*)\\]");
    if(rx3.indexIn(qs) >= 0)
        return new QueryKeywords(QString::null, rx3.cap(1));

    kDebug() << "SearchQuery::getQuery() Unknown querystring " << qs;
    return 0;
}

/*** SearchQueryList ***/

SearchQueryList::SearchQueryList(Operation op)
    : SearchQuery(op)
{
}


SearchQueryList::~SearchQueryList()
{
    qDeleteAll(queryList);
}

SearchQueryList* SearchQueryList::append(SearchQuery* q)
{
    queryList.append(q);
    return this;
}

uint SearchQueryList::count()
{
    return queryList.count();
}

SearchQuery* SearchQueryList::at(uint index)
{
    return queryList.at(index);
}

SearchQuery* SearchQueryList::take(uint index)
{
    return queryList.takeAt(index);
}

const QString SearchQueryList::getQuerystring()
{
    kDebug() << "SearchQueryList::getQuerystring() NOT IMPLEMENTED !!!";
    return QString::null;
}

const QString SearchQueryList::toQueryString(const QString& joinstr)
{
    QStringList qs;
    foreach (SearchQuery *q, queryList) {
        qs << '(' + q->getQuerystring() + ')';
    }
    return qs.join(' ' + joinstr + ' ');
}

void SearchQueryList::writeQuery(DonkeyMessage& msg)
{
    SearchQuery::writeQuery(msg);
    msg.writeInt16((int16)queryList.count());
    foreach (SearchQuery *q, queryList) {
        q->writeQuery(msg);
    }
}

/*** QueryAnd ***/

QueryAnd::QueryAnd()
    : SearchQueryList(SearchQuery::And)
{
}

const QString QueryAnd::getQuerystring()
{
    return toQueryString("AND");
}

/*** QueryOr ***/

QueryOr::QueryOr()
    : SearchQueryList(SearchQuery::Or)
{
}

const QString QueryOr::getQuerystring()
{
    return toQueryString("OR");
}

/*** QueryHidden ***/

QueryHidden::QueryHidden()
    : SearchQueryList(SearchQuery::Hidden)
{
}

const QString QueryHidden::getQuerystring()
{
    return toQueryString("HIDDEN");
}

/*** QueryAndNot ***/

QueryAndNot::QueryAndNot(SearchQuery* q1, SearchQuery* q2)
    : SearchQuery(SearchQuery::AndNot)
{
    query1 = q1; query2 = q2;
}

QueryAndNot::~QueryAndNot()
{
    delete query1; delete query2;
}

const QString QueryAndNot::getQuerystring()
{
    return "(" + query1->getQuerystring() + ") AND NOT (" + query2->getQuerystring() + ")";
}

void QueryAndNot::writeQuery(DonkeyMessage& msg)
{
    SearchQuery::writeQuery(msg);
    query1->writeQuery(msg);
    query2->writeQuery(msg);
}

/*** QueryModule ***/

QueryModule::QueryModule(QString str, SearchQuery* q)
    : SearchQuery(SearchQuery::Module)
    , string(str)
{
    query = q;
}

QueryModule::~QueryModule()
{
    delete query;
}

const QString QueryModule::getQuerystring()
{
    return query->getQuerystring();
}

void QueryModule::writeQuery(DonkeyMessage& msg)
{
    SearchQuery::writeQuery(msg);
    msg.writeString(string);
    query->writeQuery(msg);
}

/*** SearchQueryTwoStrings ***/

SearchQueryTwoStrings::SearchQueryTwoStrings(Operation op, QString str1, QString str2)
    : SearchQuery(op)
    , s1(str1)
    , s2(str2)
{
}

const QString SearchQueryTwoStrings::getQuerystring()
{
    kDebug() << "SearchQueryTwoStrings::getQuerystring() NOT IMPLEMENTED !!!";
    return QString::null;
}

void SearchQueryTwoStrings::writeQuery(DonkeyMessage& msg)
{
    SearchQuery::writeQuery(msg);
    msg.writeString(s1);
    msg.writeString(s2);
}

/*** QueryKeywords ***/

QueryKeywords::QueryKeywords(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Keywords, s1, s2)
{
}

const QString QueryKeywords::getQuerystring()
{
    return "CONTAINS[" + s2 + "]";
}

/*** QueryMinSize ***/

QueryMinSize::QueryMinSize(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::MinSize, s1, s2)
{
}

const QString QueryMinSize::getQuerystring()
{
    return "[size]>[" + s2 + "]";
}

/*** QueryMaxSize ***/

QueryMaxSize::QueryMaxSize(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::MaxSize, s1, s2)
{
}

const QString QueryMaxSize::getQuerystring()
{
    return "[size]<[" + s2 + "]";
}

/*** QueryFormat ***/

QueryFormat::QueryFormat(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Format, s1, s2)
{
}

const QString QueryFormat::getQuerystring()
{
    return "[format]CONTAINS[" + s2 + "]";
}

/*** QueryMedia ***/

QueryMedia::QueryMedia(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Media, s1, s2)
{
}

const QString QueryMedia::getQuerystring()
{
    return "[type]CONTAINS[" + s2 + "]";
}

/*** QueryMp3Artist ***/

QueryMp3Artist::QueryMp3Artist(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Mp3Artist, s1, s2)
{
}

const QString QueryMp3Artist::getQuerystring()
{
    return "[artist]CONTAINS[" + s2 + "]";
}

/*** QueryMp3Title ***/

QueryMp3Title::QueryMp3Title(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Mp3Title, s1, s2)
{
}

const QString QueryMp3Title::getQuerystring()
{
    return "[title]CONTAINS[" + s2 + "]";
}

/*** QueryMp3Album ***/

QueryMp3Album::QueryMp3Album(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Mp3Album, s1, s2)
{
}

const QString QueryMp3Album::getQuerystring()
{
    return "[album]CONTAINS[" + s2 + "]";
}

/*** QueryMp3Bitrate ***/

QueryMp3Bitrate::QueryMp3Bitrate(QString s1, QString s2)
    : SearchQueryTwoStrings(SearchQuery::Mp3Bitrate, s1, s2)
{
}

const QString QueryMp3Bitrate::getQuerystring()
{
    return "[bitrate]>[" + s2 + "]";
}


