// ************************************************************************** //
//
//  BornAgain: simulate and fit scattering at grazing incidence
//
//! @file      App/src/IsGISAXSTools.cpp
//! @brief     Implements class IsGISAXSTools.
//
//! Homepage:  apps.jcns.fz-juelich.de/BornAgain
//! License:   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2013
//! @authors   Scientific Computing Group at MLZ Garching
//! @authors   C. Durniak, G. Pospelov, W. Van Herck, J. Wuttke
//
// ************************************************************************** //

#include "IsGISAXSTools.h"
#include "BornAgainNamespace.h"
#include "Units.h"
#include "Exceptions.h"
#include "MathFunctions.h"
#include "DrawHelper.h"

#include "TCanvas.h"
#include "TH1D.h"
#include "TH2D.h"
#include "TH3D.h"
#include "TStyle.h"
#include "TLine.h"
#include "TPolyMarker.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <iterator>
#include <iomanip>
#include <cmath>
#include <algorithm>
#include <cassert>

double IsGISAXSTools::m_hist_min = 0;
double IsGISAXSTools::m_hist_max = 0;
bool   IsGISAXSTools::m_has_min = false;
bool   IsGISAXSTools::m_has_max = false;

//! Draw 2D histogram representing logarithm of OutputData (in new canvas).

void IsGISAXSTools::drawLogOutputData(const OutputData<double>& output,
        const std::string& canvas_name, const std::string& canvas_title,
        const std::string& draw_options, const std::string& histogram_title)
{
    assert(&output);
    TCanvas *c1 = new
        TCanvas(canvas_name.c_str(), canvas_title.c_str(), 0, 0, 1024, 768);
    IsGISAXSTools::setMinimum(1.);
    c1->cd();
    gPad->SetLogz();
    drawOutputDataInPad(output, draw_options, histogram_title);
}

//! Draw 4 2D histograms representing logarithm of polarized OutputData
//! (in new canvas).

void IsGISAXSTools::drawLogOutputDataPol(
        const OutputData<Eigen::Matrix2d>& output,
        const std::string& canvas_name, const std::string& canvas_title,
        const std::string& draw_options, const std::string& histogram_title)
{
    assert(&output);
    TCanvas *c1 = new
        TCanvas(canvas_name.c_str(), canvas_title.c_str(), 0, 0, 1024, 768);
    c1->Divide(2,2);

    OutputData<double> data;
    data.copyShapeFrom(output);

    // plus - plus
    c1->cd(1); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    setMinimum(1.);
    copyElementsWithPosition(output, data, 0, 0);
    drawOutputDataInPad(data, draw_options, histogram_title + ": + +");

    // plus - min
    c1->cd(2); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    setMinimum(1.);
    copyElementsWithPosition(output, data, 0, 1);
    drawOutputDataInPad(data, draw_options, histogram_title + ": + -");

    // min - plus
    c1->cd(3); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    setMinimum(1.);
    copyElementsWithPosition(output, data, 1, 0);
    drawOutputDataInPad(data, draw_options, histogram_title + ": - +");

    // min - min
    c1->cd(4); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    setMinimum(1.);
    copyElementsWithPosition(output, data, 1, 1);
    drawOutputDataInPad(data, draw_options, histogram_title + ": - -");
}

//! Draw 2D histogram representing OutputData (in new canvas).

void IsGISAXSTools::drawOutputData(const OutputData<double>& output,
        const std::string& canvas_name, const std::string& canvas_title,
        const std::string& draw_options, const std::string& histogram_title)
{
    assert(&output);
    TCanvas *c1 = new
        TCanvas(canvas_name.c_str(), canvas_title.c_str(), 0, 0, 1024, 768);
    c1->cd();
    drawOutputDataInPad(output, draw_options, histogram_title);
}

//! Draw 1D or 2D histograms representing OutputData (in current gPad).

