/**
 * @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 <graph_iterators.hpp>
#include <graph.hpp>

//
// VertexIterator implementation
//

template <class T>
VertexIterator<T>::VertexIterator(const Vertex<T> &v)
    : v_(v)
{
}

template <class T>
typename VertexIterator<T>::pointer VertexIterator<T>::operator->() const
{
    return const_cast<typename VertexIterator<T>::pointer>(&v_);
}

template <class T>
typename VertexIterator<T>::reference VertexIterator<T>::operator*() const
{
    return const_cast<typename VertexIterator<T>::reference>(v_);
}

template <class T>
VertexIterator<T> &VertexIterator<T>::operator++()
{
    ++v_.label_;
    return *this;
}

template <class T>
VertexIterator<T> VertexIterator<T>::operator++(int)
{
    VertexIterator<T> ret_v = *this;
    ++v_.label_;
    return ret_v;
}
template <class T>
bool VertexIterator<T>::operator==(const VertexIterator &other) const
{
    return v_.graph_ == other.v_.graph_ && v_.label_ == other.v_.label_;
}

template <class T>
bool VertexIterator<T>::operator!=(const VertexIterator<T> &other) const
{
    return !(*this == other);
}

//
// const_VertexIterator implementation
//

template <class T>
const_VertexIterator<T>::const_VertexIterator(const Vertex<T> &v)
    : v_(v)
{
}

template <class T>
const_VertexIterator<T>::const_VertexIterator(const VertexIterator<T> &v_it)
    : v_(v_it.v_)
{
}

template <class T>
typename const_VertexIterator<T>::reference const_VertexIterator<T>::operator*() const
{
    return v_;
}

template <class T>
typename const_VertexIterator<T>::pointer const_VertexIterator<T>::operator->() const
{
    return const_cast<typename const_VertexIterator<T>::pointer>(&v_);
}

template <class T>
const_VertexIterator<T> &const_VertexIterator<T>::operator++()
{
    ++v_.label_;
    return *this;
}

template <class T>
const_VertexIterator<T> const_VertexIterator<T>::operator++(int)
{
    const_VertexIterator<T> ret_v = *this;
    ++v_.label_;
    return ret_v;
}

template <class T>
bool const_VertexIterator<T>::operator==(const const_VertexIterator &other) const
{
    return v_.graph_ == other.v_.graph_ && v_.label_ == other.v_.label_;
}

template <class T>
bool const_VertexIterator<T>::operator!=(const const_VertexIterator &other) const
{
    return !(*this == other);
}

//
// EdgeIterator implementation
//

template <class T>
EdgeIterator<T>::EdgeIterator(const Edge<T> &e)
    : e_(e)
{
}

template <class T>
typename EdgeIterator<T>::pointer EdgeIterator<T>::operator->() const
{
    return const_cast<typename EdgeIterator<T>::pointer>(&e_);
}

template <class T>
typename EdgeIterator<T>::reference EdgeIterator<T>::operator*() const
{
    return const_cast<typename EdgeIterator<T>::reference>(e_);
}

template <class T>
EdgeIterator<T> &EdgeIterator<T>::operator++()
{
    ++e_.v_;
    while (e_.v_ < e_.graph_->order() &&
           e_.graph_->weight(e_.u_, e_.v_) == std::numeric_limits<float>::infinity())
        ++e_.v_;
    return *this;
}

template <class T>
EdgeIterator<T> EdgeIterator<T>::operator++(int)
{
    auto ret_v = *this;
    ++e_.v_;
    while (e_.v_ < e_.graph_->order() &&
           e_.graph_->weight(e_.u_, e_.v_) == std::numeric_limits<float>::infinity())
        ++e_.v_;
    return ret_v;
}

template <class T>
bool EdgeIterator<T>::operator==(const EdgeIterator &other) const
{
    return e_.graph_ == other.e_.graph_ &&
           e_.u_ == other.e_.u_ && e_.v_ == other.e_.v_;
}

template <class T>
bool EdgeIterator<T>::operator!=(const EdgeIterator &other) const
{
    return !(*this == other);
}

//
// const_EdgeIterator implementation
//

template <class T>
const_EdgeIterator<T>::const_EdgeIterator(const Edge<T> &e)
    : e_(e)
{
}

template <class T>
const_EdgeIterator<T>::const_EdgeIterator(const EdgeIterator<T> &e_it)
    : e_(e_it.e_)
{
}

template <class T>
typename const_EdgeIterator<T>::pointer const_EdgeIterator<T>::operator->() const
{
    return &e_;
}

template <class T>
typename const_EdgeIterator<T>::reference const_EdgeIterator<T>::operator*() const
{
    return e_;
}

template <class T>
const_EdgeIterator<T> &const_EdgeIterator<T>::operator++()
{
    ++e_.v_;
    while (e_.v_ < e_.graph_->order() &&
           e_.graph_->weight(e_.u_, e_.v_) == std::numeric_limits<float>::infinity())
        ++e_.v_;
    return *this;
}

template <class T>
const_EdgeIterator<T> const_EdgeIterator<T>::operator++(int)
{
    auto ret_v = *this;
    ++e_.v_;
    while (e_.v_ < e_.graph_->order() &&
           e_.graph_->weight(e_.u_, e_.v_) == std::numeric_limits<float>::infinity())
        ++e_.v_;
    return ret_v;
}

template <class T>
bool const_EdgeIterator<T>::operator==(const const_EdgeIterator &other) const
{
    return e_.graph_ == other.e_.graph_ &&
           e_.u_ == other.e_.u_ && e_.v_ == other.e_.v_;
}

template <class T>
bool const_EdgeIterator<T>::operator!=(const const_EdgeIterator &other) const
{
    return !(*this == other);
}
