/***************************************************************************

  PRunNonMusr.cpp

  Author: Andreas Suter
  e-mail: andreas.suter@psi.ch

  $Id$

***************************************************************************/

/***************************************************************************
 *   Copyright (C) 2007 by Andreas Suter                                   *
 *   andreas.suter@psi.ch                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <iostream>

#include "PRunNonMusr.h"

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor.
 */
PRunNonMusr::PRunNonMusr() : PRunBase()
{
  fFitStartTime = 0.0;
  fFitStopTime  = 0.0;
  fNoOfFitBins  = 0;

  fHandleTag = kEmpty;

  fRawRunData = 0;
}

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor
 *
 * \param msrInfo pointer to the msr-file handler
 * \param rawData raw run data
 * \param runNo number of the run within the msr-file
 * \param tag tag showing what shall be done: kFit == fitting, kView == viewing
 */
PRunNonMusr::PRunNonMusr(PMsrHandler *msrInfo, PRunDataHandler *rawData, UInt_t runNo, EPMusrHandleTag tag) : PRunBase(msrInfo, rawData, runNo, tag)
{
  // get the proper run
  fRawRunData = fRawData->GetRunData(*(fRunInfo->GetRunName()));
  if (!fRawRunData) { // couldn't get run
    cerr << endl << "PRunNonMusr::PRunNonMusr(): **ERROR** Couldn't get raw run  data!";
    cerr << endl;
    fValid = false;
  }

  // calculate fData
  if (!PrepareData())
    fValid = false;
}

//--------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
/**
 * <p>Destructor
 */
PRunNonMusr::~PRunNonMusr()
{
}

//--------------------------------------------------------------------------
// CalcChiSquare
//--------------------------------------------------------------------------
/**
 * <p>Calculate chi-square.
 *
 * <b>return:</b>
 * - chisq value
 *
 * \param par parameter vector iterated by minuit2
 */
Double_t PRunNonMusr::CalcChiSquare(const std::vector<Double_t>& par)
{
  Double_t chisq = 0.0;
  Double_t diff = 0.0;

  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  // calculate chi square
  Double_t x;
  for (UInt_t i=0; i<fData.GetValue()->size(); i++) {
    x = fData.GetX()->at(i);
    if ((x>=fFitStartTime) && (x<=fFitStopTime)) {
      diff = fData.GetValue()->at(i) - fTheory->Func(x, par, fFuncValues);
      chisq += diff*diff / (fData.GetError()->at(i)*fData.GetError()->at(i));
    }
  }

  return chisq;
}

//--------------------------------------------------------------------------
// CalcMaxLikelihood
//--------------------------------------------------------------------------
/**
 * <p>NOT IMPLEMENTED!!
 *
 * \param par parameter vector iterated by minuit2
 */
Double_t PRunNonMusr::CalcMaxLikelihood(const std::vector<Double_t>& par)
{
  cout << endl << "PRunSingleHisto::CalcMaxLikelihood(): not implemented yet ..." << endl;

  return 1.0;
}

//--------------------------------------------------------------------------
// CalcTheory
//--------------------------------------------------------------------------
/**
 * <p>Calculate theory for a given set of fit-parameters.
 */
void PRunNonMusr::CalcTheory()
{
}

//--------------------------------------------------------------------------
// PrepareData
//--------------------------------------------------------------------------
/**
 * <p>Prepare data for fitting or viewing.
 *
 * <b>return:</b>
 * - true if everthing went smooth
 * - false, otherwise.
 */
Bool_t PRunNonMusr::PrepareData()
{
  Bool_t success = true;

  if (fRunInfo->GetRunNameSize() > 1) { // ADDRUN present which is not supported for NonMusr
    cerr << endl << ">> PRunNonMusr::PrepareData(): **WARNING** ADDRUN NOT SUPPORTED FOR THIS FIT TYPE, WILL IGNORE IT." << endl;
  }

  if (fHandleTag == kFit)
    success = PrepareFitData();
  else if (fHandleTag == kView)
    success = PrepareViewData();
  else
    success = false;

  return success;
}

