// WeightedEdge.hpp
//
//     Created on: 17. 12. 2017
//         Author: Jan Uhlik
//    Modified by:
//
// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
// Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library

#ifndef ALIB2_WEIGHTEDEDGE_HPP
#define ALIB2_WEIGHTEDEDGE_HPP

#include <ostream>
#include <alib/pair>
#include <object/Object.h>
#include <alib/tuple>

#include <edge/EdgeFeatures.hpp>
#include <edge/EdgeBase.hpp>

namespace edge {

template<typename TNode, typename TWeight>
class WeightedEdge : public ext::pair<TNode, TNode>, public EdgeBase {
// ---------------------------------------------------------------------------------------------------------------------
 public:
  using node_type = TNode;
  using weight_type = TWeight;
  using normalized_type = WeightedEdge<>;

// ---------------------------------------------------------------------------------------------------------------------

 protected:
  TWeight m_weight;

// =====================================================================================================================
// Constructor, Destructor, Operators

 public:
  explicit WeightedEdge(TNode _first, TNode _second, TWeight weight);

// ---------------------------------------------------------------------------------------------------------------------

  TWeight weight() const;
  void weight(TWeight &&weight);

// =====================================================================================================================
// EdgeBase interface

 public:
  int compare(const EdgeBase &other) const override;

  virtual int compare(const WeightedEdge &other) const;

  void operator>>(std::ostream &ostream) const override;

  explicit operator std::string() const override;

// =====================================================================================================================
 public:

  virtual std::string name() const;

// ---------------------------------------------------------------------------------------------------------------------
};
// =====================================================================================================================

template<typename TNode, typename TWeight>
WeightedEdge<TNode, TWeight>::WeightedEdge(TNode _first, TNode _second, TWeight weight)
    : ext::pair<TNode, TNode>(_first, _second), m_weight(weight) {

}

// ---------------------------------------------------------------------------------------------------------------------

template<typename TNode, typename TWeight>
TWeight WeightedEdge<TNode, TWeight>::weight() const {
  return m_weight;
}

template<typename TNode, typename TWeight>
void WeightedEdge<TNode, TWeight>::weight(TWeight &&weight) {
  m_weight = std::forward<TWeight>(weight);
}

// ---------------------------------------------------------------------------------------------------------------------

template<typename TNode, typename TWeight>
std::string WeightedEdge<TNode, TWeight>::name() const {
  return "WeightedEdge";
}

// ---------------------------------------------------------------------------------------------------------------------

template<typename TNode, typename TWeight>
int WeightedEdge<TNode, TWeight>::compare(const EdgeBase &other) const {
  if (ext::type_index(typeid(*this)) == ext::type_index(typeid(other))) return this->compare((decltype(*this)) other);
  return ext::type_index(typeid(*this)) - ext::type_index(typeid(other));
}

template<typename TNode, typename TWeight>
int WeightedEdge<TNode, TWeight>::compare(const WeightedEdge &other) const {
  auto one = ext::tie(this->first, this->second, m_weight);
  auto two = ext::tie(other.first, other.second, other.m_weight);

  static ext::compare<decltype(one)> comp;

  return comp(one, two);
}

template<typename TNode, typename TWeight>
void WeightedEdge<TNode, TWeight>::operator>>(std::ostream &ostream) const {
  ostream << "(" << name() << "(first=" << this->first << ", second=" << this->second << ", weight="
          << m_weight << "))";
}

template<typename TNode, typename TWeight>
WeightedEdge<TNode, TWeight>::operator std::string() const {
  std::stringstream ss;
  ss << "(" << name() << "(first=" << this->first << ", second=" << this->second << ", weight="
     << m_weight << "))";
  return std::move(ss).str();
}

// ---------------------------------------------------------------------------------------------------------------------

} // namespace edge

// =====================================================================================================================

#endif //ALIB2_WEIGHTEDEDGE_HPP
