// Copyright 2022, Dmitry Romanov
// Subject to the terms in the LICENSE file found in the top-level directory.
//

// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2024, Dmitry Kalinkin

#include <Evaluator/DD4hepUnits.h>
#include <JANA/JApplication.h>
#include <JANA/JApplicationFwd.h>
#include <JANA/Utils/JTypeInfo.h>
#include <TMath.h>
#include <edm4eic/unit_system.h>
#include <edm4hep/SimTrackerHit.h>
#include <cmath>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "algorithms/digi/SiliconChargeSharingConfig.h"
#include "extensions/jana/JOmniFactoryGeneratorT.h"
#include "factories/digi/CFDROCDigitization_factory.h"
#include "factories/digi/PulseCombiner_factory.h"
#include "factories/digi/PulseGeneration_factory.h"
#include "factories/digi/SiliconChargeSharing_factory.h"
#include "factories/digi/SiliconPulseDiscretization_factory.h"
#include "factories/digi/SiliconTrackerDigi_factory.h"
#include "factories/reco/LGADHitCalibration_factory.h"
#include "factories/tracking/LGADHitClustering_factory.h"
#include "factories/tracking/TrackerHitReconstruction_factory.h"

extern "C" {
void InitPlugin(JApplication* app) {
  InitJANAPlugin(app);

  using namespace eicrecon;

  // Convert raw digitized hits into calibrated hits
  // time walk correction is still TBD
  app->Add(new JOmniFactoryGeneratorT<LGADHitCalibration_factory>(
      "TOFBarrelCalibratedHits", {"TOFBarrelADCTDC"}, // Input data collection tags
      {"TOFBarrelCalibratedHits"},                    // Output data tag
      {},
      app)); // Hit reco default config for factories

  // cluster all hits in a sensor into one hit location
  // Currently it's just a simple weighted average
  // More sophisticated algorithm TBD
  app->Add(new JOmniFactoryGeneratorT<LGADHitClustering_factory>(
      "TOFBarrelClusterHits", {"TOFBarrelSharedRecHits"}, // Input data collection tags
      {"TOFBarrelClusterHits"},                           // Output data tag
      {
          .readout = "TOFBarrelHits",
          .useAve  = true,
      },
      app));

  app->Add(new JOmniFactoryGeneratorT<SiliconChargeSharing_factory>(
      "TOFBarrelSharedHits", {"TOFBarrelHits"}, {"TOFBarrelSharedHits"},
      {
          .sigma_mode     = SiliconChargeSharingConfig::ESigmaMode::rel,
          .sigma_sharingx = 0.5,
          .sigma_sharingy = 0.5,
          .min_edep       = 6.0 * edm4eic::unit::keV,
          .readout        = "TOFBarrelHits",
      },
      app));

  // temporary steps to bypass pulse digitization and jump right from ChargeSharing to clusters
  // Avoid efficiency loss until we can simulate hardware accurately
  app->Add(new JOmniFactoryGeneratorT<SiliconTrackerDigi_factory>(
      "TOFBarrelSharedRawHits", {"EventHeader", "TOFBarrelSharedHits"},
      {"TOFBarrelSharedRawHits", "TOFBarrelSharedRawHitLinks", "TOFBarrelSharedRawHitAssociations"},
      {
          .threshold      = 0.0,
          .timeResolution = 0.025, // [ns]
      },
      app));

  // Convert raw digitized hits into hits with geometry info (ready for tracking)
  app->Add(new JOmniFactoryGeneratorT<TrackerHitReconstruction_factory>(
      "TOFBarrelSharedRecHits", {"TOFBarrelSharedRawHits"}, // Input data collection tags
      {"TOFBarrelSharedRecHits"},                           // Output data tag
      {},
      app)); // Hit reco default config for factories

  // calculation of the extreme values for Landau distribution can be found on lin 514-520 of
  // https://root.cern.ch/root/html524/src/TMath.cxx.html#fsokrB Landau reaches minimum for mpv =
  // 0 and sigma = 1 at x = -0.22278
  const double x_when_landau_min = -0.22278;
  const double landau_min        = TMath::Landau(x_when_landau_min, 0, 1, true);
  const double sigma_analog      = 0.293951 * edm4eic::unit::ns;
  const double Vm                = 3e-4 * dd4hep::GeV;
  const double adc_range         = 256;
  // gain is set such that pulse reaches a height of adc_range when EDep = Vm
  // gain is negative as LGAD voltage is always negative
  const double gain = -adc_range / Vm / landau_min * sigma_analog;
  const int offset  = 3;
  app->Add(new JOmniFactoryGeneratorT<PulseGeneration_factory<edm4hep::SimTrackerHit>>(
      "LGADPulseGeneration", {"TOFBarrelSharedHits"}, {"TOFBarrelSmoothPulses"},
      {
          .pulse_shape_function = "LandauPulse",
          .pulse_shape_params   = {gain, sigma_analog, offset},
          .ignore_thres         = 0.05 * adc_range,
          .timestep             = 0.01 * edm4eic::unit::ns,
      },
      app));

  app->Add(new JOmniFactoryGeneratorT<PulseCombiner_factory>(
      "TOFBarrelPulseCombiner", {"TOFBarrelSmoothPulses"}, {"TOFBarrelCombinedPulses"},
      {
          .minimum_separation = 25 * edm4eic::unit::ns,
      },
      app));

  double risetime = 0.45 * edm4eic::unit::ns;
  app->Add(new JOmniFactoryGeneratorT<SiliconPulseDiscretization_factory>(
      "TOFBarrelPulses", {"TOFBarrelCombinedPulses"}, {"TOFBarrelPulses"},
      {
          .EICROC_period = 25 * edm4eic::unit::ns,
          .local_period  = 25 * edm4eic::unit::ns / 1024,
          .global_offset = -offset * sigma_analog + risetime,
      },
      app));

  app->Add(new JOmniFactoryGeneratorT<CFDROCDigitization_factory>(
      "CFDROCDigitization", {"TOFBarrelPulses"}, {"TOFBarrelADCTDC"}, {}, app));
}
} // extern "C"