//--------------------------------------------------------------------------
// PrepareFitData
//--------------------------------------------------------------------------
/**
 * <p>Prepare data for fitting.
 *
 * <b>return:</b>
 * - true if everthing went smooth
 * - false, otherwise.
 */
Bool_t PRunNonMusr::PrepareFitData()
{
  Bool_t success = true;

  // keep start/stop time for fit: here the meaning is of course start x, stop x
  fFitStartTime = fRunInfo->GetFitRange(0);
  fFitStopTime  = fRunInfo->GetFitRange(1);

  // get x-, y-index
  UInt_t xIndex = GetXIndex();
  UInt_t yIndex = GetYIndex();

  // pack the raw data
  Double_t value  = 0.0;
  Double_t err = 0.0;
  for (UInt_t i=0; i<fRawRunData->fDataNonMusr.GetData()->at(xIndex).size(); i++) {
    if (fRunInfo->GetPacking() == 1) {
      fData.AppendXValue(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i));
      fData.AppendValue(fRawRunData->fDataNonMusr.GetData()->at(yIndex).at(i));
      fData.AppendErrorValue(fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i));
    } else { // packed data, i.e. fRunInfo->GetPacking() > 1
      if ((i % fRunInfo->GetPacking() == 0) && (i != 0)) { // fill data
        fData.AppendXValue(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i)-(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i)-fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i-fRunInfo->GetPacking()))/2.0);
        fData.AppendValue(value);
        fData.AppendErrorValue(TMath::Sqrt(err));
        value = 0.0;
        err = 0.0;
      }
      // sum raw data values
      value += fRawRunData->fDataNonMusr.GetData()->at(yIndex).at(i);
      err += fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i)*fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i);
    }
  }

  // count the number of bins to be fitted
  fNoOfFitBins=0;
  Double_t x;
  for (UInt_t i=0; i<fData.GetValue()->size(); i++) {
    x = fData.GetX()->at(i);
    if ((x >= fFitStartTime) && (x <= fFitStopTime))
      fNoOfFitBins++;
  }

  return success;
}

//--------------------------------------------------------------------------
// PrepareViewData
//--------------------------------------------------------------------------
/**
 * <p>Prepare data for viewing.
 *
 * <b>return:</b>
 * - true if everthing went smooth
 * - false, otherwise.
 */