void IsGISAXSTools::drawOutputDataInPad(const OutputData<double>& output,
        const std::string& draw_options, const std::string& histogram_title)
{
    if( !&output) throw NullPointerException("IsGISAXSTools::drawOutputDataInPad() -> Error! Null output data");
    if(!gPad) {
        throw NullPointerException("IsGISAXSTools::drawOutputDataInPad() -> Error! No canvas exists.");
    }

    TH1 *hist(0);
    if(output.getRank() == 2 ) {
        hist = IsGISAXSTools::getOutputDataTH2D(output, "p_hist1D");
    } else {
        hist = IsGISAXSTools::getOutputDataTH123D(output, "p_hist1D");
    }
    hist->SetContour(99);
    gStyle->SetPalette(1);
    gStyle->SetOptStat(0);
    if( hasMinimum() ) hist->SetMinimum(m_hist_min);
    if( hasMaximum() ) hist->SetMaximum(m_hist_max);
    hist->SetTitle(histogram_title.c_str());
    hist->DrawCopy(draw_options.c_str());

    // dealing with masks
    if(output.getMask()) {
        TPolyMarker *poly = new TPolyMarker();
        const IAxis *p_axis0 = output.getAxis(0);
        const IAxis *p_axis1 = output.getAxis(1);
        int i_point(0);
        for(OutputData<double>::const_iterator it = output.begin();
            it!= output.end(); ++it) {
            size_t axis0_index = output.toCoordinate(it.getIndex(), 0);
            size_t axis1_index = output.toCoordinate(it.getIndex(), 1);
            double axis0_value = (*p_axis0)[axis0_index];
            double axis1_value = (*p_axis1)[axis1_index];
            poly->SetPoint(i_point++, axis0_value, axis1_value);
        }
        poly->Draw("same");
    }

    delete hist;
}

//! Creates TH2D from OutputData.

TH2D *IsGISAXSTools::getOutputDataTH2D(const OutputData<double>& output,
                                       const std::string& histo_name)
{
    assert(&output);
    if (output.getRank() !=2)
        throw( "IsGISAXSTools::getOutputDataTH2D() -> "
               "Warning! Expected number of dimensiobs is 2.");

    std::vector<AxisStructure > haxises;
    haxises.resize(output.getRank());

    // we assume variable bin size and prepare [nbins+1] array of left edges of each bin plus right edge of the last bin
    for(size_t i_axis=0; i_axis<output.getRank(); ++i_axis) {
        const IAxis *axis = output.getAxis(i_axis);
        if( !axis ) throw("IsGISAXSTools::getOutputDataTH123D() -> Error! Can't cast axis");
        double dx(0);
        haxises[i_axis].nbins = axis->getSize();
        haxises[i_axis].name = axis->getName();
        if( axis->getSize() == 0) {
            throw LogicErrorException("IsGISAXSTools::getOutputDataTH123D() -> Error! Axis with zero size.");
        }else if( axis->getSize() == 1 ) {
            // only one bin, let's invent fake bin size
            dx = 0.1*(*axis)[0];
            haxises[i_axis].xbins.push_back((*axis)[0]-dx/2.);
            haxises[i_axis].xbins.push_back((*axis)[0]+dx/2.);
        }else {
            for(size_t i_bin=0; i_bin<axis->getSize(); ++i_bin) {
                if(i_bin == 0) {
                    dx = (*axis)[1]-(*axis)[0];
                } else {
                    dx = (*axis)[i_bin] - (*axis)[i_bin-1];
                }
                haxises[i_axis].xbins.push_back( (*axis)[i_bin] - dx/2.);
            }
        haxises[i_axis].xbins.push_back((*axis)[axis->getSize()-1] + dx/2.); // right bin edge of last bin, so for 100 bins size of vector will be 101
        }
    }

    // creation of 2D with variable bin size
//    std::cout << "XXXYYY " << (int)haxises[0].nbins << " "  << (int)haxises[1].nbins;
//    for(size_t i=0; i<haxises[0].xbins.size(); ++i) {
//        std::cout << i << " axis0:" << haxises[0].xbins[i] << std::endl;
//    }
//    for(size_t i=0; i<haxises[1].xbins.size(); ++i) {
//        std::cout << i << " axis1:" << haxises[1].xbins[i] << std::endl;
//    }

    TH2D *hist2 = new TH2D(histo_name.c_str(), histo_name.c_str(), (int)haxises[0].nbins, &haxises[0].xbins[0], (int)haxises[1].nbins, &haxises[1].xbins[0]);
    hist2->GetXaxis()->SetTitle( haxises[0].name.c_str() );
    hist2->GetYaxis()->SetTitle( haxises[1].name.c_str() );

    OutputData<double>::const_iterator it = output.begin();
    while (it != output.end())
    {
        double x = output.getValueOfAxis( haxises[0].name.c_str(), it.getIndex() );
        double y = output.getValueOfAxis( haxises[1].name.c_str(), it.getIndex() );
        double value = *it++;
        hist2->Fill(x, y, value);
    }
    hist2->SetContour(50);
    hist2->SetStats(0);
    hist2->GetYaxis()->SetTitleOffset(1.1);

    gStyle->SetPalette(1);
    gStyle->SetOptStat(0);
    return hist2;
}




