package fr.unice.polytech.al.trafficlight.central.data.graph;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Representation of a weighted directed graph.
 * e.g. :
 *          A -> B -> C <- D
 *            3    2     7
 *
 * Each edge of the graph has a direction and a weight associated.
 * The node type is T : we can use anything as a node.
 *
 * Created by tom dall'agnol on 25/12/16.
 */
public class WeightedDirectedGraph<T> {
    //for each node T, we can have many different edges that exit from it
    //e.g. : A -> B we'll have for the node A the edge that go to B
    private Map<T, List<Edge<T>>> outGraph;
    //for each node T, we can have many different edges that enters in it
    //e.g. : A -> B we'll have for the node B the edge that enter in it
    private Map<T, List<Edge<T>>> inGraph;

    public WeightedDirectedGraph(){
        this.outGraph = new HashMap<>();
        this.inGraph = new HashMap<>();
    }

    /**
     * Add a node into the graph
     * @param node
     *          the node we want to add
     */
    public void addNode(T node){
        if(!isPresent(node)){
            this.outGraph.put(node, new ArrayList<>());
            this.inGraph.put(node, new ArrayList<>());
        }
    }

    /**
     * Remove a node from the graph
     * @param node
     *              the node we want to remove from the graph
     */
    public void removeNode(T node){
        //remove all edges that have a link with this node
        this.removeAllEdgesOfNode(node);

        this.outGraph.remove(node);
        this.inGraph.remove(node);
    }

    /**
     * Check if the node is present in the graph
     * @param node
     *          the node we want to check if it's in the graph
     * @return true if the node is already in the graph, false otherwise
     */
    public boolean isPresent(T node){
        return this.outGraph.containsKey(node);
    }

    /**
     * Add an edge into the graph betwen the nodes begin
     * and end, with a specific weight.
     *
     * If the two nodes are not already in the graph,
     * they are created.
     *
     * @param begin
     *          the begin node
     * @param end
     *          the end node
     * @param weight
     *          the weight we want to put for this specific edge
     */
    public void addEdge(T begin, T end, int weight){
        addNode(begin);
        addNode(end);

        Edge<T> edge = new Edge<>(begin, end, weight);

        this.outGraph.get(begin).add(edge);
        this.inGraph.get(end).add(edge);
    }

    /**
     * Remove an edge from the graph
     *
     * @param edge
     *          the edge we want to remove
     */
    public void removeEdge(Edge<T> edge){
        T begin = edge.getBegin();
        T end = edge.getEnd();
        if(isPresent(begin) && isPresent(end)){
            this.outGraph.get(begin).remove(edge);
            this.inGraph.get(end).remove(edge);
        }
    }

    /**
     * Remove all edges that are linked to a specific node
     * @param node
     *          the node that we want to remove all the edges
     */
    public void removeAllEdgesOfNode(T node){
        this.outGraph.get(node).forEach(this::removeEdge);

        this.inGraph.get(node).forEach(this::removeEdge);
    }

    /**
     * Retrieve all the nodes that we can access
     * from the specific node
     *
     * @param node
     *          the node that we want the neighbours
     * @return a List of all the nodes that are accessible by the entry node
     */
    public List<T> getNeighbours(T node){
        return this.outGraph.get(node).stream().map(Edge<T>::getEnd).collect(Collectors.toList());
    }
}

@@ -3139,32 +3139,70 @@
  node)%7B%0A        
+List%3CEdge%3CT%3E%3E edges = new ArrayList%3C%3E(
 this.outGraph.ge
@@ -3200,32 +3200,48 @@
 tGraph.get(node)
+);%0A        edges
 .forEach(this::r
@@ -3253,32 +3253,56 @@
 Edge);%0A%0A        
+edges = new ArrayList%3C%3E(
 this.inGraph.get
@@ -3307,16 +3307,32 @@
 et(node)
+);%0A        edges
 .forEach
@@ -3755,11 +3755,948 @@
 );%0A    %7D
+%0A%0A    /**%0A     * Retrieve the number of neighbours of a specific node%0A     * @param node%0A     *          the node that we want to retrieve the number of neighbourgh%0A     * @return the number of neighbourgh%0A     */%0A    public int getNumberOfNeighbours(T node)%7B%0A        return this.outGraph.get(node).size();%0A    %7D%0A%0A    /**%0A     * Retrieve all the nodes that can access%0A     * the specific node%0A     *%0A     * @param node%0A     *              the node that we need to know how many nodes can access%0A     * @return a list containing all the nodes that can access a specific node%0A     */%0A    public List%3CT%3E getNodesThatEntersInNode(T node)%7B%0A        return this.inGraph.get(node).stream().map(Edge%3CT%3E::getEnd).collect(Collectors.toList());%0A    %7D%0A%0A    /**%0A     * Retrieve the number of node the graph is containing%0A     * @return the number of node in the graph%0A     */%0A    public int getNumberOfNode()%7B%0A        return this.inGraph.size();%0A    %7D
 %0A%7D%0A

Test the weighted oriented graph with unit test