/*
 * rs-core/src/dbase: rsexpr.cc
 *
 * RetroShare C++ Interface.
 *
 * Copyright 2007-2008 by Kashif Kaleem.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */


#include "retroshare/rsexpr.h"
#include "retroshare/rstypes.h"
#include <algorithm>
#include <functional>

/******************************************************************************************
eval functions of relational expressions. 

******************************************************************************************/

namespace RsRegularExpression
{
template<>
void RelExpression<int>::linearize(LinearizedExpression& e) const
{
    e._ints.push_back(Op) ;
    e._ints.push_back(LowerValue) ;
    e._ints.push_back(HigherValue) ;
}


bool DateExpression::eval(const ExpFileEntry& file)
{
    return evalRel(file.file_modtime());
}

bool SizeExpressionMB::eval(const ExpFileEntry& file)
{
    return evalRel((int)(file.file_size()/(uint64_t)(1024*1024)));
}

bool SizeExpression::eval(const ExpFileEntry& file)
{
    return evalRel(file.file_size());
}

bool PopExpression::eval(const ExpFileEntry& file)
{
    return evalRel(file.file_popularity());
}

/******************************************************************************************
Code for evaluating string expressions

******************************************************************************************/

bool NameExpression::eval(const ExpFileEntry& file)
{
    return evalStr(file.file_name());
}

bool PathExpression::eval(const ExpFileEntry& file)
{
    return evalStr(file.file_parent_path());
}

bool ExtExpression::eval(const ExpFileEntry& file)
{
    std::string ext;
    /*Get the part of the string after the last instance of . in the filename */
    size_t index = file.file_name().find_last_of('.');
    if (index != std::string::npos) {
        ext = file.file_name().substr(index+1);
        if (ext != "" ){
            return evalStr(ext);
        }
    }
    return false;
}

bool HashExpression::eval(const ExpFileEntry& file){
    return evalStr(file.file_hash().toStdString());
}

/*Check whether two strings are 'equal' to each other*/
static bool StrEquals(const std::string & str1, const std::string & str2, 
                      bool IgnoreCase ){
    if ( str1.size() != str2.size() ){
        return false;
    } else if (IgnoreCase) {
        std::equal( str1.begin(), str1.end(),
                    str2.begin(), CompareCharIC() );
    }
    return std::equal( str1.begin(), str1.end(),
                       str2.begin());
}

/*Check whether one string contains the other*/
static bool StrContains( const std::string & str1, const std::string & str2,
                         bool IgnoreCase){

    std::string::const_iterator iter ;
    if (IgnoreCase) {
        iter = std::search( str1.begin(), str1.end(),
                            str2.begin(), str2.end(), CompareCharIC() );
    } else {
        iter = std::search( str1.begin(), str1.end(),
                            str2.begin(), str2.end());
    }

    return ( iter != str1.end() );
}


bool StringExpression :: evalStr ( const std::string &str ){
    std::list<std::string>::iterator iter;
    switch (Op) {
    case ContainsAllStrings:
        for ( iter = terms.begin(); iter != terms.end(); ++iter ) {
            if ( StrContains (str, *iter, IgnoreCase) == false ){
                return false;
            }
        }
        return true;
        break;
    case ContainsAnyStrings:
        for ( iter = terms.begin(); iter != terms.end(); ++iter ) {
            if ( StrContains (str,*iter, IgnoreCase) == true ) {
                return true;
            }
        }
        break;
    case EqualsString:
        for ( iter = terms.begin(); iter != terms.end(); ++iter ) {
            if ( StrEquals (str,*iter, IgnoreCase) == true ) {
                return true;
            }
        }
        break;
    default:
        return false;
    }
    return false;
}

/*************************************************************************
 * linearization code
 *************************************************************************/

void CompoundExpression::linearize(LinearizedExpression& e) const
{
    e._tokens.push_back(LinearizedExpression::EXPR_COMP) ;
    e._ints.push_back(Op) ;

    Lexp->linearize(e) ;
    Rexp->linearize(e) ;
}

void StringExpression::linearize(LinearizedExpression& e) const
{
    e._ints.push_back(Op) ;
    e._ints.push_back(IgnoreCase) ;
    e._ints.push_back(terms.size()) ;

    for(std::list<std::string>::const_iterator it(terms.begin());it!=terms.end();++it)
        e._strings.push_back(*it) ;
}

Expression *LinearizedExpression::toExpr(const LinearizedExpression& e) 
{
    int i=0,j=0,k=0 ;
    return toExpr(e,i,j,k) ;
}

void LinearizedExpression::readStringExpr(const LinearizedExpression& e,int& n_ints,int& n_strings,std::list<std::string>& strings,bool& b,StringOperator& op) 
{
    op = static_cast<StringOperator>(e._ints[n_ints++]) ;
    b = e._ints[n_ints++] ;
    int n = e._ints[n_ints++] ;

    strings.clear() ;
    for(int i=0;i<n;++i)
        strings.push_back(e._strings[n_strings++]) ;
}

Expression *LinearizedExpression::toExpr(const LinearizedExpression& e,int& n_tok,int& n_ints,int& n_strings) 
{
    LinearizedExpression::token tok = static_cast<LinearizedExpression::token>(e._tokens[n_tok++]) ;

    switch(tok)
    {
    case EXPR_DATE:  {
        RelOperator op = static_cast<RelOperator>(e._ints[n_ints++]) ;
        int lv = e._ints[n_ints++] ;
        int hv = e._ints[n_ints++] ;

        return new DateExpression(op,lv,hv) ;
    }

    case EXPR_POP:	  {
        RelOperator op = static_cast<RelOperator>(e._ints[n_ints++]) ;
        int lv = e._ints[n_ints++] ;
        int hv = e._ints[n_ints++] ;

        return new PopExpression(op,lv,hv) ;
    }
    case EXPR_SIZE:  {
        RelOperator op = static_cast<RelOperator>(e._ints[n_ints++]) ;
        int lv = e._ints[n_ints++] ;
        int hv = e._ints[n_ints++] ;

        return new SizeExpression(op,lv,hv) ;
    }
    case EXPR_HASH:	{
        std::list<std::string> strings ;
        StringOperator op ;
        bool b ;

        readStringExpr(e,n_ints,n_strings,strings,b,op) ;
        return new HashExpression(op,strings) ;
    }
    case EXPR_NAME:	{
        std::list<std::string> strings ;
        StringOperator op ;
        bool b ;

        readStringExpr(e,n_ints,n_strings,strings,b,op) ;

        return new NameExpression(op,strings,b) ;
    }
    case EXPR_PATH: {
        std::list<std::string> strings ;
        StringOperator op ;
        bool b ;

        readStringExpr(e,n_ints,n_strings,strings,b,op) ;

        return new ExtExpression(op,strings,b) ;
    }
    case EXPR_EXT: {
        std::list<std::string> strings ;
        StringOperator op ;
        bool b ;

        readStringExpr(e,n_ints,n_strings,strings,b,op) ;

        return new ExtExpression(op,strings,b) ;
    }
    case EXPR_COMP:	{
        LogicalOperator op = static_cast<LogicalOperator>(e._ints[n_ints++]) ;

        Expression *e1 = toExpr(e,n_tok,n_ints,n_strings) ;
        Expression *e2 = toExpr(e,n_tok,n_ints,n_strings) ;

        return new CompoundExpression(op,e1,e2) ;
    }
    case EXPR_SIZE_MB: {
        RelOperator op = static_cast<RelOperator>(e._ints[n_ints++]) ;
        int lv = e._ints[n_ints++] ;
        int hv = e._ints[n_ints++] ;

        return new SizeExpressionMB(op,lv,hv) ;
    }
    default:
        std::cerr << "No expression match the current value " << tok << std::endl ;
        return NULL ;
    }
}


}
