//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
//
#ifndef EDGE_H
#define EDGE_H

/**
 * \file Edge.hpp
 *
 * Define the Edge class to be used with the VVGraph class.
 */

#include <stdint.h>
#include <QtCore/QHash>

#include <Config.hpp>
#include <Common.hpp>


namespace mgx 
{
  /**
   * Type of the identifier of an edge
   */
  typedef intptr_t edge_identity_t;
  
  /**
   * \class Edge Edge.hpp <Edge.hpp>
   * Edge of a vv graph.
   *
   * The edges represent weak references on the edges data. The data are owned by
   * the graph. You must never try to access an edge that was deleted from its
   * graph.
   */
  template <typename EdgeContent> class Edge {
  public:
    /**
     * Type of the identity of a vertex
     */
    typedef edge_identity_t identity_t;
  
    /**
     * Type of the content of the edge
     */
    typedef EdgeContent content_t;
  
    /**
     * Type of the equivalent pointer
     */
    typedef EdgeContent* pointer;
  
    /**
     * Creates a null edge.
     */
    Edge();
  
    /**
     * Creates an edge from \c src to \c tgt with a given content.
     *
     * The object do not take ownership of the content which must then be kept
     * alive for as long as needed.
     *
     * \note This function is meant to be used by the graph, not really by the
     * user of the VV library.
     */
    Edge(identity_t src, identity_t tgt, EdgeContent* content);
  
    /**
     * Get a new weak reference on the copy.
     */
    Edge(const Edge& copy);
  
    /**
     * Data access.
     *
     * \warning Do not try to access the data of the null edge or of an edge that
     * does not exist anymore in its graph.
     */
    EdgeContent* operator->() const {
      return _content;
    }
  
    /**
     * Data access.
     *
     * \warning Do not try to access the data of the null edge or of an edge that
     * does not exist anymore in its graph.
     */
    EdgeContent& operator*() const {
      return *_content;
    }
  
    /**
     * Access to the data via pointer to member
     */
    template <typename R> R& operator->*(R EdgeContent::*ptr) {
      return _content->*ptr;
    }
    /**
     * Constant access to the data via pointer to member
     */
    template <typename R> const R& operator->*(R EdgeContent::*ptr) const {
      return _content->*ptr;
    }
  
    /**
     * Change the reference help by the object.
     */
    Edge& operator=(const Edge& other);
  
    /**
     * Comparison operators.
     *
     * \note The comparison is done on the identity of the edge, not the content.
     */
    bool operator==(const Edge& other) const {
      return _content == other._content;
    }
    /**
     * Comparison operators.
     *
     * \note The comparison is done on the identity of the edge, not the content.
     */
    bool operator!=(const Edge& other) const {
      return _content != other._content;
    }
    /**
     * Comparison operators.
     *
     * \note The comparison is done on the identity of the edge, not the content.
     */
    bool operator>(const Edge& other) const {
      return _content > other._content;
    }
    /**
     * Comparison operators.
     *
     * \note The comparison is done on the identity of the edge, not the content.
     */
    bool operator<(const Edge& other) const {
      return _content < other._content;
    }
  
    /**
     * Test if an edge is null.
     */
    bool isNull() const {
      return _content == 0;
    }
  
    /**
     * Convert an edge to \c true if it is not null.
     */
    operator bool() const { return _content != 0; }
  
    /**
     * Returns the identifier of the source of the edge.
     *
     * \note You should rather use the VVGraph::source() method that returns the
     * source vertex.
     */
    identity_t source() const {
      return _source;
    }
    /**
     * Returns the identifier of the target of the edge.
     *
     * \note You should rather use the VVGraph::target() method that returns the
     * target vertex.
     */
    identity_t target() const {
      return _target;
    }
  
    /**
     * Reset an edge weak pointer to null
     */
    void clear()
    {
      _source = 0;
      _target = 0;
      _content = 0;
    }
  
    static Edge null;
  
  protected:
    /**
     * Identity of the source of the edge
     */
    identity_t _source;
    /**
     * Identity of the target of the edge
     */
    identity_t _target;
    /**
     * Content of the edge
     */
    EdgeContent* _content;
  };
  
  template <typename EdgeContent> Edge<EdgeContent> Edge<EdgeContent>::null;
  
  template <typename EdgeContent>
  Edge<EdgeContent>::Edge()
    : _source(0)
    , _target(0)
    , _content(0)
  {
  }
  
  template <typename EdgeContent>
  Edge<EdgeContent>::Edge(const Edge& copy)
    : _source(copy._source)
    , _target(copy._target)
    , _content(copy._content)
  {
  }
  
