#include <unistd.h>
#include <getopt.h>

#include <cstdlib>
#include <iostream>

#include "Header.hh"
#include "ElementContainer.hh"

#include "FuncCombToElementContainer.hh"

void usage(Char *command) {
    std::cout << "Name:" << std::endl;
    std::cout << "    " << command << " -- test FuncCombToElementContainer" << std::endl;
    std::cout << "Synopsis:" << std::endl;
    std::cout << "    " << command << " [-h|--help] [-l|--lower DOUBLE -u|upper DOUBLE -n|--divnum INT]" << std::endl;
    std::cout << "Options:" << std::endl;
    std::cout << "    -h | --help" << std::endl;
    std::cout << "        print help message" << std::endl;
    std::cout << "    -d | --divnum" << std::endl;
    std::cout << "        specifies the division number of the domain." << std::endl;
    std::cout << "    -l | --lower" << std::endl;
    std::cout << "        specifies the lower bound of the domain." << std::endl;
    std::cout << "    -u | --upper" << std::endl;
    std::cout << "        specifies the lower bound of the domain." << std::endl;
    std::cout << std::endl;
}

void test(const Double lower, const Double upper, const UInt4 divnum) {

    FuncParser parser = FuncParser(string("g g g"));
    vector<FuncBase*> funcList = parser.parse();

    vector<Double> paramList = vector<Double>(9);
    paramList.at(0) =  1.0; // the height of the first gaussian
    paramList.at(1) =  0.0; // the center of the first gaussian
    paramList.at(2) =  1.0; // the HWHM of the first gaussian
    paramList.at(3) =  1.0; // the height of the second gaussian
    paramList.at(4) =  5.0; // the center of the second gaussian
    paramList.at(5) =  1.5; // the HWHM of the second gaussian
    paramList.at(6) =  1.0; // the height of the second gaussian
    paramList.at(7) = 10.0; // the center of the second gaussian
    paramList.at(8) =  1.5; // the HWHM of the second gaussian

    vector<Double> paramErrList=vector<Double>(9, 0.0);

    Double delta=(upper-lower)/divnum;
    vector<Double> bin=vector<Double>(divnum+1);
    for (UInt4 i=0; i<=divnum; ++i) {
        bin.at(i)=lower+delta*i;
    }

    FuncCombToElementContainer ftoEC = FuncCombToElementContainer(funcList, paramList, paramErrList);
    ElementContainer ecc = ftoEC.create("x", "y", "e", "", "", bin);
    ElementContainer ecl = ftoEC.create("x", "y", "e", "", "", bin, 0.0);
    ElementContainer ecu = ftoEC.create("x", "y", "e", "", "", bin, 1.0);
    ecc.Dump();
    ecl.Dump();
    ecu.Dump();
}

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

    struct option longOptions[] = {
        {"divnumr", required_argument, NULL, 'd'},
        {"help",    no_argument,       NULL, 'h'},
        {"lower",   required_argument, NULL, 'l'},
        {"upper",   required_argument, NULL, 'u'},
    };

    Double lower=-5.0;
    Double upper=15.0;
    UInt4  divnum=100;

    Int4 c;
    Int4 optionIndex;

    while(true) {
        c = getopt_long(argc, argv, "d:hl:u:", longOptions, &optionIndex);
        if (c == -1) break;

        switch (c) {
            case 'd':
                divnum = static_cast<UInt4>(std::atoi(optarg));
                break;
            case 'h':
                usage(argv[0]);
                exit(EXIT_SUCCESS);
            case 'l':
                lower = std::atof(optarg);
                break;
            case 'u':
                upper = std::atof(optarg);
                break;
            default:
                // do nothing
                break;
        }
    }

    std::cout << "the lower bound:     " << lower  << std::endl;
    std::cout << "the upper bound:     " << upper  << std::endl;
    std::cout << "the division number: " << divnum << std::endl;

    test(lower, upper, divnum);

    exit(EXIT_SUCCESS);
}
