/**
 * @file graph_iterators.hpp
 *
 * CopyRight F. J. Madrid-Cuevas <fjmadrid@uco.es>
 *
 * Sólo se permite el uso de este código en la docencia de las asignaturas sobre
 * Estructuras de Datos de la Universidad de Córdoba.
 *
 * Está prohibido su uso para cualquier otro objetivo.
 */
#pragma once
#include <iterator>
#include <cstddef>

template <class T>
class WGraph;
template <class T>
class Vertex;
template <class T>
class Edge;
template <class T>
class const_VertexIterator;
template <class T>
class const_EdgeIterator;
template <class T>
class VertexIterator
{
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = Vertex<T>;
    using difference_type = std::ptrdiff_t;
    using pointer = Vertex<T> *;
    using reference = Vertex<T> &;
    VertexIterator() = default;
    pointer operator->() const;
    reference operator*() const;
    VertexIterator &operator++();
    VertexIterator operator++(int);
    bool operator==(const VertexIterator &other) const;
    bool operator!=(const VertexIterator &other) const;

protected:
    friend class WGraph<T>;
    friend class const_VertexIterator<T>;
    VertexIterator(const Vertex<T> &v);
    Vertex<T> v_;
};

template <class T>
class const_VertexIterator
{
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = Vertex<T>;
    using difference_type = std::ptrdiff_t;
    using pointer = const Vertex<T> *;
    using reference = const Vertex<T> &;
    const_VertexIterator() = default;
    const_VertexIterator(const VertexIterator<T> &v_it);
    pointer operator->() const;
    reference operator*() const;
    const_VertexIterator &operator++();
    const_VertexIterator operator++(int);
    bool operator==(const const_VertexIterator &other) const;
    bool operator!=(const const_VertexIterator &other) const;

protected:
    friend class WGraph<T>;

    const_VertexIterator(const Vertex<T> &v);
    Vertex<T> v_;
};

template <class T>
class EdgeIterator
{
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = Edge<T>;
    using difference_type = std::ptrdiff_t;
    using pointer = Edge<T> *;
    using reference = Edge<T> &;

    EdgeIterator() = default;
    pointer operator->() const;
    reference operator*() const;
    EdgeIterator &operator++();
    EdgeIterator operator++(int);
    bool operator==(const EdgeIterator &other) const;
    bool operator!=(const EdgeIterator &other) const;

protected:
    friend class WGraph<T>;
    friend class const_EdgeIterator<T>;
    EdgeIterator(const Edge<T> &e);
    Edge<T> e_;
};

template <class T>
class const_EdgeIterator
{
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = Edge<T>;
    using difference_type = std::ptrdiff_t;
    using pointer = const Edge<T> *;
    using reference = const Edge<T> &;
    const_EdgeIterator() = default;
    const_EdgeIterator(const EdgeIterator<T> &e_it);
    pointer operator->() const;
    reference operator*() const;
    const_EdgeIterator &operator++();
    const_EdgeIterator operator++(int);
    bool operator==(const const_EdgeIterator &other) const;
    bool operator!=(const const_EdgeIterator &other) const;

protected:
    friend class WGraph<T>;
    const_EdgeIterator(const Edge<T> &e);
    Edge<T> e_;
};

#include <graph_iterators_imp.hpp>