/* ************************************************************************* */
// create TH2D from OutputData
/* ************************************************************************* */
//TH2D *IsGISAXSTools::getOutputDataTH2D(const OutputData<double>& output, const std::string &histo_name)
//{
//    assert(&output);
//    if (output.getNdimensions() !=2) throw( "IsGISAXSTools::getOutputDataTH2D() -> Warning! Expected number of dimensiobs is 2.");

//    std::vector<AxisStructure > haxises;
//    haxises.resize(output.getNdimensions());

//    // we assume variable bin size and prepare [nbins+1] array of left edges of each bin plus right edge of the last bin
//    for(size_t i_axis=0; i_axis<output.getNdimensions(); ++i_axis) {
//        const IAxis *axis = output.getAxis(i_axis);
//        if( !axis ) throw("IsGISAXSTools::getOutputDataTH123D() -> Error! Can't cast axis");
//        double dx(0);
//        haxises[i_axis].nbins = axis->getSize();
//        haxises[i_axis].name = axis->getName();
//        if( axis->getSize() == 0) {
//            throw LogicErrorException("IsGISAXSTools::getOutputDataTH123D() -> Error! Axis with zero size.");
//        }else if( axis->getSize() == 1 ) {
//            // only one bin, let's invent fake bin size
//            dx = 0.1*(*axis)[0];
//            haxises[i_axis].xbins.push_back((*axis)[0]-dx/2.);
//            haxises[i_axis].xbins.push_back((*axis)[0]+dx/2.);
//        }else {
//            for(size_t i_bin=0; i_bin<axis->getSize(); ++i_bin) {
//                if(i_bin == 0) {
//                    dx = (*axis)[1]-(*axis)[0];
//                } else {
//                    dx = (*axis)[i_bin] - (*axis)[i_bin-1];
//                }
//                haxises[i_axis].xbins.push_back( (*axis)[i_bin] - dx/2.);
//            }
//        haxises[i_axis].xbins.push_back((*axis)[axis->getSize()-1] + dx/2.); // right bin edge of last bin, so for 100 bins size of vector will be 101
//        }
//    }

//    // creation of 2D with variable bin size
//    std::cout << "XXXYYY " << (int)haxises[0].nbins << " "  << (int)haxises[1].nbins;
//    for(size_t i=0; i<haxises[0].xbins.size(); ++i) {
//        std::cout << i << " axis0:" << haxises[0].xbins[i] << std::endl;
//    }
//    for(size_t i=0; i<haxises[1].xbins.size(); ++i) {
//        std::cout << i << " axis1:" << haxises[1].xbins[i] << std::endl;
//    }

//    TH2D *hist2 = new TH2D(histo_name.c_str(), histo_name.c_str(), (int)haxises[0].nbins, &haxises[0].xbins[0], (int)haxises[1].nbins, &haxises[1].xbins[0]);
//    hist2->GetXaxis()->SetTitle( haxises[0].name.c_str() );
//    hist2->GetYaxis()->SetTitle( haxises[1].name.c_str() );

