/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Volker Friese [committer] */

#include "EventBuilder.h"

#include <cassert>
#include <iomanip>

#include <xpu/host.h>

using std::is_sorted;
using std::vector;

namespace cbm::algo::evbuild
{

  // -----   Algorithm execution   --------------------------------------------
  EventBuilder::resultType EventBuilder::operator()(const DigiData& ts, const vector<double> triggers,
                                                    std::optional<DigiEventSelector> selector) const
  {
    xpu::push_timer("EventBuilder");
    xpu::t_add_bytes(ts.TotalSizeBytes());

    // --- Output data
    resultType result = {};
    auto& events      = result.first;
    events.resize(triggers.size());

    std::transform(triggers.begin(), triggers.end(), events.begin(),
                   [&ts, &result, this](const double& trigger) { return BuildEvent(ts, result.second, trigger); });

    // --- Apply event selector
    if (selector.has_value()) {
      auto notSelected = [&](DigiEvent& ev) { return !((*selector)(ev)); };
      auto removeIt    = std::remove_if(events.begin(), events.end(), notSelected);
      events.erase(removeIt, events.end());
    }

    EventBuilderMonitorData& monitor = result.second;

    monitor.sts.nDigis += ts.fSts.size();
    monitor.rich.nDigis += ts.fRich.size();
    monitor.much.nDigis += ts.fMuch.size();
    monitor.trd.nDigis += ts.fTrd.size();
    monitor.trd2d.nDigis += ts.fTrd2d.size();
    monitor.tof.nDigis += ts.fTof.size();
    monitor.psd.nDigis += ts.fPsd.size();
    monitor.fsd.nDigis += ts.fFsd.size();
    monitor.bmon.nDigis += ts.fBmon.size();
    monitor.numTriggers += triggers.size();
    monitor.numEvents += result.first.size();

    monitor.time = xpu::pop_timer();
    return result;
  }

  // --- Build a single event
  DigiEvent EventBuilder::BuildEvent(const DigiData& ts, EventBuilderMonitorData& monitor, double trigger) const
  {
    DigiEvent event;
    event.fTime = trigger;

    // --- Loop over systems
    for (auto entry : fConfig.fWindows) {

      auto system       = entry.first;
      const double tMin = trigger + entry.second.first;
      const double tMax = trigger + entry.second.second;

      // --- Build the event using trigger window
      switch (system) {
        case ECbmModuleId::kSts: {
          event.fSts = CopyRange(ts.fSts, tMin, tMax);
          break;
        }
        case ECbmModuleId::kRich: {
          event.fRich = CopyRange(ts.fRich, tMin, tMax);
          break;
        }
        case ECbmModuleId::kMuch: {
          event.fMuch = CopyRange(ts.fMuch, tMin, tMax);
          break;
        }
        case ECbmModuleId::kTrd: {
          event.fTrd = CopyRange(ts.fTrd, tMin, tMax);
          break;
        }
        case ECbmModuleId::kTrd2d: {
          event.fTrd2d = CopyRange(ts.fTrd2d, tMin, tMax);
          break;
        }
        case ECbmModuleId::kTof: {
          event.fTof = CopyRange(ts.fTof, tMin, tMax);
          break;
        }
        case ECbmModuleId::kPsd: {
          event.fPsd = CopyRange(ts.fPsd, tMin, tMax);
          break;
        }
        case ECbmModuleId::kFsd: {
          event.fFsd = CopyRange(ts.fFsd, tMin, tMax);
          break;
        }
        case ECbmModuleId::kBmon: {
          event.fBmon = CopyRange(ts.fBmon, tMin, tMax);
          break;
        }
        default: break;
      }
    }
    monitor.sts.nDigisInEvents += event.fSts.size();
    monitor.rich.nDigisInEvents += event.fRich.size();
    monitor.much.nDigisInEvents += event.fMuch.size();
    monitor.trd.nDigisInEvents += event.fTrd.size();
    monitor.trd2d.nDigisInEvents += event.fTrd2d.size();
    monitor.tof.nDigisInEvents += event.fTof.size();
    monitor.psd.nDigisInEvents += event.fPsd.size();
    monitor.fsd.nDigisInEvents += event.fFsd.size();
    monitor.bmon.nDigisInEvents += event.fBmon.size();
    return event;
  }
  // --------------------------------------------------------------------------


  // -----   Info to string   -------------------------------------------------
  std::string EventBuilder::ToString() const
  {
    std::stringstream out;
    out << "--- Using EventBuilder with event windows:";
    for (const auto& entry : fConfig.fWindows) {
      out << "\n  " << std::left << std::setw(5) << ::ToString(entry.first) << ": ";
      out << "  [" << std::right << std::setw(5) << entry.second.first;
      out << ", " << std::right << std::setw(5) << entry.second.second << "] ns";
    }
    return out.str();
  }
  // --------------------------------------------------------------------------

}  // namespace cbm::algo::evbuild