// Classname: TFitConstraintMGaus
// Author: Jan E. Sundermann, Verena Klose (TU Dresden)

//________________________________________________________________
//
// TFitConstraintMGaus::
// --------------------
//
// Fit constraint: mass conservation ( m_i - m_j - alpha * MassConstraint == 0 )
//

#include <iostream>
#include <iomanip>
#include "PhysicsTools/KinFitter/interface/TFitConstraintMGaus.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "TClass.h"

//----------------
// Constructor --
//----------------
TFitConstraintMGaus::TFitConstraintMGaus() : TFitConstraintM() { init(); }

TFitConstraintMGaus::TFitConstraintMGaus(std::vector<TAbsFitParticle*>* ParList1,
                                         std::vector<TAbsFitParticle*>* ParList2,
                                         Double_t Mass,
                                         Double_t Width)
    : TFitConstraintM(ParList1, ParList2, Mass) {
  init();
  setMassConstraint(Mass, Width);
}

TFitConstraintMGaus::TFitConstraintMGaus(const TString& name,
                                         const TString& title,
                                         std::vector<TAbsFitParticle*>* ParList1,
                                         std::vector<TAbsFitParticle*>* ParList2,
                                         Double_t Mass,
                                         Double_t Width)
    : TFitConstraintM(name, title, ParList1, ParList2, Mass) {
  init();
  setMassConstraint(Mass, Width);
}

void TFitConstraintMGaus::init() {
  _nPar = 1;
  _iniparameters.ResizeTo(1, 1);
  _iniparameters(0, 0) = 1.;
  _parameters.ResizeTo(1, 1);
  _parameters = _iniparameters;
}

//--------------
// Destructor --
//--------------
TFitConstraintMGaus::~TFitConstraintMGaus() {}

//--------------
// Operations --
//--------------

void TFitConstraintMGaus::setMassConstraint(Double_t Mass, Double_t Width) {
  _TheMassConstraint = Mass;
  _width = Width;
  setCovMatrix(nullptr);
  if (!Mass)
    throw cms::Exception("Configuration") << "Error occured!\n"
                                          << "Object type : TFitConstraintMGaus\n"
                                          << "Object name : " << GetName() << "\n"
                                          << "Object title: " << GetTitle() << "\n"
                                          << "Mass of 0 GeV not supported, please choose a larger mass!\n";
  _covMatrix(0, 0) = (Width * Width) / (Mass * Mass);
}

Double_t TFitConstraintMGaus::getInitValue() {
  // Get initial value of constraint (before the fit)

  Double_t InitValue =
      CalcMass(&_ParList1, true) - CalcMass(&_ParList2, true) - _iniparameters(0, 0) * _TheMassConstraint;

  return InitValue;
}

Double_t TFitConstraintMGaus::getCurrentValue() {
  // Get value of constraint after the fit

  Double_t CurrentValue =
      CalcMass(&_ParList1, false) - CalcMass(&_ParList2, false) - _parameters(0, 0) * _TheMassConstraint;

  return CurrentValue;
}

TMatrixD* TFitConstraintMGaus::getDerivativeAlpha() {
  // Calculate dF/dAlpha = -1 * M

  TMatrixD* DerivativeMatrix = new TMatrixD(1, 1);
  DerivativeMatrix->Zero();

  (*DerivativeMatrix)(0, 0) = -1. * _TheMassConstraint;

  return DerivativeMatrix;
}

TString TFitConstraintMGaus::getInfoString() {
  // Collect information to be used for printout

  std::stringstream info;
  info << std::scientific << std::setprecision(6);

  info << "__________________________" << std::endl << std::endl;
  info << "OBJ: " << IsA()->GetName() << "\t" << GetName() << "\t" << GetTitle() << std::endl;

  info << "initial value: " << getInitValue() << std::endl;
  info << "current value: " << getCurrentValue() << std::endl;
  info << "mean mass: " << _TheMassConstraint << std::endl;
  info << "width: " << _width << std::endl;
  info << "initial mass: " << _iniparameters(0, 0) * _TheMassConstraint << std::endl;
  info << "current mass: " << _parameters(0, 0) * _TheMassConstraint << std::endl;

  return info.str();
}

void TFitConstraintMGaus::print() {
  // Print constraint contents

  edm::LogVerbatim("KinFitter") << this->getInfoString();
}
