import gtk
import ns3
from visualizer.base import InformationWindow

NODE_STATISTICS_MEMORY = 10


class StatisticsCollector(object):
    """
    Collects interface statistics for all nodes.
    """

    class NetDevStats(object):
        __slots__ = ['rxPackets', 'rxBytes', 'txPackets', 'txBytes',
                     'rxPacketRate', 'rxBitRate', 'txPacketRate', 'txBitRate']

    def __init__(self, visualizer):
        self.node_statistics = {} # nodeid -> list(raw statistics)
        self.visualizer = visualizer

    def simulation_periodic_update(self, viz):
        nodes_statistics = viz.simulation.sim_helper.GetNodesStatistics()
        for stats in nodes_statistics:
            try:
                raw_stats_list = self.node_statistics[stats.nodeId]
            except KeyError:
                raw_stats_list = []
                self.node_statistics[stats.nodeId] = raw_stats_list
            raw_stats_list.append(stats.statistics)
            while len(raw_stats_list) > NODE_STATISTICS_MEMORY:
                raw_stats_list.pop(0)

    def get_interface_statistics(self, nodeId):
        try:
            raw_stats_list = self.node_statistics[nodeId]
        except KeyError:
            return []
        
        if len(raw_stats_list) < NODE_STATISTICS_MEMORY:
            return []
        assert len(raw_stats_list) == NODE_STATISTICS_MEMORY
        tx_packets1 = [] # transmitted packets, one value per interface
        rx_packets1 = []
        tx_bytes1 = []
        rx_bytes1 = []
        for iface, stats in enumerate(raw_stats_list[0]):
            tx_packets1.append(stats.transmittedPackets)
            tx_bytes1.append(stats.transmittedBytes)
            rx_packets1.append(stats.receivedPackets)
            rx_bytes1.append(stats.receivedBytes)

        retval = []

        k = self.visualizer.sample_period*(NODE_STATISTICS_MEMORY-1)
        for iface, stats in enumerate(raw_stats_list[-1]):
            outStat = self.NetDevStats()
            outStat.txPackets = stats.transmittedPackets
            outStat.txBytes = stats.transmittedBytes
            outStat.rxPackets = stats.receivedPackets
            outStat.rxBytes = stats.receivedBytes
            
            outStat.txPacketRate = (stats.transmittedPackets - tx_packets1[iface])/k
            outStat.rxPacketRate = (stats.receivedPackets - rx_packets1[iface])/k
            outStat.txBitRate = (stats.transmittedBytes - tx_bytes1[iface])*8/k
            outStat.rxBitRate = (stats.receivedBytes - rx_bytes1[iface])*8/k
            retval.append(outStat)
        return retval


class ShowInterfaceStatistics(InformationWindow):
    (
        COLUMN_INTERFACE,

        COLUMN_TX_PACKETS,
        COLUMN_TX_BYTES,
        COLUMN_TX_PACKET_RATE,
        COLUMN_TX_BIT_RATE,

        COLUMN_RX_PACKETS,
        COLUMN_RX_BYTES,
        COLUMN_RX_PACKET_RATE,
        COLUMN_RX_BIT_RATE,

        ) = range(9)

    def __init__(self, visualizer, node_index, statistics_collector):
        InformationWindow.__init__(self)
        self.win = gtk.Dialog(parent=visualizer.window,
                              flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_NO_SEPARATOR,
                              buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
        self.win.connect("response", self._response_cb)
        self.win.set_title("Statistics for node %i" % node_index) 
        self.visualizer = visualizer
        self.statistics_collector = statistics_collector
        self.node_index = node_index
        self.viz_node = visualizer.get_node(node_index)

        self.table_model = gtk.ListStore(*([str]*13))

        treeview = gtk.TreeView(self.table_model)
        treeview.show()
        self.win.vbox.add(treeview)
        
        def add_column(descr, colid):
            column = gtk.TreeViewColumn(descr, gtk.CellRendererText(), text=colid)
            treeview.append_column(column)
            
        add_column("Interface", self.COLUMN_INTERFACE)

        add_column("Tx Packets", self.COLUMN_TX_PACKETS)
        add_column("Tx Bytes", self.COLUMN_TX_BYTES)
        add_column("Tx pkt/1s", self.COLUMN_TX_PACKET_RATE)
        add_column("Tx bit/1s", self.COLUMN_TX_BIT_RATE)

        add_column("Rx Packets", self.COLUMN_RX_PACKETS)
        add_column("Rx Bytes", self.COLUMN_RX_BYTES)
        add_column("Rx pkt/1s", self.COLUMN_RX_PACKET_RATE)
        add_column("Rx bit/1s", self.COLUMN_RX_BIT_RATE)

        self.visualizer.add_information_window(self)
        self.win.show()

    def _response_cb(self, win, response):
        self.win.destroy()
        self.visualizer.remove_information_window(self)
    
    def update(self):
        node = ns3.NodeList.GetNode(self.node_index)
        stats_list = self.statistics_collector.get_interface_statistics(self.node_index)
        self.table_model.clear()
        for iface, stats in enumerate(stats_list):
            tree_iter = self.table_model.append()
            netdevice = node.GetDevice(iface)
            interface_name = ns3.Names.FindName(netdevice)
            if not interface_name:
                interface_name = "(interface %i)" % iface
            self.table_model.set(tree_iter,
                                 self.COLUMN_INTERFACE, interface_name,

                                 self.COLUMN_TX_PACKETS, str(stats.txPackets),
                                 self.COLUMN_TX_BYTES, str(stats.txBytes),
                                 self.COLUMN_TX_PACKET_RATE, str(stats.txPacketRate),
                                 self.COLUMN_TX_BIT_RATE, str(stats.txBitRate),
                                 
                                 self.COLUMN_RX_PACKETS, str(stats.rxPackets),
                                 self.COLUMN_RX_BYTES, str(stats.rxBytes),
                                 self.COLUMN_RX_PACKET_RATE, str(stats.rxPacketRate),
                                 self.COLUMN_RX_BIT_RATE, str(stats.rxBitRate)
                                 )


def populate_node_menu(viz, node, menu, statistics_collector):
    
    menu_item = gtk.MenuItem("Show Interface Statistics")
    menu_item.show()

    def _show_it(dummy_menu_item):
        ShowInterfaceStatistics(viz, node.node_index, statistics_collector)

    menu_item.connect("activate", _show_it)
    menu.add(menu_item)


def register(viz):
    statistics_collector = StatisticsCollector(viz)
    viz.connect("populate-node-menu", populate_node_menu, statistics_collector)
    viz.connect("simulation-periodic-update", statistics_collector.simulation_periodic_update)
