// -*- C++ -*-
//
// This file is part of YODA -- Yet more Objects for Data Analysis
// Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
//
#ifndef YODA_BinUtils_h
#define YODA_BinUtils_h

#include "YODA/Utils/BinningUtils.h"

namespace YODA {

  /// @name Mixin of convenience methods using CRTP
  /// @{

  /// @brief CRTP mixin introducing convenience aliases along X axis.
  template <class Derived, typename EdgeT = double>
  struct XBinMixin {

    /// @name Bin-interval properties
    /// @{

    /// @brief Upper bin edge along X axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> xMax() const noexcept {
      return static_cast<const Derived*>(this)->template max<0>();
    }

    /// @brief Lower bin edge along X axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> xMin() const noexcept {
      return static_cast<const Derived*>(this)->template min<0>();
    }

    /// @brief Bin centre along X axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> xMid() const noexcept {
      return static_cast<const Derived*>(this)->template mid<0>();
    }

    /// @brief Bin width along X axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> xWidth() const noexcept {
      return static_cast<const Derived*>(this)->template width<0>();
    }

    /// @brief Bin edge along X axis.
    ///
    /// @note These are only supported for a discrete axis
    template<typename T = EdgeT>
    enable_if_DAxisT<T> xEdge() const noexcept {
      return static_cast<const Derived*>(this)->template edge<0>();
    }

    /// @}

  };


  /// @brief CRTP mixin introducing convenience aliases along Y axis.
  template <class Derived, typename EdgeT = double>
  struct YBinMixin {

    /// @name Bin-interval properties
    /// @{

    /// @brief Upper bin edge along Y axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> yMax() const noexcept {
      return static_cast<const Derived*>(this)->template max<1>();
    }

    /// @brief Lower bin edge along Y axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> yMin() const noexcept {
      return static_cast<const Derived*>(this)->template min<1>();
    }

    /// @brief Bin centre along Y axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> yMid() const noexcept {
      return static_cast<const Derived*>(this)->template mid<1>();
    }

    /// @brief Bin width along Y axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> yWidth() const noexcept {
      return static_cast<const Derived*>(this)->template width<1>();
    }

    /// @brief Bin edge along Y axis.
    ///
    /// @note These are only supported for a discrete axis
    template<typename T = EdgeT>
    enable_if_DAxisT<T> yEdge() const noexcept {
      return static_cast<const Derived*>(this)->template edge<1>();
    }

    /// @}

  };


  /// @brief CRTP mixin introducing convenience aliases along Z axis.
  template <class Derived, typename EdgeT = double>
  struct ZBinMixin {

    /// @name Bin-interval properties
    /// @{

    /// @brief Upper bin edge along Z axis.
    template<typename T = EdgeT>
    enable_if_CAxisT<T> zMax() const noexcept {
      return static_cast<const Derived*>(this)->template max<2>();
    }

    /// @brief Lower bin edge along Z axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> zMin() const noexcept {
      return static_cast<const Derived*>(this)->template min<2>();
    }

    /// @brief Bin centre along Z axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> zMid() const noexcept {
      return static_cast<const Derived*>(this)->template mid<2>();
    }

    /// @brief Bin width along Z axis.
    ///
    /// @note These are only supported for a continuous axis
    template<typename T = EdgeT>
    enable_if_CAxisT<T> zWidth() const noexcept {
      return static_cast<const Derived*>(this)->template width<2>();
    }

    /// @brief Bin edge along Z axis.
    ///
    /// @note These are only supported for a discrete axis
    template<typename T = EdgeT>
    enable_if_DAxisT<T> zEdge() const noexcept {
      return static_cast<const Derived*>(this)->template edge<2>();
    }

    /// @}

  };

  /// @}

}

#endif