Bool_t PRunNonMusr::PrepareViewData()
{
  Bool_t success = true;

  // get x-, y-index
  UInt_t xIndex = GetXIndex();
  UInt_t yIndex = GetYIndex();

  // fill data histo
  // pack the raw data
  Double_t value  = 0.0;
  Double_t err = 0.0;
  for (UInt_t i=0; i<fRawRunData->fDataNonMusr.GetData()->at(xIndex).size(); i++) {
    if (fRunInfo->GetPacking() == 1) {
      fData.AppendXValue(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i));
      fData.AppendValue(fRawRunData->fDataNonMusr.GetData()->at(yIndex).at(i));
      fData.AppendErrorValue(fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i));
    } else { // packed data, i.e. fRunInfo->GetPacking() > 1
      if ((i % fRunInfo->GetPacking() == 0) && (i != 0)) { // fill data
        fData.AppendXValue(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i)-(fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i)-fRawRunData->fDataNonMusr.GetData()->at(xIndex).at(i-fRunInfo->GetPacking()))/2.0);
        fData.AppendValue(value);
        fData.AppendErrorValue(TMath::Sqrt(err));
        value = 0.0;
        err = 0.0;
      }
      // sum raw data values
      value += fRawRunData->fDataNonMusr.GetData()->at(yIndex).at(i);
      err += fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i)*fRawRunData->fDataNonMusr.GetErrData()->at(yIndex).at(i);
    }
  }

  // count the number of bins to be fitted
  fNoOfFitBins = fData.GetValue()->size();

  // fill theory histo
  // feed the parameter vector
  std::vector<Double_t> par;
  PMsrParamList *paramList = fMsrInfo->GetMsrParamList();
  for (UInt_t i=0; i<paramList->size(); i++)
    par.push_back((*paramList)[i].fValue);
  // calculate functions
  for (Int_t i=0; i<fMsrInfo->GetNoOfFuncs(); i++) {
    fFuncValues[i] = fMsrInfo->EvalFunc(fMsrInfo->GetFuncNo(i), *fRunInfo->GetMap(), par);
  }

  // get plot range
  PMsrPlotList *plotList;
  PMsrPlotStructure plotBlock;
  plotList = fMsrInfo->GetMsrPlotList();
  // find the proper plot block
  // Here a small complication to be handled: there are potentially multiple
  // run blocks and the run might be present in various of these run blocks. In
  // order to get a nice resolution on the theory the following procedure will be
  // followed: the smallest x-interval found will be used to for the fXTheory resolution
  // which is 1000 function points. The function will be calculated from the smallest
  // xmin found up to the largest xmax found.
  Double_t xMin = 0.0, xMax = 0.0;

  // init xMin/xMax, xAbsMin/xAbsMax
  plotBlock = plotList->at(0);
  if (plotBlock.fTmin.size() == 0) { // check if no range information is present
    PMsrRunList *runList = fMsrInfo->GetMsrRunList();
    xMin = runList->at(0).GetFitRange(0);
    xMax = runList->at(0).GetFitRange(1);
    for (UInt_t i=1; i<runList->size(); i++) {
      if (runList->at(i).GetFitRange(0) < xMin)
        xMin = runList->at(i).GetFitRange(0);
      if (runList->at(i).GetFitRange(1) > xMax)
        xMax = runList->at(i).GetFitRange(1);
    }
  } else if (plotBlock.fTmin.size() == 1) { // check if 'range' information is present
    xMin = plotBlock.fTmin[0];
    xMax = plotBlock.fTmax[0];
  } else if (plotBlock.fTmin.size() > 1) { // check if 'sub_ranges' information is present
    xMin = plotBlock.fTmin[0];
    xMax = plotBlock.fTmax[0];
    for (UInt_t i=1; i<plotBlock.fTmin.size(); i++) {
      if (plotBlock.fTmin[i] < xMin)
        xMin = plotBlock.fTmin[i];
      if (plotBlock.fTmax[i] > xMax)
        xMax = plotBlock.fTmax[i];
    }
  }

  if (plotBlock.fUseFitRanges) { // check if 'use_fit_ranges' information is present
    PMsrRunList *runList = fMsrInfo->GetMsrRunList();
    xMin = runList->at(0).GetFitRange(0);
    xMax = runList->at(0).GetFitRange(1);
    for (UInt_t i=1; i<runList->size(); i++) {
      if (runList->at(i).GetFitRange(0) < xMin)
        xMin = runList->at(i).GetFitRange(0);
      if (runList->at(i).GetFitRange(1) > xMax)
        xMax = runList->at(i).GetFitRange(1);
    }
  }

  for (UInt_t i=1; i<plotList->size(); i++) { // go through all the plot blocks
    plotBlock = plotList->at(i);

    if (plotBlock.fTmin.size() == 0) { // check if no range information is present
      PMsrRunList *runList = fMsrInfo->GetMsrRunList();
      for (UInt_t i=0; i<runList->size(); i++) {
        if (runList->at(i).GetFitRange(0) < xMin)
          xMin = runList->at(i).GetFitRange(0);
        if (runList->at(i).GetFitRange(1) > xMax)
          xMax = runList->at(i).GetFitRange(1);
      }
    } else if (plotBlock.fTmin.size() == 1) { // check if 'range' information is present
      if (plotBlock.fTmin[0] < xMin)
        xMin = plotBlock.fTmin[0];
      if (plotBlock.fTmax[0] > xMax)
        xMax = plotBlock.fTmax[0];
    } else if (plotBlock.fTmin.size() > 1) { // check if 'sub_ranges' information is present
      for (UInt_t i=0; i<plotBlock.fTmin.size(); i++) {
        if (plotBlock.fTmin[i] < xMin)
          xMin = plotBlock.fTmin[i];
        if (plotBlock.fTmax[i] > xMax)
          xMax = plotBlock.fTmax[i];
      }
    }

    if (plotBlock.fUseFitRanges) { // check if 'use_fit_ranges' information is present
      PMsrRunList *runList = fMsrInfo->GetMsrRunList();
      for (UInt_t i=0; i<runList->size(); i++) {
        if (runList->at(i).GetFitRange(0) < xMin)
          xMin = runList->at(i).GetFitRange(0);
        if (runList->at(i).GetFitRange(1) > xMax)
          xMax = runList->at(i).GetFitRange(1);
      }
    }
  }

  // typically take 1000 points to calculate the theory, except if there are more data points, than take that number
  Double_t xStep;
  if (fData.GetX()->size() > 1000.0)
    xStep = (xMax-xMin)/fData.GetX()->size();
  else
    xStep = (xMax-xMin)/1000.0;

  Double_t xx = xMin;
  do {
    // fill x-vector
    fData.AppendXTheoryValue(xx);
    // fill y-vector
    fData.AppendTheoryValue(fTheory->Func(xx, par, fFuncValues));
    // calculate next xx
    xx += xStep;
  } while (xx < xMax);

  // clean up
  par.clear();

  return success;
}

