/// \file ROOT/RNTupleRange.hxx
/// \ingroup NTuple
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2018-10-05

/*************************************************************************
 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_RNTupleRange
#define ROOT_RNTupleRange

#include <ROOT/RNTupleTypes.hxx>

namespace ROOT {

// clang-format off
/**
\class ROOT::RNTupleGlobalRange
\ingroup NTuple
\brief Used to loop over indexes (entries or collections) between start and end
*/
// clang-format on
class RNTupleGlobalRange {
private:
   ROOT::NTupleSize_t fStart;
   ROOT::NTupleSize_t fEnd;

public:
   class RIterator {
   private:
      ROOT::NTupleSize_t fIndex = ROOT::kInvalidNTupleIndex;

   public:
      using iterator = RIterator;
      using iterator_category = std::input_iterator_tag;
      using value_type = ROOT::NTupleSize_t;
      using difference_type = std::ptrdiff_t;
      using pointer = const ROOT::NTupleSize_t *;
      using reference = const ROOT::NTupleSize_t &;

      RIterator() = default;
      explicit RIterator(ROOT::NTupleSize_t index) : fIndex(index) {}
      ~RIterator() = default;

      iterator operator++(int) /* postfix */
      {
         auto r = *this;
         fIndex++;
         return r;
      }
      iterator &operator++() /* prefix */
      {
         ++fIndex;
         return *this;
      }
      reference operator*() const { return fIndex; }
      pointer operator->() const { return &fIndex; }
      bool operator==(const iterator &rh) const { return fIndex == rh.fIndex; }
      bool operator!=(const iterator &rh) const { return fIndex != rh.fIndex; }
   };

   RNTupleGlobalRange(ROOT::NTupleSize_t start, ROOT::NTupleSize_t end) : fStart(start), fEnd(end) {}
   RIterator begin() const { return RIterator(fStart); }
   RIterator end() const { return RIterator(fEnd); }
   ROOT::NTupleSize_t size() const { return fEnd - fStart; }
   bool IsValid() const { return (fStart != ROOT::kInvalidNTupleIndex) && (fEnd != ROOT::kInvalidNTupleIndex); }
};

// clang-format off
/**
\class ROOT::RNTupleLocalRange
\ingroup NTuple
\brief Used to loop over entries of collections in a single cluster
*/
// clang-format on
class RNTupleLocalRange {
private:
   ROOT::DescriptorId_t fClusterId;
   ROOT::NTupleSize_t fStart;
   ROOT::NTupleSize_t fEnd;

public:
   class RIterator {
   private:
      RNTupleLocalIndex fLocalIndex;

   public:
      using iterator = RIterator;
      using iterator_category = std::input_iterator_tag;
      using value_type = RNTupleLocalIndex;
      using difference_type = std::ptrdiff_t;
      using pointer = const RNTupleLocalIndex *;
      using reference = const RNTupleLocalIndex &;

      RIterator() = default;
      explicit RIterator(RNTupleLocalIndex localIndex) : fLocalIndex(localIndex) {}
      ~RIterator() = default;

      iterator operator++(int) /* postfix */
      {
         auto r = *this;
         fLocalIndex++;
         return r;
      }
      iterator &operator++() /* prefix */
      {
         fLocalIndex++;
         return *this;
      }
      reference operator*() const { return fLocalIndex; }
      pointer operator->() const { return &fLocalIndex; }
      bool operator==(const iterator &rh) const { return fLocalIndex == rh.fLocalIndex; }
      bool operator!=(const iterator &rh) const { return fLocalIndex != rh.fLocalIndex; }
   };

   RNTupleLocalRange(ROOT::DescriptorId_t clusterId, ROOT::NTupleSize_t start, ROOT::NTupleSize_t end)
      : fClusterId(clusterId), fStart(start), fEnd(end)
   {
   }
   RIterator begin() const { return RIterator(RNTupleLocalIndex(fClusterId, fStart)); }
   RIterator end() const { return RIterator(RNTupleLocalIndex(fClusterId, fEnd)); }
   ROOT::NTupleSize_t size() const { return fEnd - fStart; }
};

} // namespace ROOT

#endif
