#include "Header.hh"
#include "Domain.hh"
#include <cmath>
#include <cstdlib>
#include <ios>

ElementContainer init_src() {
    vector<Double> x;
    vector<Double> b; // background intensity
    vector<Double> y;
    vector<Double> e;
    vector<Double> r;

    const UInt4 N=100;

    Double p, q, a;

    for(UInt4 i=0; i <= N; ++i) {
        //x.push_back(static_cast<Double>(i));
        x.push_back(0.1*i);
    }
    //std::cout << "bin.size()=" <<x.size() << endl;

   for(UInt4 i=0; i < N; ++i) {
       q=(static_cast<Double>(rand())/static_cast<Double>(RAND_MAX));
       b.push_back(1000.0*(1.0 + q));
   }

    for(UInt4 i=0; i < N; ++i) {
        a=(x.at(i) + x.at(i+1))/2.0;
        p=1.0*exp(-1.0*pow((a - 5.0)/2.5, 2.0));
        q=(static_cast<Double>(rand())/static_cast<Double>(RAND_MAX));
        y.push_back(floor(1000000.0*p*(1.0 + q) ));
        //y.push_back(floor(1000000.0*p*(1.0 + q) + b.at(i)));
        //y.push_back(floor(10000.0*b.at(i)));
    }

    for(UInt4 i=0; i < N; ++i) {
        e.push_back(1.0/sqrt(y.at(i)));
    }

    ElementContainer *src = new ElementContainer();
    src->AddToHeader("run number", 1);
    src->AddToHeader("level", 1);
    src->AddToHeader("Inst.", "manyo");
    src->Add("TOF", x);
    src->Add("Intensity", y);
    src->Add("Error", e);
    src->SetKeys("TOF", "Intensity", "Error");

    return *src;
}


void domainTester(Domain &domain) {

    std::cout << "lower and upper bound:        [" << domain.getLowerBound()      << ", " << domain.getUpperBound()      << ")" << endl;
    std::cout << "lower and upper bin bound id: [" << domain.getLowerBinBoundID() << ", " << domain.getUpperBinBoundID() << "]" << endl;
    std::cout << "lower and upper bound id:     [" << domain.getLowerBoundID()    << ", " << domain.getUpperBoundID()    << "]" << endl;
    std::cout << "lower and upper bin id:       [" << domain.getLowerBinID()      << ", " << domain.getUpperBinID()      << "]" << endl;

    vector<Double> bounds    =domain.getBinBounds();
    vector<Int4> *binBoundIDs=domain.getBinBoundIDs();
    vector<Int4> *binIDs     =domain.getBinIDs();
    std::cout << "lower and upper bound:        [" << bounds.front()       << ", " << bounds.back()       << ")" << endl;
    std::cout << "lower and upper bin bound id: [" << binBoundIDs->front() << ", " << binBoundIDs->back() << "]" << endl;
    std::cout << "lower and upper bin id:       [" << binIDs->front()      << ", " << binIDs->back()      << "]" << endl;

    std::cout << "the number of bin: " << domain.getNumberOfBin() << endl << endl;

    vector<Double> new_bin = *domain.getBin();
    std::cout << "the number of new bins: " << new_bin.size() << endl;
    for (std::vector<Double>::const_iterator i=new_bin.begin(); i != new_bin.end(); ++i) {
        std::cout << " " << *i;
    }
    std::cout << endl;

    vector<Double> x = *domain.createXC();
    std::cout << "the number of bin centers: " << x.size() << endl;
    for (std::vector<Double>::const_iterator i=x.begin(); i != x.end(); ++i) {
        std::cout << " " << *i;
    }
    std::cout << endl;
    std::cout << endl;
}


int main(Int4 argc, Char **argv) {

    ElementContainer src=init_src();

    vector<Double> r;

    //std::cout << "testDomain.cc::main, PutXKey() = " << src.PutXKey() << '\n';
    r = src.Put("TOF");
    //std::cout << "testDomain.cc::main, " << r.size() << '\n';
    r = src.Put(src.PutXKey());
    //std::cout << "testDomain.cc::main, " << r.size() << '\n';
    r = src.Put(src.PutXKey());
    for (std::vector<Double>::const_iterator cur = r.begin(); cur != r.end(); ++cur) {
        //std::cout << "testDomain.cc::main, r= " << *cur << '\n';
    }
    //std::cout << "testDomain.cc::main, PutXKey=" << src.PutXKey() << ", PutYKey=" << src.PutYKey() << ", PutEKey=" << src.PutEKey() << endl;
    src.Dump();

    ElementContainer *result = new ElementContainer(src.PutHeader());
    //std::cout << "testDomain.cc::main, result CheckKey operation ==> " << result->PutHeaderPointer()->CheckKey("operation") << '\n';
    //std::cout << "testDomain.cc::main, result CheckKey method    ==> " << result->PutHeaderPointer()->CheckKey("method") << '\n';
    //std::cout << "testDomain.cc::main, result CheckKey BinBounds ==> " << result->PutHeaderPointer()->CheckKey("BinBounds") << '\n';
    result->AddToHeader("operation", "smoothing");
    //std::cout << "testDomain.cc::main, result CheckKey operation ==> " << result->PutHeaderPointer()->CheckKey("operation") << '\n';
    result->AddToHeader("method", "moving average");
    //std::cout << "testDomain.cc::main, result CheckKey method    ==> " << result->PutHeaderPointer()->CheckKey("method") << '\n';
    //result->Dump();

    Domain domain;
    domain.setSource(src);
    //domain.setType(Domain::CC);
    domain.setRange(0.0,    10.0);
    domainTester(domain);
    domain.setRange(1.33,    9.47);
    domainTester(domain);
    domain.setRange(2.38,    9.13);
    domainTester(domain);
    domain.setRange(0.42,    8.41);
    domainTester(domain);
    domain.setRange(5.25,    5.35);
    domainTester(domain);
    domain.setRange(0.0,     0.05);
    domainTester(domain);
    domain.setRange(9.995,  10.00);
    domainTester(domain);
    //
    //domain.setRange( 0, src.PutSize(src.PutXKey())-1);  // error occurs
    domain.setRange(10, 90U);
    domainTester(domain);
    //
    domain.setBinRange( 0, src.PutSize(src.PutXKey())-2);
    domainTester(domain);
    domain.setBinRange(10, 90U);
    domainTester(domain);
    domain.setBinRange(10, 10U);
    domainTester(domain);
    domain.setBinRange(0, 0);
    domainTester(domain);
    domain.setBinRange(99, 99);
    domainTester(domain);

    return EXIT_SUCCESS;
}
