// WeightedGraphClasses.hpp
//
//     Created on: 01. 03. 2018
//         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_WEIGHTEDGRAPHCLASSES_HPP
#define ALIB2_WEIGHTEDGRAPHCLASSES_HPP

#include <common/Normalize.hpp>
#include <common/default_type/DefaultNodeType.hpp>
#include <common/default_type/DefaultWeightedEdgeType.hpp>

#include <graph/undirected/UndirectedGraph.hpp>
#include <graph/undirected/UndirectedMultiGraph.hpp>
#include <graph/directed/DirectedGraph.hpp>
#include <graph/directed/DirectedMultiGraph.hpp>
#include <graph/mixed/MixedGraph.hpp>
#include <graph/mixed/MixedMultiGraph.hpp>

namespace graph {

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedUndirectedGraph : public UndirectedGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedUndirectedMultiGraph : public UndirectedMultiGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedDirectedGraph : public DirectedGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedDirectedMultiGraph : public DirectedMultiGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedMixedGraph : public MixedGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

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

template<typename TNode = DefaultNodeType, typename TEdge = DefaultWeightedEdgeType>
class WeightedMixedMultiGraph : public MixedMultiGraph<TNode, TEdge> {
 public:
  using node_type = TNode;
  using edge_type = TEdge;
};

// ---------------------------------------------------------------------------------------------------------------------
}

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

namespace core {

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedUndirectedGraph < TNode, TEdge > > {
  static graph::WeightedUndirectedGraph<> eval(graph::WeightedUndirectedGraph<TNode, TEdge> &&value) {
    graph::WeightedUndirectedGraph<> graph;

    for (auto &&i: value.getAdjacencyList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultWeightedEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};

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

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedUndirectedMultiGraph < TNode, TEdge > > {
  static graph::WeightedUndirectedMultiGraph<> eval(graph::WeightedUndirectedMultiGraph<TNode, TEdge> &&value) {
    graph::WeightedUndirectedMultiGraph<> graph;

    for (auto &&i: value.getAdjacencyList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultWeightedEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};

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

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedDirectedGraph < TNode, TEdge > > {
  static graph::WeightedDirectedGraph<> eval(graph::WeightedDirectedGraph<TNode, TEdge> &&value) {
    graph::WeightedDirectedGraph<> graph;

    // Create successor
    for (auto &&i: value.getSuccessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create predecessors
    for (auto &&i: value.getPredecessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};
// ---------------------------------------------------------------------------------------------------------------------

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedDirectedMultiGraph < TNode, TEdge > > {
  static graph::WeightedDirectedMultiGraph<> eval(graph::WeightedDirectedMultiGraph<TNode, TEdge> &&value) {
    graph::WeightedDirectedMultiGraph<> graph;

    // Create successor
    for (auto &&i: value.getSuccessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create predecessors
    for (auto &&i: value.getPredecessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};

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

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedMixedGraph < TNode, TEdge > > {
  static graph::WeightedMixedGraph<> eval(graph::WeightedMixedGraph<TNode, TEdge> &&value) {
    graph::WeightedMixedGraph<> graph;

    // Create edges
    for (auto &&i: value.getAdjacencyList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create successor
    for (auto &&i: value.getSuccessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create predecessors
    for (auto &&i: value.getPredecessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};

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

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the graph with default template parameters or unmodified instance if the template parameters were already the default ones
 */

template<typename TNode, typename TEdge>
struct normalize < graph::WeightedMixedMultiGraph < TNode, TEdge > > {
  static graph::WeightedMixedMultiGraph<> eval(graph::WeightedMixedMultiGraph<TNode, TEdge> &&value) {
    graph::WeightedMixedMultiGraph<> graph;

    // Create edges
    for (auto &&i: value.getAdjacencyList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create successor
    for (auto &&i: value.getSuccessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    // Create predecessors
    for (auto &&i: value.getPredecessorList()) {
      DefaultNodeType first = common::Normalize::normalizeNode(std::move(i.first));
      for (auto &&j: i.second) {
        DefaultNodeType second = common::Normalize::normalizeNode(std::move(j.first));
        DefaultEdgeType edge = common::Normalize::normalizeWeightedEdge(std::move(j.second));

        graph.addNode(first);
        graph.addNode(second);
        graph.addEdge(edge);
      }
    }

    return graph;
  }
};

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

}

// =====================================================================================================================
#endif //ALIB2_WEIGHTEDGRAPHCLASSES_HPP