//    OutputData<double>::const_iterator it = output.begin();
//    while (it != output.end())
//    {
//        double x = output.getValueOfAxis( haxises[0].name.c_str(), it.getIndex() );
//        double y = output.getValueOfAxis( haxises[1].name.c_str(), it.getIndex() );
//        double value = *it++;
//        //std::cout << "OOO " << x << " " << y << std::endl;
//        hist2->Fill(x, y, value);
//    }
//    hist2->SetContour(50);
//    hist2->SetStats(0);
//    hist2->GetYaxis()->SetTitleOffset(1.1);

//    gStyle->SetPalette(1);
//    gStyle->SetOptStat(0);
//    return hist2;
//}


/* ************************************************************************* */
// create TH1D, TH2D or TH3D from OutputData
/* ************************************************************************* */
TH1 *IsGISAXSTools::getOutputDataTH123D(const OutputData<double>& output, const std::string& histo_name)
{
    assert(&output);
    if (output.getRank() >3) {
        std::cout << "IsGISAXSTools::getOutputDataTH123D() -> Warning! Expected number of dimensions should be not more than 3" << std::endl;
        return 0;
    }

    std::vector<AxisStructure > haxises;
    haxises.resize(output.getRank());

    // we assume variable bin size and prepare [nbins+1] array of left edges of each bin plus right edge of the last bin
    for(size_t i_axis=0; i_axis<output.getRank(); ++i_axis) {
        const IAxis *axis = output.getAxis(i_axis);
        if( !axis ) throw("IsGISAXSTools::getOutputDataTH123D() -> Error! Can't cast axis");
        double dx(0);
        haxises[i_axis].nbins = axis->getSize();
        haxises[i_axis].name = axis->getName();
        if( axis->getSize() == 0) {
            throw LogicErrorException("IsGISAXSTools::getOutputDataTH123D() -> Error! Axis with zero size.");
        }else if( axis->getSize() == 1 ) {
            // only one bin, let's invent fake bin size
            dx = 0.1*(*axis)[0];
            haxises[i_axis].xbins.push_back((*axis)[0]-dx/2.);
            haxises[i_axis].xbins.push_back((*axis)[0]+dx/2.);
        }else {
            for(size_t i_bin=0; i_bin<axis->getSize(); ++i_bin) {
                if(i_bin == 0) {
                    dx = (*axis)[1]-(*axis)[0];
                } else {
                    dx = (*axis)[i_bin] - (*axis)[i_bin-1];
                }
                haxises[i_axis].xbins.push_back( (*axis)[i_bin] - dx/2.);
            }
        haxises[i_axis].xbins.push_back((*axis)[axis->getSize()-1] + dx/2.); // right bin edge of last bin, so for 100 bins size of vector will be 101
        }
    }

//    for(size_t i_axis=0; i_axis<output.getNdimensions(); ++i_axis) {
//       std::cout << "axis " << i_axis << " size:" << haxises[i_axis].xbins.size() << std::endl;
//       for(size_t i_bin=0; i_bin<haxises[i_axis].xbins.size(); ++i_bin) {
//           std::cout << haxises[i_axis].xbins[i_bin] << " ";
//       }
//       std::cout << std::endl;
//    }

    // creation of 1D, 2D or 3D histogram with variable bin size
    TH1 *hist(0);
    TH1D *hist1(0);
    TH2D *hist2(0);
    TH3D *hist3(0);
    if(output.getRank() == 1) {
        hist1 = new TH1D(histo_name.c_str(), histo_name.c_str(), (int)haxises[0].nbins, &haxises[0].xbins[0]);
        hist1->GetXaxis()->SetTitle( haxises[0].name.c_str() );
        hist = hist1;
    } else if(output.getRank() == 2) {
        hist2 = new TH2D(histo_name.c_str(), histo_name.c_str(), (int)haxises[0].nbins, &haxises[0].xbins[0], (int)haxises[1].nbins, &haxises[1].xbins[0]);
        hist2->GetXaxis()->SetTitle( haxises[0].name.c_str() );
        hist2->GetYaxis()->SetTitle( haxises[1].name.c_str() );
        hist = hist2;
    } else if(output.getRank() == 3) {
        hist3 = new TH3D(histo_name.c_str(), histo_name.c_str(), (int)haxises[0].nbins, &haxises[0].xbins[0], (int)haxises[1].nbins, &haxises[1].xbins[0], (int)haxises[1].nbins, &haxises[1].xbins[0]);
        hist3->GetXaxis()->SetTitle( haxises[0].name.c_str() );
        hist3->GetYaxis()->SetTitle( haxises[1].name.c_str() );
        hist3->GetZaxis()->SetTitle( haxises[2].name.c_str() );
        hist = hist3;
    } else {
        throw LogicErrorException("IsGISAXSTools::getOutputDataTH123D() -> Error! Wrong number of dimensions.");
    }

    OutputData<double>::const_iterator it = output.begin();
    while (it != output.end())
    {
        std::vector<double > xyz;
        for(size_t i_axis=0; i_axis<haxises.size(); ++i_axis) {
            xyz.push_back(output.getValueOfAxis( haxises[i_axis].name.c_str(), it.getIndex() ) );
        }
        double value = *it++;
        if(hist1) hist1->Fill(xyz[0], value);
        if(hist2) hist2->Fill(xyz[0], xyz[1], value);
        if(hist3) hist3->Fill(xyz[0], xyz[1], xyz[2], value);
    }
    hist->SetContour(50);
    hist->SetStats(0);
    hist->GetYaxis()->SetTitleOffset(1.1);

    gStyle->SetPalette(1);
    gStyle->SetOptStat(0);
    return hist;
}



