"""
Basic capabilities and capacities tests

"""

import logging

import unittest

import oftest.controller as controller
import oftest.cstruct as ofp
import oftest.message as message
import oftest.dataplane as dataplane
import oftest.action as action
import oftest.parse as parse
import basic

from testutils import *

#@var caps_port_map Local copy of the configuration map from OF port
# numbers to OS interfaces
caps_port_map = None
#@var caps_logger Local logger object
caps_logger = None
#@var caps_config Local copy of global configuration data
caps_config = None

# For test priority
test_prio = {}

def test_set_init(config):
    """
    Set up function for caps test classes

    @param config The configuration dictionary; see oft
    """

    basic.test_set_init(config)

    global caps_port_map
    global caps_logger
    global caps_config

    caps_logger = logging.getLogger("caps")
    caps_logger.info("Initializing caps test set")
    caps_port_map = config["port_map"]
    caps_config = config


def flow_caps_common(obj, is_exact=True):
    """
    The common function for 

    @param obj The calling object
    @param is_exact If True, checking exact match; else wildcard
    """

    global caps_port_map
    of_ports = caps_port_map.keys()
    of_ports.sort()

    rv = delete_all_flows(obj.controller, caps_logger)
    obj.assertEqual(rv, 0, "Failed to delete all flows")

    pkt = simple_tcp_packet()
    match = packet_to_flow_match(obj, pkt)
    obj.assertTrue(match is not None, "Could not generate flow match from pkt")
    for port in of_ports:
        break;
    match.in_port = port
    match.nw_src = 1
    request = message.flow_mod()
    count_check = 101  # fixme:  better way to determine this.
    if is_exact:
        match.wildcards = 0
    else:
        match.wildcards |= ofp.OFPFW_DL_SRC

    request.match = match
    request.buffer_id = 0xffffffff      # set to NONE
    caps_logger.info(request.show())

    tstats = message.table_stats_request()
    try:  # Determine the table index to check (or "all")
        table_idx = caps_config["caps_table_idx"]
    except:
        table_idx = -1  # Accumulate all table counts

    # Make sure we can install at least one flow
    caps_logger.info("Inserting initial flow")
    rv = obj.controller.message_send(request)
    obj.assertTrue(rv != -1, "Error installing flow mod")
    obj.assertEqual(do_barrier(obj.controller), 0, "Barrier failed")
    flow_count = 1

    caps_logger.info("Table idx: " + str(table_idx))
    caps_logger.info("Check every " + str(count_check) + " inserts")

    while True:
        request.match.nw_src += 1
        rv = obj.controller.message_send(request)
        flow_count += 1
        if flow_count % count_check == 0:
            obj.assertEqual(do_barrier(obj.controller), 0, "Barrier failed")
            response, pkt = obj.controller.transact(tstats)
            obj.assertTrue(response is not None, "Get tab stats failed")
            caps_logger.info(response.show())
            if table_idx == -1:  # Accumulate for all tables
                active_flows = 0
                for stats in response.stats:
                    active_flows += stats.active_count
            else: # Table index to use specified in config
                active_flows = response.stats[table_idx].active_count
            if active_flows != flow_count:
                break

    caps_logger.error("RESULT: " + str(flow_count) + " flows inserted")
    caps_logger.error("RESULT: " + str(active_flows) + " flows reported")


class FillTableExact(basic.SimpleProtocol):
    """
    Fill the flow table with exact matches; can take a while

    Fill table until no more flows can be added.  Report result.
    Increment the source IP address.  Assume the flow table will
    fill in less than 4 billion inserts

    To check the number of flows in the tables is expensive, so
    it's only done periodically.  This is controlled by the
    count_check variable.

    A switch may have multiple tables.  The default behaviour
    is to count all the flows in all the tables.  By setting 
    the parameter "caps_table_idx" in the configuration array,
    you can control which table to check.
    """
    def runTest(self):
        caps_logger.info("Running " + str(self))
        flow_caps_common(self)

test_prio["FillTableExact"] = -1

class FillTableWC(basic.SimpleProtocol):
    """
    Fill the flow table with wildcard matches

    Fill table using wildcard entries until no more flows can be
    added.  Report result.
    Increment the source IP address.  Assume the flow table will
    fill in less than 4 billion inserts

    To check the number of flows in the tables is expensive, so
    it's only done periodically.  This is controlled by the
    count_check variable.

    A switch may have multiple tables.  The default behaviour
    is to count all the flows in all the tables.  By setting 
    the parameter "caps_table_idx" in the configuration array,
    you can control which table to check.

    """
    def runTest(self):
        caps_logger.info("Running " + str(self))
        flow_caps_common(self, is_exact=False)

test_prio["FillTableWC"] = -1
