/* Copyright (C) 2012-2020 Institute for Nuclear Research, Moscow
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Alla Maevskaya, Selim Seddiki, Sergey Morozov [committer], Volker Friese, Evgeny Kashirin */

// -------------------------------------------------------------------------
// -----                CbmPsdSimpleDigitizer source file              -----
// -----                  Created 15/05/12  by  Alla & SELIM & FLORIAN -----
// -----                 Modified 17/03/18  by  Sergey Morozov         -----
// -------------------------------------------------------------------------
#include "CbmPsdSimpleDigitizer.h"

#include "CbmPsdDigi.h"
#include "CbmPsdPoint.h"

#include "FairRootManager.h"
#include <Logger.h>

#include "TClonesArray.h"
#include "TMath.h"
#include "TStopwatch.h"

#include <cassert>
#include <iomanip>
#include <iostream>
#include <map>

using std::cout;
using std::endl;
using std::fixed;
using std::right;
using std::setprecision;
using std::setw;

using std::map;
using std::pair;


// -----   Default constructor   -------------------------------------------
CbmPsdSimpleDigitizer::CbmPsdSimpleDigitizer()
  : CbmDigitize<CbmPsdDigi>("PsdDigitize")
  , fNofEvents(0)
  , fNofPoints(0.)
  , fNofDigis(0.)
  , fTimeTot(0.)
  , fPointArray(NULL)
{
}
// -------------------------------------------------------------------------


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


// -----   Public method Init   --------------------------------------------
InitStatus CbmPsdSimpleDigitizer::Init()
{

  // Matches are not produced
  fCreateMatches = kFALSE;

  // Get RootManager
  FairRootManager* ioman = FairRootManager::Instance();
  assert(ioman),

    // Get input array
    fPointArray = (TClonesArray*) ioman->GetObject("PsdPoint");
  assert(fPointArray);

  // Create and register output array
  RegisterOutput();

  // Statistics
  fNofEvents = 0;
  fNofPoints = 0;
  fNofDigis  = 0.;
  fTimeTot   = 0.;

  LOG(info) << fName << ": Initialisation successful " << kSUCCESS;
  return kSUCCESS;
}
// -------------------------------------------------------------------------


// -----   Public method Exec   --------------------------------------------
void CbmPsdSimpleDigitizer::Exec(Option_t*)
{
  // Event info (for event time)
  GetEventInfo();

  TStopwatch timer;
  timer.Start();

  // Loop over PsdPoints
  Int_t nPoints = fPointArray->GetEntriesFast();
  LOG(debug) << fName << ": processing event " << fCurrentEvent << " at t = " << fCurrentEventTime << " ns";

  // Declare some variables
  CbmPsdPoint* point = nullptr;
  Int_t modID        = -1;  // module ID
  Int_t scinID       = -1;  // #sciillator
  Int_t sec;
  std::map<UInt_t, CbmPsdDigi> fired_digis_map;  // map<UInt_t uAddress, CbmPsdDigi>

  for (Int_t iPoint = 0; iPoint < nPoints; iPoint++) {
    point = (CbmPsdPoint*) fPointArray->At(iPoint);
    if (!point) continue;

    modID          = point->GetModuleID();    //marina  1-44 (45)
    scinID         = point->GetDetectorID();  //1-60
    Double_t eLoss = point->GetEnergyLoss();
    Double_t pTime     = point->GetTime();
    sec                = (Int_t)((scinID - 1) / 6) + 1;  //marina   1-10
    UInt_t uAddress    = CbmPsdAddress::GetAddress(modID, sec);

    auto it = fired_digis_map.find(uAddress);
    if (it != fired_digis_map.end()) {
      //this key exists
      it->second.SetEdep(it->second.GetEdep() + eLoss);
      if (pTime < it->second.GetTime()) it->second.SetTime(pTime);
    }
    else {
      //this key is new
      CbmPsdDigi digi = CbmPsdDigi(uAddress, pTime, eLoss);
      fired_digis_map.insert(std::make_pair(uAddress, digi));
    }
  }  // Loop over MCPoints

  Int_t nDigis = 0;
  for (auto entry : fired_digis_map) {
    Double_t eDep            = entry.second.GetEdep();
    Double_t eLossMIP        = eDep / 0.005;  // 5MeV per MIP
    Double_t pixPerMIP       = 15.;           // 15 pix per MIP
    Double_t eLossMIPSmeared = gRandom->Gaus(eLossMIP * pixPerMIP, sqrt(eLossMIP * pixPerMIP)) / pixPerMIP;
    Double_t eLossSmeared    = eLossMIPSmeared * 0.005;
    Double_t eNoise          = gRandom->Gaus(0, 15) / 50. * 0.005;
    eLossSmeared += eNoise;
    // The digi time is set to MC point time [relative to event start] + Event Start time
    CbmPsdDigi* digi = new CbmPsdDigi(entry.second.GetAddress(), entry.second.GetTime() + fCurrentEventTime, eLossSmeared);
    SendData(digi);
    nDigis++;
    LOG(debug1) << fName << ": Digi " << nDigis << " Time " << entry.second.GetTime() + fCurrentEventTime << " Section "
               << entry.second.GetSectionID() << " Module " << entry.second.GetModuleID() << " energy " << eLossSmeared;
  }

  // --- Event log
  timer.Stop();
  LOG(info) << "+ " << setw(15) << GetName() << ": Event " << setw(6) << right << fCurrentEvent << " at " << fixed
            << setprecision(3) << fCurrentEventTime << " ns, points: " << nPoints << ", digis: " << nDigis
            << ". Exec time " << setprecision(6) << timer.RealTime() << " s.";

  // --- Run statistics
  fNofEvents++;
  fNofPoints += nPoints;
  fNofDigis += nDigis;
  fTimeTot += timer.RealTime();
}
// -------------------------------------------------------------------------


// -----   End-of-run   ----------------------------------------------------
void CbmPsdSimpleDigitizer::Finish()
{
  std::cout << std::endl;
  LOG(info) << "=====================================";
  LOG(info) << GetName() << ": Run summary";
  LOG(info) << "Events processed    : " << fNofEvents;
  LOG(info) << "PsdPoint / event    : " << setprecision(1) << fNofPoints / Double_t(fNofEvents);
  LOG(info) << "PsdDigi / event     : " << fNofDigis / Double_t(fNofEvents);
  LOG(info) << "Digis per point     : " << setprecision(6) << fNofDigis / fNofPoints;
  LOG(info) << "Real time per event : " << fTimeTot / Double_t(fNofEvents) << " s";
  LOG(info) << "=====================================";
}
// -------------------------------------------------------------------------


ClassImp(CbmPsdSimpleDigitizer)