// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once

#include "Application.h"
#include "ApplicationModule.h"
#include "HierarchyModifyingGroup.h"
#include "ScalarAccessor.h"
#include "VariableGroup.h"

#include <ChimeraTK/SupportedUserTypes.h>

#include <map>

namespace ChimeraTK {

  /**
   *  Module which gathers statistics on data loss inside the application. It will
   * read the data loss counter once per trigger and update the output statistics
   * variables.
   */
  template<typename TRIGGERTYPE = int32_t>
  struct DataLossCounter : ApplicationModule {
    DataLossCounter(ModuleGroup* owner, const std::string& name, const std::string& description,
        const std::string& pathToTrigger, const std::unordered_set<std::string>& tags = {})
    : ApplicationModule(owner, name, description, tags), directTrigger(this, pathToTrigger, "", "Trigger Input"),
      trigger(directTrigger) {}

    /**
     *  Construct a DataLossCounter object.
     *
     *  pathToTrigger is a qualified name of the trigger source. It should start with "/" or ".." to denote an absolute
     *  resp. relative path. Note that relative paths are relative to the DataLossCounter itself.
     */
    [[deprecated("Use constructor without explicit hierarchy and qualified path instead")]] DataLossCounter(
        EntityOwner* owner, const std::string& name, const std::string& description, const std::string& pathToTrigger,
        HierarchyModifier hierarchyModifier = HierarchyModifier::none, const std::unordered_set<std::string>& tags = {})
    : ApplicationModule(owner, name, description, hierarchyModifier, tags),
      directTrigger(this, pathToTrigger, "", "Trigger Input"), trigger(directTrigger) {}

    /// Deprecated form of the constructor for backwards compatibility only.
    [[deprecated("Use constructor without explicit hierarchy and qualified path instead")]] DataLossCounter(
        EntityOwner* owner, const std::string& name, const std::string& description,
        HierarchyModifier hierarchyModifier = HierarchyModifier::none, const std::unordered_set<std::string>& tags = {})
    : ApplicationModule(owner, name, description, hierarchyModifier, tags),
      triggerGroup_compat(this, "TriggerGroup", "", HierarchyModifier::hideThis), trigger(triggerGroup_compat.trigger) {
    }

    DataLossCounter() {}

    ScalarPushInput<TRIGGERTYPE> directTrigger;

    struct TriggerGroup_compat : VariableGroup {
      using VariableGroup::VariableGroup;
      ScalarPushInput<TRIGGERTYPE> trigger{this, "trigger", "", "Trigger input"};
    } triggerGroup_compat;

    // This is for backwards compatibility!
    ScalarPushInput<TRIGGERTYPE>& trigger;

    ScalarOutput<uint64_t> lostDataInLastTrigger{this, "lostDataInLastTrigger", "",
        "Number of data transfers during "
        "the last trigger which resulted in data loss."};
    ScalarOutput<uint64_t> triggersWithDataLoss{this, "triggersWithDataLoss", "",
        "Number of trigger periods during "
        "which at least on data transfer resulted in data loss."};

    void mainLoop() override {
      while(true) {
        trigger.read();
        uint64_t counter = Application::getAndResetDataLossCounter();
        lostDataInLastTrigger = counter;
        if(counter > 0) ++triggersWithDataLoss;
        writeAll();
      }
    }
  };

} // namespace ChimeraTK