package net.onrc.onos.core.intent;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;

import net.onrc.onos.core.topology.Link;
import net.onrc.onos.core.topology.LinkEvent;
import net.onrc.onos.core.topology.Switch;
import net.onrc.onos.core.util.Dpid;

/**
 * This class creates bandwidth constrained breadth first tree and returns paths
 * from root switch to leaf switches which satisfies the bandwidth condition. If
 * bandwidth parameter is not specified, the normal breadth first tree will be
 * calculated. The paths are snapshot paths at the point of the class
 * instantiation.
 */
public class ConstrainedBFSTree {
    LinkedList<Switch> switchQueue = new LinkedList<>();
    HashSet<Switch> switchSearched = new HashSet<>();
    HashMap<Dpid, LinkEvent> upstreamLinks = new HashMap<>();
    HashMap<Switch, Path> paths = new HashMap<>();
    Switch rootSwitch;
    PathIntentMap intents = null;
    double bandwidth = 0.0; // 0.0 means no limit for bandwidth (normal BFS tree)

    /**
     * Constructor.
     *
     * @param rootSwitch root of the BFS tree
     */
    public ConstrainedBFSTree(Switch rootSwitch) {
        this.rootSwitch = rootSwitch;
        calcTree();
    }

    /**
     * Constructor.
     *
     * @param rootSwitch root switch of the BFS tree
     * @param intents map of Intents
     * @param bandwidth bandwidth constraint
     */
    public ConstrainedBFSTree(Switch rootSwitch, PathIntentMap intents, double bandwidth) {
        this.rootSwitch = rootSwitch;
        this.intents = intents;
        this.bandwidth = bandwidth;
        calcTree();
    }

    /**
     * Calculates the BFS tree using any provided constraints and Intents.
     */
    protected void calcTree() {
        switchQueue.add(rootSwitch);
        switchSearched.add(rootSwitch);
        while (!switchQueue.isEmpty()) {
            Switch sw = switchQueue.poll();
            for (Link link : sw.getOutgoingLinks()) {
                Switch reachedSwitch = link.getDstPort().getSwitch();
                if (switchSearched.contains(reachedSwitch)) {
                    continue;
                }
                if (intents != null &&
                        intents.getAvailableBandwidth(link) < bandwidth) {
                    continue;
                }
                switchQueue.add(reachedSwitch);
                switchSearched.add(reachedSwitch);
                upstreamLinks.put(reachedSwitch.getDpid(), new LinkEvent(link));
            }
        }
    }

    /**
     * Return the computed path from the root switch to the leaf switch.
     *
     * @param leafSwitch the leaf switch
     * @return the Path from the root switch to the leaf switch
     */
    public Path getPath(Switch leafSwitch) {
        Path path = paths.get(leafSwitch);
        Dpid rootSwitchDpid = rootSwitch.getDpid();
        if (path == null && switchSearched.contains(leafSwitch)) {
            path = new Path();
            Dpid sw = leafSwitch.getDpid();
            while (!sw.equals(rootSwitchDpid)) {
                LinkEvent upstreamLink = upstreamLinks.get(sw);
                path.add(0, upstreamLink);
                sw = upstreamLink.getSrc().getDpid();
            }
            paths.put(leafSwitch, path);
        }
        return path;
    }
}

@@ -719,22 +719,20 @@
 HashSet%3C
-Switch
+Dpid
 %3E switch
@@ -833,22 +833,20 @@
 HashMap%3C
-Switch
+Dpid
 , Path%3E 
@@ -1807,32 +1807,42 @@
 d.add(rootSwitch
+.getDpid()
 );%0A        while
@@ -2090,24 +2090,34 @@
 eachedSwitch
+.getDpid()
 )) %7B%0A       
@@ -2407,32 +2407,42 @@
 dd(reachedSwitch
+.getDpid()
 );%0A             
@@ -2822,24 +2822,34 @@
 t(leafSwitch
+.getDpid()
 );%0A        D
@@ -2953,16 +2953,26 @@
 afSwitch
+.getDpid()
 )) %7B%0A   
@@ -3299,16 +3299,26 @@
 afSwitch
+.getDpid()
 , path);

Switch instance should not be used as a Key element.