package net.onrc.onos.api.flowmanager;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;

/**
 * A directed connected tree topology representation used by TreeFlow.
 */
public class Tree extends FlowLinks {
    Map<Dpid, Set<PortNumber>> ports = new HashMap<Dpid, Set<PortNumber>>();
    Map<SwitchPort, FlowLink> inLinks = new HashMap<SwitchPort, FlowLink>();
    Map<SwitchPort, FlowLink> outLinks = new HashMap<SwitchPort, FlowLink>();

    /**
     * Creates new instance.
     */
    public Tree() {
        super();
    }

    /**
     * Creates new instance from the specified Path object.
     *
     * @param path the Path object
     * @throws IllegalArgumentException if the path object does not form a
     *         single connected directed path topology
     */
    public Tree(Path path) {
        super();
        checkNotNull(path);

        for (FlowLink link : path) {
            if (!addLink(link)) {
                throw new IllegalArgumentException();
            }
        }
    }

    /**
     * Creates new instance using FlowLinks object.
     *
     * @param links the FlowLinks object
     * @throws IllegalArgumentException if the links object does not form a
     *         single connected directed tree topology
     */
    public Tree(FlowLinks links) {
        super();
        checkNotNull(links);

        for (FlowLink link : links) {
            if (!addLink(link)) {
                throw new IllegalArgumentException();
            }
        }
    }

    private void addPort(SwitchPort port) {
        checkNotNull(port);
        if (!ports.containsKey(port.getDpid())) {
            ports.put(port.getDpid(), new HashSet<PortNumber>());
        }
        ports.get(port.getDpid()).add(port.getPortNumber());
    }

    /**
     * Adds FlowLink object to this tree.
     * <p>
     * This method checks specified FlowLink object to keep this Tree object a
     * connected tree.
     *
     * @param link FlowLink object to be added.
     * @return true if succeeded, false otherwise.
     */
    public boolean addLink(FlowLink link) {
        checkNotNull(link);
        if (links.size() > 0) {
            if (!hasDpid(link.getSrcDpid()) && !hasDpid(link.getDstDpid())) {
                // no attaching point
                return false;
            }
            if (hasDpid(link.getSrcDpid()) && hasDpid(link.getDstDpid())) {
                // loop or duplicated paths
                return false;
            }
        }

        if (hasSwitchPort(link.getSrcSwitchPort())
                || hasSwitchPort(link.getDstSwitchPort())) {
            // some port has already been occupied by another link
            return false;
        }

        links.add(link);
        addPort(link.getSrcSwitchPort());
        addPort(link.getDstSwitchPort());

        inLinks.put(link.getDstSwitchPort(), link);
        outLinks.put(link.getSrcSwitchPort(), link);

        return true;
    }

    /**
     * Checks if specified dpid exists in this tree.
     *
     * @param dpid DPID to be checked.
     * @return true if found, false otherwise.
     */
    public boolean hasDpid(Dpid dpid) {
        return ports.containsKey(dpid);
    }

    /**
     * Checks if specified switch-port exists in this tree.
     *
     * @param port SwitchPort object to be checked.
     * @return true if found, false otherwise.
     */
    public boolean hasSwitchPort(SwitchPort port) {
        return inLinks.containsKey(port) || outLinks.containsKey(port);
    }

    @Override
    public int hashCode() {
        // TODO think if this is correct.
        return super.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        // TODO think if this is correct.
        // - has to check equality using a set, not a list?
        return super.equals(o);
    }
}

@@ -517,163 +517,8 @@
 %3E();
-%0A    Map%3CSwitchPort, FlowLink%3E inLinks = new HashMap%3CSwitchPort, FlowLink%3E();%0A    Map%3CSwitchPort, FlowLink%3E outLinks = new HashMap%3CSwitchPort, FlowLink%3E();
 %0A%0A  
@@ -2881,114 +2881,8 @@
 );%0A%0A
-        inLinks.put(link.getDstSwitchPort(), link);%0A        outLinks.put(link.getSrcSwitchPort(), link);%0A%0A
     
@@ -3002,33 +3002,32 @@
 ID to be checked
-.
 %0A     * @return 
@@ -3048,33 +3048,32 @@
  false otherwise
-.
 %0A     */%0A    pub
@@ -3144,32 +3144,385 @@
 y(dpid);%0A    %7D%0A%0A
+    /**%0A     * Gets a set of %7B@link PortNumber%7Ds associated with specified DPID.%0A     *%0A     * @param dpid the DPID%0A     * @return a set of %7B@link PortNumber%7Ds associated with the DPID, or null if%0A     *         the dpid does not exist in this tree%0A     */%0A    public Set%3CPortNumber%3E getPortNumbersOf(Dpid dpid) %7B%0A        return ports.get(dpid);%0A    %7D%0A%0A
     /**%0A     * C
@@ -3630,17 +3630,16 @@
  checked
-.
 %0A     * 
@@ -3668,33 +3668,32 @@
  false otherwise
-.
 %0A     */%0A    pub
@@ -3749,51 +3749,120 @@
     
-return inLinks.containsKey(port) %7C%7C outLink
+Set%3CPortNumber%3E portNumbers = getPortNumbersOf(port.getDpid());%0A        return portNumbers != null && portNumber
 s.co
@@ -3867,24 +3867,37 @@
 contains
-Key
 (port
+.getPortNumber()
 );%0A    %7D

Add getPortNumbersOf(dpid) method to Tree class.