/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Felix Weiglhofer [committer] */
#ifndef CBM_ALGO_BASE_RECOPARAMS_H
#define CBM_ALGO_BASE_RECOPARAMS_H

#include "Definitions.h"
#include "config/Property.h"
#include "util/EnumDict.h"

#include <xpu/defines.h>

namespace cbm::algo
{

  /**
   * @brief RecoParams contains all parameters to configure reconstruction
   */
  struct RecoParams {
    enum class SortMode : u8
    {
      BlockSort        = 0,
      CUBSegmentedSort = 1,
    };
    enum class UnpackMode : u8
    {
      CPU,
      XPU,
    };
    enum class AllocationMode : u8
    {
      Auto,     //< Static on GPU, dynamic on CPU
      Static,   //< Allocate all buffers beforehand
      Dynamic,  //< Allocate buffers per timeslice
    };

    struct STS {
      SortMode digiSortMode;
      SortMode clusterSortMode;

      UnpackMode unpackMode;
      u8 findClustersMultiKernels;

      f32 timeCutDigiAbs;
      f32 timeCutDigiSig;
      f32 timeCutClusterAbs;
      f32 timeCutClusterSig;

      struct Memory {
        AllocationMode allocationMode;
        u64 maxNDigisPerTS;
        u64 maxNDigisPerModule;
        f64 clustersPerDigi;
        f64 hitsPerCluster;

        u64 NClustersUpperBound(u64 nDigis) const { return nDigis * clustersPerDigi; }
        u64 NHitsUpperBound(u64 nDigis) const { return NClustersUpperBound(nDigis) * hitsPerCluster; }

        u64 MaxNClustersPerModule() const { return NClustersUpperBound(maxNDigisPerModule); }
        u64 MaxNHitsPerModule() const { return MaxNClustersPerModule() * hitsPerCluster; }

        bool IsDynamic() const { return allocationMode == RecoParams::AllocationMode::Dynamic; }
        bool IsStatic() const { return allocationMode == RecoParams::AllocationMode::Static; }
        bool IsAuto() const { return allocationMode == RecoParams::AllocationMode::Auto; }

        static constexpr auto Properties = std::make_tuple(
          config::Property(&Memory::allocationMode, "allocationMode", "Allocation mode (Auto, Static, Dynamic)"),
          config::Property(&Memory::maxNDigisPerTS, "maxNDigisPerTS", "Maximal number of digis per time slice"),
          config::Property(&Memory::maxNDigisPerModule, "maxNDigisPerModule", "Maximal number of digis per module"),
          config::Property(&Memory::clustersPerDigi, "clustersPerDigi", "Number of clusters per digi in a time slice"),
          config::Property(&Memory::hitsPerCluster, "hitsPerCluster", "Number of hits per cluster in a time slice"));
      } memory;

      static constexpr auto Properties = std::make_tuple(
        config::Property(&STS::digiSortMode, "digiSortMode",
                         "Digi sort mode (0 = block sort, 1 = cub segmented sort))"),
        config::Property(&STS::clusterSortMode, "clusterSortMode", "Cluster sort mode"),

        config::Property(&STS::unpackMode, "unpackMode", "Unpack mode (0 = legacy, 1 = XPU)"),
        config::Property(&STS::findClustersMultiKernels, "findClustersMultiKernels",
                         "Split cluster finding into multiple kernels"),

        config::Property(&STS::timeCutDigiAbs, "timeCutDigiAbs",
                         "Time delta for neighboring digis to be considered for the same cluster. [ns]"),
        config::Property(
          &STS::timeCutDigiSig, "timeCutDigiSig",
          "Used if timeCutDigiAbs is negative. Time delta must be < 'value * sqrt2 * timeResolution'. [ns]"),
        config::Property(&STS::timeCutClusterAbs, "timeCutClusterAbs",
                         "Maximal time difference between two clusters in a hit [ns]."
                         " Setting to a positive value will override timeCutClustersSig."),
        config::Property(
          &STS::timeCutClusterSig, "timeCutClusterSig",
          "Time cut for clusters."
          " Two clusters are considered it their time difference is below 'value * sqrt(terr1**2 + terr2*+2)'"),
        config::Property(&STS::memory, "memory", "Memory limits for STS reco"));
    };

    STS sts;

    static constexpr auto Properties = std::make_tuple(config::Property(&RecoParams::sts, "sts", "STS reco settings"));
  };

};  // namespace cbm::algo

CBM_ENUM_DICT(cbm::algo::RecoParams::SortMode,
  {"BlockSort", RecoParams::SortMode::BlockSort},
  {"CUBSegmentedSort", RecoParams::SortMode::CUBSegmentedSort}
);

CBM_ENUM_DICT(cbm::algo::RecoParams::UnpackMode,
  {"CPU", RecoParams::UnpackMode::CPU},
  {"XPU", RecoParams::UnpackMode::XPU}
);

CBM_ENUM_DICT(cbm::algo::RecoParams::AllocationMode,
  {"Auto", RecoParams::AllocationMode::Auto},
  {"Static", RecoParams::AllocationMode::Static},
  {"Dynamic", RecoParams::AllocationMode::Dynamic}
);

#endif  // CBM_ALGO_BASE_RECOPARAMS_H