#
# Topology builder helpers for rumba
#
#    Copyright (C) 2017-2018 Nextworks S.r.l.
#    Copyright (C) 2017-2018 imec
#
#    Sander Vrijders   <sander.vrijders@ugent.be>
#    Dimitri Staessens <dimitri.staessens@ugent.be>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., http://www.fsf.org/about/contact/.
#

"""Topology builder functions for common network shapes."""

from rumba.model import Node, UnicastLayer, EthDixLayer


def _node_name(names, i, prefix):
    """Return the node name: from `names` list if given, else prefix + i."""
    if names is not None:
        return names[i]
    return prefix + str(i)


def build_chain(num_nodes, layer, prefix="node", shim_prefix="e",
                names=None):
    """Build a linear chain of nodes connected by EthDixLayers.

    Args:
        num_nodes:    Number of nodes in the chain.
        layer:        The UnicastLayer all nodes belong to.
        prefix:       Name prefix for nodes (default: "node").
        shim_prefix:  Name prefix for EthDixLayers (default: "e").
        names:        Optional list of node names (overrides prefix).

    Returns:
        A list of Node objects forming a chain.
    """
    nodes = []
    shim_prev = None

    for i in range(num_nodes):
        shim = EthDixLayer(shim_prefix + str(i)) if i < num_nodes - 1 else None

        layers = [layer]
        regs = []

        if shim_prev is not None:
            layers.append(shim_prev)
            regs.append(shim_prev)
        if shim is not None:
            layers.append(shim)
            regs.append(shim)

        node = Node(_node_name(names, i, prefix), layers=layers,
                    registrations={layer: regs})
        nodes.append(node)
        shim_prev = shim

    return nodes


def build_ring(num_nodes, layer, prefix="node", shim_prefix="e",
               names=None):
    """Build a ring of nodes connected by EthDixLayers.

    Like build_chain, but the last shim links back to the first node.

    Args:
        num_nodes:    Number of nodes in the ring (must be >= 3).
        layer:        The UnicastLayer all nodes belong to.
        prefix:       Name prefix for nodes (default: "node").
        shim_prefix:  Name prefix for EthDixLayers (default: "e").
        names:        Optional list of node names (overrides prefix).

    Returns:
        A list of Node objects forming a ring.
    """
    nodes = []
    shim_prev = None

    for i in range(num_nodes):
        shim = EthDixLayer(shim_prefix + str(i))

        layers = [layer]
        regs = []

        if shim_prev is not None:
            layers.append(shim_prev)
            regs.append(shim_prev)
        layers.append(shim)
        regs.append(shim)

        node = Node(_node_name(names, i, prefix), layers=layers,
                    registrations={layer: regs})
        nodes.append(node)
        shim_prev = shim

    # Close the ring: connect last shim back to first node
    nodes[0].add_layer(shim_prev)
    nodes[0].add_registration(layer, shim_prev)

    return nodes


def build_star(leaf_names, layer, hub_name="router", shim_prefix="e"):
    """Build a star topology: leaf nodes connected to a central hub.

    Each leaf is connected to the hub via its own EthDixLayer.

    Args:
        leaf_names:   List of names for the leaf nodes.
        layer:        The UnicastLayer all nodes belong to.
        hub_name:     Name of the central hub node (default: "router").
        shim_prefix:  Name prefix for EthDixLayers (default: "e").

    Returns:
        A tuple (leaves, hub) where leaves is a list of leaf Nodes
        and hub is the central Node.
    """
    leaves = []
    shims = []

    for i, name in enumerate(leaf_names):
        shim = EthDixLayer(shim_prefix + str(i + 1))
        shims.append(shim)
        leaf = Node(name, layers=[shim, layer],
                    registrations={layer: [shim]})
        leaves.append(leaf)

    hub = Node(hub_name, layers=shims + [layer],
               registrations={layer: shims})

    return leaves, hub