/* ************************************************************************* */
// draw 1D distribution over values stored in OutputData
// binning of histogram is calculated on the fly
/* ************************************************************************* */
void IsGISAXSTools::drawOutputDataDistribution1D(const OutputData<double>& output, const std::string& draw_options, const std::string& histogram_title)
{
    if(!gPad) {
        throw NullPointerException("IsGISAXSTools::drawOutputDataDistribution1D() -> Error! No canvas exists.");
    }

    std::string histo_name = histogram_title;
    if (histo_name.empty()) {
        histo_name = gPad->GetTitle();
    }

    // creating histogram with automatic binning
    TH1::SetDefaultBufferSize(5000);
    TH1D h1_spect("h1_spect", histo_name.c_str(), 200, 1.0, -1.0); // xmin > xmax as a sign of automatic binning

    OutputData<double>::const_iterator it = output.begin();
    while (it != output.end())
    {
        h1_spect.Fill( *it++ );
    }

    h1_spect.DrawCopy(draw_options.c_str());
}


/* ************************************************************************* */
// draw 1D distribution over relative difference in two OutputData sets
/* ************************************************************************* */
void IsGISAXSTools::drawOutputDataDifference1D(const OutputData<double>& left, const OutputData<double>& right, const std::string& draw_options, const std::string& histogram_title)
{
    assert(&left);
    assert(&right);
    if(!gPad) {
        throw NullPointerException("IsGISAXSTools::drawOutputDataDifference1D() -> Error! No canvas exists.");
    }

    OutputData<double> *left_clone = left.clone();
    OutputData<double> *right_clone = right.clone();

    *left_clone -= *right_clone;
    *left_clone /= *right_clone;

    std::string histo_name = histogram_title;
    if (histo_name.empty()) {
        histo_name = gPad->GetTitle();
    }

    TH1D h1_spect("difference", histo_name.c_str(), 40, -20.0, 20.0);
    h1_spect.GetXaxis()->SetTitle("log10( (we-isgi)/isgi )");

    OutputData<double>::const_iterator it = left_clone->begin();
    while (it != left_clone->end())
    {
        double x = *it++;
        if(x!=0) {
            x = log10(fabs(x));
        } else {
            // lets put the cases when the difference is exactly 0 to underflow bin
            x = -21.;
        }
        h1_spect.Fill( x );
    }

//    gPad->SetLogy();
    gPad->SetRightMargin(0.115);
    gPad->SetLeftMargin(0.115);
    h1_spect.SetStats(1);
    gStyle->SetOptStat(111111);
    if( hasMinimum() ) h1_spect.SetMinimum(m_hist_min);
    if( hasMaximum() ) h1_spect.SetMaximum(m_hist_max);

    h1_spect.DrawCopy(draw_options.c_str());
    delete left_clone;
    delete right_clone;
}


