/**
 * @file edge.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 <memory>
#include <vertex.hpp>

template <class T>
class Vertex;

template <class T>
class WGraph;

template <class T>
class EdgeIterator;

template <class T>
class const_EdgeIterator;

/**
 * @brief Class to model the Edge of a WGraph ADT.
 * An edge refers both a directed link u->v or an undirected link (u,v)
 *

 * @tparam E is the edge's data type item.
 *
 * @ingroup WGraph
 */
template <class T>
class Edge
{
public:
    /** @name Lifecycle Makers*/
    /** @{*/
    /**
     * @brief Makes an invalid edge.
     * @post !is_valid()
     */
    Edge() = default;
    /** @}*/

    /** @name Observers*/
    /** @{*/

    /**
     * @brief Is this edge valid?
     *
     * @return true if this edge belongs to a graph.
     */
    bool is_valid() const;

    /**
     *  @brief Get edge's data item.
     * @return the edge's data item.
     * @pre is_valid()
     */
    float item() const;

    /**
     *  @brief Is vertex u an end of this edge.
     * @param[in] u_label is the vertex's label to test.
     * @return true is @a u is one of the edge's ends.
     * @pre is_valid()
     */
    bool has(size_t u_label) const;

    /**
     *  @brief Is vertex u an end of this edge.
     * @param[in] u is the vertex to test.
     * @return true is @a u is one of the edge's ends.
     * @pre is_valid()
     */
    bool has(const Vertex<T> &u) const;

    /**
     * @brief The other vertex end that is not u.
     * @param[in] u_label is one of the vertex ends.
     * @return the other vertex end that is not @a u.
     * @pre is_valid()
     * @pre has(u)
     * @post has(retVal) and other(retVal)==u
     */
    size_t other(size_t u_label) const;

    /**
     * @brief The other vertex end that is not u.
     * @param[in] u is one of the vertex ends.
     * @return the other vertex end that is not @a u.
     * @pre is_valid()
     * @pre has(u)
     * @post has(retVal) and other(retVal)==u
     */
    const_VertexIterator<T> other(const const_VertexIterator<T> &u) const;

    /**
     * @brief The other vertex end that is not u.
     * @param[in] u is one of the vertex ends.
     * @return the other vertex end that is not @a u.
     * @pre is_valid()
     * @pre has(u)
     * @post has(retVal) and other(retVal)==u
     */
    VertexIterator<T> other(const VertexIterator<T> &u);

    /**
     * @brief the first vertex of the directed edge (u->v)
     * @return @a u.
     * @pre is_valid()
     */
    const_VertexIterator<T> first() const;

    /**
     * @brief the first vertex of the directed edge (u->v)
     * @return @a u.
     * @pre is_valid()
     */
    VertexIterator<T> first();

    /**
     * @brief the second vertex of the directed edge (u->v).
     * @return @a v.
     * @pre is_valid()
     */
    const_VertexIterator<T> second() const;

    /**
     * @brief the second vertex of the directed edge (u->v).
     * @return @a v.
     * @pre is_valid()
     */
    VertexIterator<T> second();

    /**
     * @brief Check if this edge is the same that other edge.
     *
     * @param other edge to compare with.
     * @return true if this edge and other are the same edge of the same graph.
     */
    bool operator==(const Edge<T> &other) const;

    /**@}*/

    /** @name Modifiers*/
    /** @{*/

    /**
     * @brief Set data saved in the edge.
     * @param[in] v is the data to save in.
     * @pre is_valid()
     * @post item()==v
     */
    void set_item(float v);

    /** @} */

protected:
    friend class WGraph<T>;
    friend class Vertex<T>;
    friend class EdgeIterator<T>;
    friend class const_EdgeIterator<T>;

    /**
     * @brief Makes an edge (u->v).
     * @param[in] u,
     * @param[in] v are the edge's end (first, second if directed)
     * @param[in] graph is the edge's graph.
     * @post !is_valid() || has(u)
     * @post !is_Valid() || has(v)
     * @post !is_valid() || first()==graph->vertex(u)
     * @post !is_valid() || second()==graph->vertex(v)
     * @post !is_valid() || other(graph->vertex(u))==graph->vertex(v)
     * @post !is_valid() || other(graph->vertex(v))==graph->vertex(u)
     * @post !is_valid() || item()==graph->weight(u,v)
     * @post !is_valid() || (graph->is_directed() || item() == graph_->weight(v, u))
     */
    Edge(size_t u, size_t v, WGraph<T> *graph);

    size_t u_;         /**< the first end.*/
    size_t v_;         /**< the second end.*/
    WGraph<T> *graph_; /**< the graph */
};

#include <edge_imp.hpp>
