#pragma once

#include <array>
#include <opencv2/core/types.hpp>
#include <vector>

namespace sme::mesh {

/**
 * @brief Iterate around the edge vertices of a pixel contour.
 *
 * Given a closed loop 8-connected pixel contour generated by Suzuki
 * contour tracing, this class iterates around the edge vertices of the pixels
 * to to produce a 4-connected contour of vertices.
 *
 * @note The OpenCV function cv::findContours() implements Suzuki contour
 * tracing.
 * @note Edge vertices are shifted by (-1/2,-1/2) relative to the pixel centre,
 * e.g the (0,0) vertex corresponds to the top-left corner of the (0,0) pixel.
 */
class PixelCornerIterator {
private:
  // indices of corners of a pixel in anti-clockwise order
  std::array<cv::Point, 4> corners{cv::Point{1, 0}, cv::Point{0, 0},
                                   cv::Point{0, 1}, cv::Point{1, 1}};
  const std::vector<cv::Point> &pixels;
  std::size_t counter{0};
  std::size_t cornerIndex{0};
  std::size_t pixelIndex{0};
  cv::Point startVertex;
  /**
   * @brief Try to advance to the next pixel in the input contour
   *
   * If the current vertex is a corner of the next pixel in the input contour,
   * advance to this pixel and corner. This method can change both pixelIndex
   * and cornerIndex, but does not change the current vertex.
   */
  void advancePixelIfPossible();

public:
  /**
   * @brief Iterate around the edge vertices of a pixel contour.
   *
   * Given a closed loop 8-connected pixel contour generated by Suzuki
   * contour tracing, this class iterates around the edge vertices of the pixels
   * to to produce a 4-connected contour of vertices.
   *
   * @param[in] points 8-connected pixel contour as returned by
   * cv::findContours()
   * @param[in] outer true if the contour is an outer contour (has no parent)
   */
  PixelCornerIterator(const std::vector<cv::Point> &points, bool outer);
  /**
   * @brief The current vertex.
   */
  [[nodiscard]] cv::Point vertex() const;
  /**
   * @brief Returns true if at the end of the contour.
   */
  [[nodiscard]] bool done() const;
  /**
   * @brief Progress to the next vertex in the contour.
   */
  PixelCornerIterator &operator++();
};

} // namespace sme::mesh
