import socket
import select

## This module provides library to send and receive messages to NOX's messenger
#
# This is a rewrite of noxmsg.py from OpenRoads (OpenFlow Wireless)
#
# @author ykk (Stanford University)
# @date January, 2010
# @see messenger

def stringarray(string):
    """Output array of binary values in string.
    """
    arrstr = ""
    if (len(string) != 0):
        for i in range(0,len(string)):
            arrstr += "%x " % struct.unpack("=B",string[i])[0]
    return arrstr

def printarray(string):
    """Print array of binary values
    """
    print "Array of length "+str(len(string))
    print stringarray(string)

class channel:
    """TCP channel to communicate to NOX with.
    """
    def __init__(self,ipAddr,portNo=2603,debug=False):
        """Initialize with socket
        """
        ##Socket reference for channel
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((ipAddr,portNo))
        self.debug = debug
        ##Internal buffer for receiving
        self.__buffer = ""
        ##Internal reference to header
        self.__header = messenger_msg()

    def baresend(self, msg):
        """Send bare message"""
        self.sock.send(msg)

    def send(self,msg):
        """Send message
        """
        msgh = messenger_msg()
        remaining = msgh.unpack(msg)
        if (msgh.length != len(msg)):
            msgh.length = len(msg)
            msg = msgh.pack()+remaining
        self.baresend(msg)
        if (self.debug):
            printarray(msg)
        
    def receive(self, recvLen=0,timeout=0):
        """Receive command
        If length == None, nonblocking receive (return None or message)
        With nonblocking receive, timeout is used for select statement

        If length is zero, return single message
        """            
        if (recvLen==0):
            #Receive full message
            msg=""
            length=len(self.__header)
            while (len(msg) < length):
                msg+=self.sock.recv(1)
                #Get length
                if (len(msg) == length):
                    self.__header.unpack(msg)
                    length=self.__header.length
            return msg
        elif (recvLen==None):
            #Non-blocking receive
            ready_to_read = select.select([self.sock],[],[],timeout)[0]
            if (ready_to_read):
                self.__buffer += self.sock.recv(1)
            if (len(self.__buffer) >= len(self.__header)):
                self.__header.unpack(self.__buffer)
                if (self.__header.length == len(self.__buffer)):
                    msg = self.__buffer
                    self.__buffer = ""
                    return msg
            return None
        else:
            #Fixed length blocking receive
            return self.sock.recv(recvLen)

    def __del__(self):
        """Terminate connection
        """
        emsg = messenger_msg()
        emsg.type = MSG_DISCONNECT
        emsg.length = len(emsg)
        self.send(emsg.pack())
        self.sock.shutdown(1)
        self.sock.close()

class sslChannel(channel):
    """SSL channel to communicate to NOX with.
    """
    def __init__(self, ipAddr, portNo=1304,debug=False):
        """Initialize with SSL sock
        """
        NOXChannel.__init__(self, ipAddr, portNo,debug)
        ##Reference to SSL socket for channel
        self.sslsock = socket.ssl(self.sock)

    def baresend(self, msg):
        """Send bare message"""
        self.sslsock.write(msg)

