#include <iostream>
#include <istream>
#include <ostream>
#include <cstdio>
#include <cctype>

#include "FuncBase.hh"
#include "Gaussian.hh"
#include "Lorentzian.hh"
#include "AugmentedLorentzian.hh"
#include "PseudoVoigt1.hh"
#include "PseudoVoigt2.hh"

void testFunc(const Double xmin, const Double xmax, UInt4 N, FuncBase *func, const Double *p) {

    //func->dump();
    fprintf(stdout, "%s, %u params=(", func->getName().c_str(), func->getNumberOfParam());
    for (UInt4 i=0; i<func->getNumberOfParam(); ++i) {
        fprintf(stdout, " %g", p[i]);
    }
    fprintf(stdout, ")\n\n");

    fprintf(stdout, "%5s %10s %23s %23s %23s", "No.", "x", func->getName().c_str(), "1st. derivative", "2nd. derivative");
    for (UInt4 j=0; j<func->getNumberOfParam(); ++j) {
        fprintf(stdout, " %23u", j);
    }
    fprintf(stdout, "\n");
    Double delta=(xmax-xmin)/N;
    for (UInt4 i=0; i<=N; ++i) {
        Double x=xmin + delta*i;
        Double *grad=func->gradient(x, p);
        fprintf(stdout, "%5u %10.5f %23.15e %23.15e %23.15e", i, x, func->eval(x, p), func->der1st(x, p), func->der2nd(x, p));
        for (UInt4 j=0; j<func->getNumberOfParam(); ++j) {
            fprintf(stdout, " %23.15e", grad[j]);
        }
        fprintf(stdout, "\n");
        delete grad; grad=NULL;
    }
    fprintf(stdout, "\n");
}

enum tokenType { TOKEN_END, TOKEN_ID, TOKEN_OP };
struct Token { enum tokenType type; string str; };

static string expr = string("g+g+g");


Int4 main(Int4 argc, Char *argv[]) {

    Double xmin;
    Double xmax;
    UInt4  N;  // the number of data points
    Double *p; // pointer to an array of parameters

    std::cout << "xmin : " ; std::cin >> xmin; std::cout << xmin << endl;
    std::cout << "xmax : " ; std::cin >> xmax; std::cout << xmax << endl;
    std::cout << "the number of data points : " ; std::cin >> N; std::cout << N << endl;
    std::cout << endl;

    /*
    vector<FuncBase*> buildInFuncList; 
    buildInFuncList.push_back(new Gaussian());
    buildInFuncList.push_back(new Lorentzian());
    buildInFuncList.push_back(new PseudoVoigt1());
    buildInFuncList.push_back(new PseudoVoigt2());
    */
    vector<FuncBase*> *buildInFuncList = new vector<FuncBase*>(); 
    buildInFuncList->push_back(new Gaussian());
    buildInFuncList->push_back(new Lorentzian());
    buildInFuncList->push_back(new PseudoVoigt1());
    buildInFuncList->push_back(new PseudoVoigt2());
    buildInFuncList->push_back(new AugmentedLorentzian());

    UInt4 totalParam=0;
    UInt4 offset=0;
    UInt4 ct=0;
    fprintf(stdout, "%-20s %-6s %6s %6s %6s\n", "name", "symbol", "param", "offset", "total");
    /*
    for (std::vector<FuncBase*>::iterator i=buildInFuncList.begin(); i != buildInFuncList.end(); ++i) {
       totalParam += (*i)->getNumberOfParam();
       fprintf(stdout, "%-20s %-6s %6u %6u %6u\n", (*i)->getName().c_str(), (*i)->getSymbol().c_str(), (*i)->getNumberOfParam(), offset, totalParam);
       offset += (*i)->getNumberOfParam();
       ++ct;
    }
    */
    for (std::vector<FuncBase*>::iterator i=buildInFuncList->begin(); i != buildInFuncList->end(); ++i) {
       totalParam += (*i)->getNumberOfParam();
       fprintf(stdout, "%-20s %-6s %6u %6u %6u\n", (*i)->getName().c_str(), (*i)->getSymbol().c_str(), (*i)->getNumberOfParam(), offset, totalParam);
       offset += (*i)->getNumberOfParam();
       ++ct;
    }

    p = new Double[totalParam];
    // for gaussian
    p[ 0]=1.0; p[ 1]=0.0; p[ 2]=1.0;
    // for lorentzian
    p[ 3]=1.0; p[ 4]=0.0; p[ 5]=1.0;
    // pseudo voigt1
    p[ 6]=1.0; p[ 7]=0.0; p[ 8]=1.0; p[ 9]=2.0/3.0;
    // pseudo voigt2
    p[10]=1.0; p[11]=0.0; p[12]=1.0; p[13]=2.0; p[14]=2.0/3.0;
    // augmented lorentzian
    p[15]=1.0; p[16]=0.0, p[17]=1.0; p[18]=1.0;
    //
    offset=0;
    /*
    for (std::vector<FuncBase*>::iterator i=buildInFuncList.begin(); i != buildInFuncList.end(); ++i) {
        testFunc(xmin, xmax, N, *i, p+offset);
        offset += (*i)->getNumberOfParam();
    }
    */
    for (std::vector<FuncBase*>::iterator i=buildInFuncList->begin(); i != buildInFuncList->end(); ++i) {
        testFunc(xmin, xmax, N, *i, p+offset);
        offset += (*i)->getNumberOfParam();
    }
}
