#include <cstring>
#include "AdvFuncParser.hh"

const std::string AdvFuncParser::className = std::string("AdvFuncParser");

/**
 *  default constructor
 */
/*
AdvFuncParser::AdvFuncParser() {
    createBuildInFuncList();
    //funcNameList=&(*(new std::vector<std::string>()));
}
*/

/**
 *  constructor
 *
 * \param[in] expr a string parsed
 */
AdvFuncParser::AdvFuncParser(const std::string expr) {
    createBuildInFuncList();
    this->expr=expr;
    this->i=0U;
}

/**
 *  constructor
 *
 * \param[in] expr a string parsed. this must be null-terminated.
 */
/*
AdvFuncParser::AdvFuncParser(const Char *expr) {
    createBuildInFuncList();
    setExpr(expr);
}
*/

/**
 * destructor
 */
AdvFuncParser::~AdvFuncParser() {
}

void AdvFuncParser::createBuildInFuncList() {
    std::string memberName=std::string("createBuildInFuncList()");

    this->buildInFuncList = new std::vector<AdvFuncBase*>();

    /* for peak fit */
    this->buildInFuncList->push_back(new AdvConstant());
    this->buildInFuncList->push_back(new AdvConstantConv());
    this->buildInFuncList->push_back(new AdvPolynomial1());
    this->buildInFuncList->push_back(new AdvPolynomial1Conv());
    this->buildInFuncList->push_back(new AdvPolynomial2());
    this->buildInFuncList->push_back(new AdvPolynomial2Conv());
    this->buildInFuncList->push_back(new AdvPolynomial3());
    this->buildInFuncList->push_back(new AdvPolynomial3Conv());
    this->buildInFuncList->push_back(new AdvDeltaConv());
    this->buildInFuncList->push_back(new AdvExponential());
    this->buildInFuncList->push_back(new AdvErrorFunction());
    this->buildInFuncList->push_back(new AdvGaussian());
    this->buildInFuncList->push_back(new AdvGaussianConv());
    this->buildInFuncList->push_back(new AdvLorentzian());
    this->buildInFuncList->push_back(new AdvLorentzianConv());
    this->buildInFuncList->push_back(new AdvAugmentedLorentzian());
    this->buildInFuncList->push_back(new AdvAugmentedLorentzianConv());
    this->buildInFuncList->push_back(new AdvPseudoVoigt1());
    this->buildInFuncList->push_back(new AdvPseudoVoigt2());
    this->buildInFuncList->push_back(new AdvPseudoVoigt1Conv());
    this->buildInFuncList->push_back(new AdvPseudoVoigt2Conv());
    this->buildInFuncList->push_back(new AdvDampedHarmonicOscillator());
    this->buildInFuncList->push_back(new AdvDampedHarmonicOscillatorConv());
    /* geometric functions */
    this->buildInFuncList->push_back(new AdvTriangle());
    this->buildInFuncList->push_back(new AdvTrapezoid());

#ifdef DEBUG
    for (std::vector<FuncBase*>::iterator i=this->buildInFuncList->begin(); i != this->buildInFuncList->end(); ++i) {
        DebugMessage(className, memberName, "register %-20s %5u %5s\n",
            (*i)->getName().c_str(), (*i)->getNumberOfParam(), (*i)->getSymbol().c_str());
    }
#endif // DEBUG
}

//std::vector<std::string> *AdvFuncParser::*getBuildInFuncList() {
//
//    std::vector<std::string> *retval=new std::vector<std::string>();
//
//    for (std::vector<FuncBase*>::iterator i=this->buildInFuncList->begin(); i != this->buildInFuncList->end(); ++i) {
//        retuval->push_back(*i->getName());
//    }
//    return retval;
//}

//void AdvFuncParser::setFuncNameList(const std::vector<std::string> nameList) {
//    DebugMessage(className, "setFuncNameList(const std::vector<std::string>)", "functions: %u\n", nameList.size()());
//    this->funcNameList=nameList;
//}


std::vector<AdvFuncBase*> AdvFuncParser::parse() {
    std::string memberName = std::string("parse()");

    std::vector<AdvFuncBase*> *funcList = new std::vector<AdvFuncBase*>();
    //UInt4 i;
    Token token;
    Bool match;

    DebugMessage(className, memberName, "this->expr \"%s\"\n", expr.c_str());
    this->i=0;
    
    token=getToken();
    DebugMessage(className, memberName, "token.type=%d\n", token.type);
    while (token.type != TOKEN_END) {
        switch (token.type) {
            case TOKEN_IDENTIFIER:
                DebugMessage(className, memberName, "TOKEN_IDENTIFIER\n");
                match=false;
                for (std::vector<AdvFuncBase*>::iterator f=this->buildInFuncList->begin(); f != this->buildInFuncList->end(); ++f) {
                    DebugMessage(className, memberName, "check %s\n", (*f)->getName().c_str());
                    if ( token.str.compare((*f)->getSymbol()) == 0 || token.str.compare((*f)->getName()) == 0 ) {
                        DebugMessage(className, memberName, "recognized \"%s\" form token \"%s\" at %u\n", (*f)->getName().c_str(), token.str.c_str(), this->i);
                        funcList->push_back(*f);
                        match=true;
                        break;
                    }
                }
                if ( ! match ) {
                    warningMessage(className, memberName, "unknown token %s ignored\n", token.str.c_str());
                }
                break;
            case TOKEN_OPERATOR:
                //DebugMessage(className, memberName, "%u %s => skip\n", i, token.str.c_str());
                DebugMessage(className, memberName, "skip token \"%s\" at %u\n", token.str.c_str(), this->i);
                break;
            default:
                // never reached
                errorMessage(className, memberName, "never reached: token=%s\n", token.str.c_str());
                break;
        }
        token=getToken();
    }
    if (funcList->empty()) {
        errorMessage(className, memberName, "no valid function expression: %s\n", this->expr.c_str());
    }

    //return *funcDataList;
    return *funcList;
}

AdvFuncParser::Token AdvFuncParser::getToken() {

    Token retval;

    while ( this->i < this->expr.size() && isspace(this->expr.at(this->i)) ) {
        ++ (this->i);
    }

    if ( this->i >= this->expr.size() ) {  // end of std::string
        retval.type=TOKEN_END;
        retval.str=*(new std::string(""));
    } else if ( isalpha(this->expr.at(this->i)) ) {  // identifier, function name, etc
        UInt4 b, e;
        b=e=this->i;
        while ( this->i < this->expr.size() && isalnum(this->expr.at(this->i)) ) {
            e=(++(this->i));
        }
        retval.type=TOKEN_IDENTIFIER;
        retval.str=*(new std::string(this->expr, b, e-b));
    } else if ( ispunct(this->expr.at(this->i)) ) {
        ++(this->i);
        retval.type=TOKEN_OPERATOR;
        retval.str=*(new std::string(this->expr, (this->i)-1, 1));
    }
    return retval;
}
