#ifndef SPATIALGRID_HPP_
#define SPATIALGRID_HPP_

#include <array>
#include <cmath>
#include <cstdint>
#include <vector>


template<typename value_t>
class SpatialGrid {
public:
  SpatialGrid() = delete;
  explicit SpatialGrid(double x_start, double y_start, double x_end, double y_end) :
    offset_({static_cast<size_t>(x_start / spacing), static_cast<size_t>(y_start / spacing)}),
    size_x_(static_cast<size_t>(std::ceil(x_end / spacing)) - offset_[0]),
    size_y_(static_cast<size_t>(std::ceil(y_end / spacing)) - offset_[1]),
    grid_(size_x_, std::vector<value_t>(size_y_))
  {}
  ~SpatialGrid() = default;

  value_t& at(double x, double y)
  {
    size_t const id_x{static_cast<size_t>(x / spacing) - offset_[0]};
    size_t const id_y{static_cast<size_t>(y / spacing) - offset_[1]};
    return grid_[id_x][id_y];
  }
  value_t& at(std::array<double, 2> pos)
  {
    return at(pos[0], pos[1]);
  }

  std::vector<value_t>& operator[](size_t idx)
  {
    return grid_[idx];
  }
  size_t size_x() const
  {
    return grid_.size();
  }
  size_t size_y() const
  {
    return grid_[0].size();
  }
  size_t start_x() const noexcept
  {
    return offset_[0] * spacing;
  }
  size_t start_y() const noexcept
  {
    return offset_[1] * spacing;
  }

  std::vector<std::vector<value_t>>& data() {
    return grid_;
  }

  static constexpr size_t spacing{5};

private:
  size_t const offset_[2];
  size_t const size_x_;
  size_t const size_y_;

  std::vector<std::vector<value_t>> grid_;
};


#endif /* SPATIALGRID_HPP_ */