  template <typename EdgeContent>
  Edge<EdgeContent>::Edge(identity_t src, identity_t tgt, EdgeContent* content)
    : _source(src)
    , _target(tgt)
    , _content(content)
  {
  }
  
  template <typename EdgeContent> Edge<EdgeContent>& Edge<EdgeContent>::operator=(const Edge<EdgeContent>& other)
  {
    _source = other._source;
    _target = other._target;
    _content = other._content;
    return *this;
  }
  
  /**
   * Default copy of the arc uses the = operator.
   * Overload this function for your own type to change this behavior.
   */
  template <typename T> void copy_symmetric(T& e2, const T& e1) {
    e2 = e1;
  }
  
  /**
   * Type of a undirected edge (or arc)
   *
   * When an arc is destroyed, the content of the main edge is copied in the
   * other one.
   *
   * Note that you should never keep an arc! Is is meant for temporary usage.
   * This is why there is no copy constructor or operator=.
   */
  template <typename EdgeContent> struct Arc {
    /**
     * Type of the identity of a vertex
     */
    typedef edge_identity_t identity_t;
  
    typedef EdgeContent content_t;
  
    typedef Edge<EdgeContent> edge_t;
  
    /**
     * Cosntruct an empty (null) arc
     */
    Arc();
  
    /**
     * Copy constructor
     */
    Arc(const Arc& copy);
  
    /**
     * Full constructor -> should never be called by the user
     */
    Arc(identity_t src, identity_t tgt, EdgeContent* c1, EdgeContent* c2);
  
    /**
     * Convert the current arc into the corresponding edge
     */
    operator edge_t() const { return edge_t(_source, _target, main); }
  
    /**
     * Synchronize the edges and returns the opposite arc.
     */
    Arc inv() const;
  
    /**
     * Unary '-' operator synchronize edges and returns opposite arc.
     */
    Arc operator-() const {
      return inv();
    }
  
    /**
     * Destroy and copy the content of the arcs
     */
    ~Arc() {
      sync();
    }
  
    /**
     * Test if the content is null
     */
    bool isNull() const {
      return main == 0;
    }
  
    /**
     * AN arc evaluates to true if it contains some data
     */
    operator bool() const { return main != 0; }
  
    /**
     * Reference the content of the arc
     */
    EdgeContent& operator*() const {
      return *main;
    }
    /**
     * Reference the content of the arc as a pointer
     */
    EdgeContent* operator->() const {
      return main;
    }
  
    /**
     * Returns the identifier of the source of the edge.
     *
     * \note You should rather use the VVGraph::source() method that returns the
     * source vertex.
     */
    identity_t source() const {
      return _source;
    }
    /**
     * Returns the identifier of the target of the edge.
     *
     * \note You should rather use the VVGraph::target() method that returns the
     * target vertex.
     */
    identity_t target() const {
      return _target;
    }
  
    /**
     * Synchronize both sides of the arc
     */
    void sync() const
    {
      if(main)
        copy_symmetric(*other, *main);
    }
  
  protected:
    identity_t _source, _target;
    mutable EdgeContent* main, *other;
  };
  
  template <typename EdgeContent>
  Arc<EdgeContent>::Arc()
    : _source(0)
    , _target(0)
    , main(0)
    , other(0)
  {
  }
  
  template <typename EdgeContent>
  Arc<EdgeContent>::Arc(identity_t src, identity_t tgt, EdgeContent* e1, EdgeContent* e2)
    : _source(src)
    , _target(tgt)
    , main(e1)
    , other(e2)
  {
  }
  
  template <typename EdgeContent>
  Arc<EdgeContent>::Arc(const Arc& copy)
    : _source(copy._source)
    , _target(copy._target)
    , main(copy.main)
    , other(copy.other)
  {
  }
  
  template <typename EdgeContent> Arc<EdgeContent> Arc<EdgeContent>::inv() const
  {
    sync();
    return Arc(_target, _source, other, main);
  }
}

namespace std 
{
  HASH_NS_ENTER
  template <typename EdgeContent> struct hash<mgx::Edge<EdgeContent> > 	{
    size_t operator()(const mgx::Edge<EdgeContent>& e) const {
      return e.source() + e.target();
    }
  };
  HASH_NS_EXIT
}

template <typename EdgeContent> uint qHash(const mgx::Edge<EdgeContent>& e)
{
  return qHash(uint(e.source() >> 4)) ^ qHash(uint(e.target() >> 4));
}

#endif