/* ************************************************************************* */
// draw relative difference of two 2D OutputData sets
/* ************************************************************************* */
void IsGISAXSTools::drawOutputDataRelativeDifference2D(const OutputData<double>& left, const OutputData<double>& right, const std::string& draw_options, const std::string& histogram_title)
{
    assert(&left);
    assert(&right);
    if(!gPad) {
        throw NullPointerException("IsGISAXSTools::drawOutputDataDifference2D -> Error! No canvas exists.");
    }
    OutputData<double> *left_clone = left.clone();
    OutputData<double> *right_clone = right.clone();

    *left_clone -= *right_clone;
    *left_clone /= *right_clone;
    left_clone->scaleAll(100.0);

    IsGISAXSTools::drawOutputDataInPad(*left_clone, draw_options, histogram_title);

    delete left_clone;
    delete right_clone;
}


/* ************************************************************************* */
// draw relative chi2 difference of two 2D OutputData sets
/* ************************************************************************* */
void IsGISAXSTools::drawOutputDataChi2Difference2D(const OutputData<double>& left, const OutputData<double>& right, const std::string& draw_options, const std::string& histogram_title)
{
    assert(&left);
    assert(&right);
    if(!gPad) {
        throw NullPointerException("IsGISAXSTools::drawOutputDataDifference2D -> Error! No canvas exists.");
    }
    OutputData<double> *left_clone = left.clone();
    OutputData<double> *right_clone = right.clone();

    *left_clone -= *right_clone;
    OutputData<double> *tmp = left_clone->clone();

    *left_clone *= *tmp;
    left_clone->scaleAll(1./left_clone->totalSum());

    IsGISAXSTools::drawOutputDataInPad(*left_clone, draw_options, histogram_title);

    delete left_clone;
    delete right_clone;
    delete tmp;
}


void IsGISAXSTools::exportOutputDataInVectors2D(const OutputData<double>& input_data
                                        , std::vector<std::vector<double > >& v_data
                                        , std::vector<std::vector<double > >& v_axis0
                                        , std::vector<std::vector<double > >& v_axis1)
{
    assert(&input_data);
    if (input_data.getRank() != 2) return;

    OutputData<double> *data = input_data.clone();

    const IAxis *p_axis0 = data->getAxis(0);
    const IAxis *p_axis1 = data->getAxis(1);
    size_t axis0_size = p_axis0->getSize();
    size_t axis1_size = p_axis1->getSize();

    v_data.clear();
    v_axis0.clear();
    v_axis1.clear();

    v_data.resize(axis0_size);
    v_axis0.resize(axis0_size);
    v_axis1.resize(axis0_size);

    for(size_t i=0; i<axis0_size; ++i) {
        v_data[i].resize(axis1_size,0.0);
        v_axis0[i].resize(axis1_size,0.0);
        v_axis1[i].resize(axis1_size,0.0);
    }

    // saving data
    OutputData<double>::const_iterator it = data->begin();
    while (it != data->end())
    {
        size_t axis0_index = data->toCoordinates(it.getIndex())[0];
        size_t axis1_index = data->toCoordinates(it.getIndex())[1];
        double intensity = *it++;
        v_data[axis0_index][axis1_index] = intensity;
    }

    // saving axis
    data->removeAllMasks();
    it = data->begin();
    while (it != data->end())
    {
        size_t axis0_index = data->toCoordinates(it.getIndex())[0];
        size_t axis1_index = data->toCoordinates(it.getIndex())[1];
        double axis0_value = (*p_axis0)[axis0_index];
        double axis1_value = (*p_axis1)[axis1_index];
        v_axis0[axis0_index][axis1_index] = axis0_value;
        v_axis1[axis0_index][axis1_index] = axis1_value;
        it++;
    }

}


