#ifndef AMPL_SLICE_H
#define AMPL_SLICE_H

#include <vector>

#include "ampl/ep/dataframe_ep.h"
#include "ampl/variant.h"

namespace ampl {
class DataFrame;
namespace internal {
class DataFrame;

/**
 * Represents a slice (row or column) of a DataFrame.
 */
template <bool ROW>
class Slice {
  friend class ampl::DataFrame;
  DataFrame *data_;
  std::size_t fixedIndex_;

  Slice(internal::DataFrame *data, std::size_t index)
      : data_(data), fixedIndex_(index) {}
  // A proxy used for implementing operator->.
  class Proxy {
   private:
    VariantRef inner_;

   public:
    // Constructor
    explicit Proxy(VariantRef e) : inner_(e) {}
    // Arrow operator
    const VariantRef *operator->() const { return &inner_; }
  };

 public:
  class iterator {
   public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = VariantRef;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type *;
    using reference = value_type &;

   private:
    friend class Slice;
    const Slice *parent_;
    std::size_t index_;
    iterator(const Slice *parent, std::size_t index)
        : parent_(parent), index_(index) {}

   public:
    value_type operator*() const { return (*parent_)[index_]; }
    iterator &operator++() {
      ++index_;
      return *this;
    }
    iterator operator++(int) {
      iterator clone(*this);
      ++index_;
      return clone;
    }
    bool operator==(const iterator &other) const {
      return (parent_->data_ == other.parent_->data_) &&
             (parent_->fixedIndex_ == other.parent_->fixedIndex_) &&
             (index_ == other.index_);
    }
    bool operator!=(const iterator &other) const { return !(*this == other); }
    /*
     * Arrow operator
     */
    Proxy operator->() const { return Proxy((*parent_)[index_]); }
  };

  std::size_t size() const {
    if (ROW)
      return internal::AMPL_DataFrame_getNumCols(data_);
    else
      return internal::AMPL_DataFrame_getNumRows(data_);
  }

  iterator begin() const { return iterator(this, 0); }

  iterator end() const { return iterator(this, size()); }

  VariantRef operator[](std::size_t index) const {
    if (ROW)
      return VariantRef(
          internal::AMPL_DataFrame_Element(data_, fixedIndex_, index));
    else
      return VariantRef(
          internal::AMPL_DataFrame_Element(data_, index, fixedIndex_));
  }
};
}  // namespace internal
}  // namespace ampl

#endif  // AMPL_SLICE_H
