#!/usr/bin/env python
# Script that connects to Pox SDN controller and creates IRI messages
#
# Copyright (C) 2014 Barbora Frankova
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os, os.path
import sys
import socket
import json
import time
from pox.lib.revent import *
from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.util import dpid_to_str

import networkx as nx

sys.path.append("/opt/slis/light")
from modules.sockets.li2 import connectUnixSocket
socket = None

log = core.getLogger()

class SlisWrapper(object):
    def __init__(self):
        global socket
        core.listen_to_dependencies(self)
        core.openflow.addListeners(self)
        self.topo = nx.Graph()
        socket = connectUnixSocket("/tmp/iricol")
    
    def createIRI(self, iriType, textMessage, nid_ip, nid_mac, switch_type, switch_dpid, switch_port):
        nidList = []
        nidList.append(("IPv6" if ":" in nid_ip else "IPv4", nid_ip))
        nidList.append(("MAC", nid_mac))
        #switch_dpid = switch_dpid.replace('-',':')
        nidList.append(("sdnConnector", switch_type + "_" + switch_dpid + "_" + switch_port))
        return ("SDN-pox",
                time.time(),
                iriType,
                textMessage,
                nidList
                )
        
    def _all_dependencies_met (self):
        log.info("slis_wrapper ready")

    def _handle_host_tracker_UpdateIpEvent(self, event):
        global socket
        dpid, port, mac = event.mac.split(' ')
        if event.action == "assign":
            log.info("HOST %s %s : %s", event.action, event.mac, event.ip)
            msg = self.createIRI("BEGIN", "", event.ip, mac, "??", dpid_to_str(int(dpid)), port)
        elif event.action == "delete":
            msg = self.createIRI("END", "", event.ip, mac, "??", dpid_to_str(int(dpid)), port)
        else:
            msg = self.createIRI("CONTINUE", "", event.ip, mac, "??", dpid_to_str(int(dpid)), port)
        #print(msg)
        socket.send(str(msg) + '\n')

    def _handle_ConnectionUp (self, event):
        # attribute ofp - ofp_switch_features
        # ofp.n_buffers, ofp.n_tables, ofp.capabilities (bitmap), ofp.reserved, ports
        # event.connection ???
        
        #log.info("SWITCH %s UP",dpid_to_str(event.dpid))
        msg = of.ofp_stats_request(body=of.ofp_desc_stats_request())
        msg.type = 0 # for betta bug, can be removed
        event.connection.send(msg)
        self.topo.add_node(dpid_to_str(event.dpid))

    def _handle_SwitchDescReceived (self, event):
        #log.info("SWITCH %s description recieved",dpid_to_str(event.connection.dpid))
        return

    def _handle_ConnectionDown(self, event):
        #log.info("SWITCH %s DOWN",dpid_to_str(event.dpid))
        self.topo.remove_node(dpid_to_str(event.dpid))
        
    def _handle_openflow_discovery_LinkEvent(self, event):
        status = ""
        s1 = dpid_to_str(event.link.dpid1)
        s2 = dpid_to_str(event.link.dpid2)
        p1 = event.link.port1
        p2 = event.link.port2
        if event.added and not event.removed:
            status = "UP"
            if not self.topo.has_edge(s1, s2):
                self.topo.add_edge(s1, s2)
        elif not event.added and event.removed:
            status = "DOWN"
            if self.topo.has_edge(s1, s2):
                self.topo.remove_edge(s1, s2)
        else:
            status = "???"
        #log.info("LINK %s.%s -> %s.%s %s", s1, p1, s2, p2, status)

    def _handle_host_tracker_HostEvent(self, event):
        if event.join:
            log.info("ADD HOST %s %s %s", str(event.entry.macaddr), dpid_to_str(event.entry.dpid), event.entry.port)
			#topology.add_host(str(event.entry.macaddr),None,str(event.entry.dpid),event.entry.port)

        elif event.move:
            log.info("MOVE HOST %s %s %s", str(event.entry.macaddr), dpid_to_str(event.entry.dpid), event.entry.port)
			#topology.update_host(str(event.entry.macaddr),None,str(event.entry.dpid),event.entry.port)	

        elif event.leave:
            log.info("DELETE HOST %s %s %s", str(event.entry.macaddr),dpid_to_str(event.entry.dpid), event.entry.port)
			#topology.del_host(str(event.entry.macaddr))

def launch ():
    core.registerNew(SlisWrapper)
    