/* ************************************************************************* */
// Create TLine for displaying of one-dimensional data scan
// OutputData should be 2D, and one of two dimensions should have number of bins == 1
/* ************************************************************************* */
TLine *IsGISAXSTools::getOutputDataScanLine(const OutputData<double>& data)
{
    assert(&data);

    if(data.getRank() != 2) throw LogicErrorException("IsGISAXSTools::getOutputDataScanLine() -> Error! Number of dimensions should be 2");
    double x1(0), x2(0), y1(0), y2(0);
    if( data.getAxis(BA::ALPHA_AXIS_NAME) && data.getAxis(BA::ALPHA_AXIS_NAME)->getSize() == 1) {
        // horizontal line
        x1 = data.getAxis(BA::PHI_AXIS_NAME)->getMin();
        x2 = data.getAxis(BA::PHI_AXIS_NAME)->getMax();
        y1 = y2 = data.getAxis(BA::ALPHA_AXIS_NAME)->getMin();
    }else if( data.getAxis(BA::PHI_AXIS_NAME) && data.getAxis(BA::PHI_AXIS_NAME)->getSize() == 1 ) {
        // it's vertical line
        x1 = x2 = data.getAxis(BA::PHI_AXIS_NAME)->getMin();
        y1 = data.getAxis(BA::ALPHA_AXIS_NAME)->getMin();
        y2 = data.getAxis(BA::ALPHA_AXIS_NAME)->getMax();
    } else {
        throw LogicErrorException("IsGISAXSTools::getOutputDataScanLine() -> Error! Can't handle these axes.");
    }
    TLine *line = new TLine(x1,y1,x2,y2);
    line->SetLineColor(kRed);
    line->SetLineStyle(1);
    line->SetLineWidth(2);
    return line;
}


/* ************************************************************************* */
// Create TH1D for displaying of one-dimensional data scan
// OutputData should be 2D, and one of two dimensions should have number of bins == 1
/* ************************************************************************* */
TH1D *IsGISAXSTools::getOutputDataScanHist(const OutputData<double>& data, const std::string& hname)
{
    assert(&data);

    if(data.getRank() != 2) throw LogicErrorException("IsGISAXSTools::getOutputDataScanHist() -> Error! Number of dimensions should be 2");
    // one of axis should have dimension 1
    if( (data.getAxis(BA::ALPHA_AXIS_NAME) && data.getAxis(BA::ALPHA_AXIS_NAME)->getSize() != 1) && (data.getAxis(BA::PHI_AXIS_NAME) && data.getAxis(BA::PHI_AXIS_NAME)->getSize() != 1))
    {
        throw LogicErrorException("IsGISAXSTools::getOutputDataScanHist() -> Info. Can't create 1D histogram from these axes");
        //std::cout << "IsGISAXSTools::getOutputDataScanHist() -> Info. Can't create 1D histogram from these axes" << std::endl;
        //return 0;
    }

    TH2D *hist2 = IsGISAXSTools::getOutputDataTH2D( data, hname);

    TH1D *hist1(0);
    std::ostringstream ostr_title;
    if( data.getAxis(BA::ALPHA_AXIS_NAME) && data.getAxis(BA::ALPHA_AXIS_NAME)->getSize() == 1) {
        hist1 = hist2->ProjectionX();
        ostr_title << hname << ", alpha_f=" << data.getAxis(BA::ALPHA_AXIS_NAME)->getMin();
    }else if( data.getAxis(BA::PHI_AXIS_NAME) && data.getAxis(BA::PHI_AXIS_NAME)->getSize() == 1 ) {
        hist1 = hist2->ProjectionY();
        ostr_title << hname << ", phi_f=" << data.getAxis(BA::PHI_AXIS_NAME)->getMin();
    } else {
        throw LogicErrorException("IsGISAXSTools::getOutputDataScanHist() -> Error! Unexpected place");
    }
    delete hist2;
    if( !hist1 ) throw LogicErrorException("IsGISAXSTools::getOutputDataScanHist() -> Error! Failed to make projection, existing name?");

    hist1->SetTitle(ostr_title.str().c_str());
    // FIXME remove this trick to bypass weird bug with DrawCopy of TH1D projection of TH1D histograms
    TH1D *h1 = dynamic_cast<TH1D*>(hist1->Clone());
    delete hist1;
    return h1;
}