//--------------------------------------------------------------------------
// GetXIndex
//--------------------------------------------------------------------------
/**
 * <p>Returns the x-axis data index.
 *
 * <b>return:</b>
 * - x-index
 */
UInt_t PRunNonMusr::GetXIndex()
{
  UInt_t index = 0;
  Bool_t found = false;

  if (fRawRunData->fDataNonMusr.FromAscii()) { // ascii-file format
    index = 0;
    found = true;
  } else { // db-file format
    if (fRunInfo->GetXDataIndex() > 0) { // xy-data already indices
      index = fRunInfo->GetXDataIndex()-1; // since xy-data start with 1 ...
      found = true;
    } else { // xy-data data tags which needs to be converted to an index
      for (UInt_t i=0; i<fRawRunData->fDataNonMusr.GetDataTags()->size(); i++) {
        if (fRawRunData->fDataNonMusr.GetDataTags()->at(i).CompareTo(*fRunInfo->GetXDataLabel()) == 0) {
          index = i;
          found = true;
          break;
        }
      }
    }
  }

  if (!found) {
    cerr << endl << "PRunNonMusr::GetXIndex(): **ERROR** Couldn't obtain x-data index!";
    cerr << endl;
    assert(0);
  }

  return index;
}

//--------------------------------------------------------------------------
// GetYIndex
//--------------------------------------------------------------------------
/**
 * <p>Returns the y-axis data index.
 *
 * <b>return:</b>
 * - y-index
 */
UInt_t PRunNonMusr::GetYIndex()
{
  UInt_t index = 0;
  Bool_t found = false;

  if (fRawRunData->fDataNonMusr.FromAscii()) { // ascii-file format
    index = 1;
    found = true;
  } else { // db-file format
    if (fRunInfo->GetYDataIndex() > 0) { // xy-data already indices
      index = fRunInfo->GetYDataIndex()-1; // since xy-data start with 1 ...
      found = true;
    } else { // xy-data data tags which needs to be converted to an index
      for (UInt_t i=0; i<fRawRunData->fDataNonMusr.GetDataTags()->size(); i++) {
        if (fRawRunData->fDataNonMusr.GetDataTags()->at(i).CompareTo(*fRunInfo->GetYDataLabel()) == 0) {
          index = i;
          found = true;
          break;
        }
      }
    }
  }

  if (!found) {
    cerr << endl << "PRunNonMusr::GetYIndex(): **ERROR** Couldn't obtain y-data index!";
    cerr << endl;
    assert(0);
  }

  return index;
}
