#include "YODA/Histo.h"
#include "YODA/Profile.h"
#include "YODA/Scatter.h"

#include <cmath>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <sys/time.h>

using namespace std;
using namespace YODA;

int main() {

    // Creating a histogram and measuring the time it takes to do it.
    struct timeval startTime{0,0};
    struct timeval endTime{0,0};
    Histo2D h(200, 0, 100, 200, 0, 100);

    double tS;
    double tE;

    cout << "Checking if filling does modify the grid:";
    size_t beforeAdd = h.numBins();
    h.fill(10000, 34234, 1);
    if (h.numBins() != beforeAdd) {
        cout << "FAIL" << endl;
        return -1;
    }
    cout << "PASS" << endl;

    // Checking if everything is still working as desired.
    // It is a good thing to do, as at some earlier stages
    // in development adding a broken bin destroyed the cache of edges.
    cout << "Is the origin working correctly?         ";
    int originIndex = h.fill(0.0, 0.0, 1);
    if (originIndex == -1) {
        cout << "FAIL" << endl;
        return -1;
    }
    cout << "PASS" << endl;

    cout << "Does rebinning work?                     ";
    Histo2D h2(20, 0, 100, 20, 0, 100);
    h2.rebin<0>(2,3);
    cout << "PASS" << endl;

    cout << "Trying to fill the newly created bin:    ";
    int fillTest = h.fill(-3, -3, 1);
    if (fillTest != int(h.indexAt(-3,-3))) {
        cout << "FAIL" << endl;
        return -1;
    }
    cout << "PASS" << endl;

    gettimeofday(&endTime, NULL);

    fillTest = h.fill(180, 180, 1);
    if (fillTest != int(h.indexAt(180,180))) {
        cout << "FAIL" << endl;
        return -1;
    }
    tS = (startTime.tv_sec*1000000 + startTime.tv_usec)/(double)1000000;
    tE = (endTime.tv_sec*1000000 + endTime.tv_usec)/(double)1000000;
    cout << "PASS (" << tE - tS << "s)" << endl;

    cout << "Testing cuts:                            ";
    Histo2D sampleHisto(50, 0, 100, 39, 0, 10);
    sampleHisto.fill(0,0,123121);
    if (!fuzzyEquals(sampleHisto.sumW(false), (double)123121)) {
        cout << "FAIL" << endl;
        return -1;
    }
    if (!fuzzyEquals(sampleHisto.sumW2(false), 1.51588e+10)) {
        cout << "FAIL" << endl;
        return -1;
    }
    cout << "PASS" << endl;

    cout << "Testing if histo2D transforms to Profile:";
    Histo2D test1(100, 0, 100, 100, 0, 100);
    Profile1D test2(100, 0, 100);

    test1.fill(1,1,1);
    test2.fill(1,1,1);
    if(test2 != test1.mkMarginalProfile<0>()){
      cout << "FAIL" << endl;
      return -1;
    }
    cout << "PASS" << endl;
    cout << "A more elaborate test:                   ";
    for(size_t i = 0; i < 100000; ++i){
      size_t x = rand()%100;
      size_t y = rand()%100;
      test1.fill(x,y,1);
      test2.fill(x,y,2);
    }
    if(test2 != test1.mkMarginalProfile<0>()){
      cout << "FAIL" << endl;
      return -1;
    }
    cout << "PASS" << endl;

    return EXIT_SUCCESS;
}