/* ************************************************************************* */
// add noise to data
/* ************************************************************************* */
OutputData<double > *IsGISAXSTools::createNoisyData(const OutputData<double>& exact_data, double noise_factor)
{
    assert(&exact_data);
    OutputData<double > *real_data = exact_data.clone();
    OutputData<double>::iterator it = real_data->begin();
    while (it != real_data->end()) {
        double current = *it;
        double sigma = noise_factor*std::sqrt(current);
        double random = MathFunctions::GenerateNormalRandom(current, sigma);
        if (random<0.0) random = 0.0;
        *it = random;
        ++it;
    }
    return real_data;
}


OutputData<double > *IsGISAXSTools::createDataWithGaussianNoise(const OutputData<double>& exact_data, double sigma)
{
    assert(&exact_data);
    OutputData<double > *real_data = exact_data.clone();
    OutputData<double>::iterator it = real_data->begin();
    while (it != real_data->end()) {
        double current = *it;
        double random = MathFunctions::GenerateNormalRandom(0.0, sigma);
        *it = current+random;
        ++it;
    }
    return real_data;
}

void IsGISAXSTools::drawOutputDataComparisonResults(
        const OutputData<double>& data, const OutputData<double>& reference,
        const std::string& name, const std::string& title, double hmin,
        double hmax, double hdiff)
{
    assert(&data);
    assert(&reference);
    TCanvas *c1 = DrawHelper::createAndRegisterCanvas(name, title);
    c1->Divide(2,2);

    // our calculations
    c1->cd(1); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    if(hasMinimum()) IsGISAXSTools::setMinimum(hmin);
    //if(hmax>0) IsGISAXSTools::setMaximum(hmax);
    if(hasMaximum()) IsGISAXSTools::setMaximum(hmax);
    IsGISAXSTools::drawOutputDataInPad(data, "CONT4 Z", "this");

    // isgisaxs data
    c1->cd(2); gPad->SetLogz();
    gPad->SetRightMargin(0.12);
    IsGISAXSTools::drawOutputDataInPad(reference, "CONT4 Z", "isgi");

    // difference
    c1->cd(3);
    gPad->SetRightMargin(0.12);
    IsGISAXSTools::setMinimum(-hdiff);
    IsGISAXSTools::setMaximum(hdiff);
    IsGISAXSTools::drawOutputDataRelativeDifference2D(data, reference,
            "CONT4 Z", "2D Difference map");

    // difference
    c1->cd(4);
    gPad->SetRightMargin(0.12);
    IsGISAXSTools::resetMinimumAndMaximum();
    IsGISAXSTools::setMinimum(1);
    IsGISAXSTools::drawOutputDataDifference1D(data, reference, "",
            "Difference spectra");

}

void IsGISAXSTools::copyElementsWithPosition(
        const OutputData<Eigen::Matrix2d>& source,
        OutputData<double>& destination, int pos_x, int pos_y)
{
    OutputData<Eigen::Matrix2d>::const_iterator it_source =
            source.begin();
    OutputData<double>::iterator it_dest = destination.begin();
    while (it_source != source.end()) {
        *it_dest = (*it_source)(pos_x, pos_y);
        it_source++;
        it_dest++;
    }
}