#!/usr/bin/python
#
""" wtMessenger.py

 author michael branton

 This script 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 script; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""

import OSC
from OSC import OSCMessage
from wtConnection import wtConnection

class wtMessenger:

	"""encapsulates basic methods needed to talk to a
	wireTAP server (http://wiretap.stetson.edu/)"""

	def __init__(self):

		"""This is the class constructor.  It can be used to create a
		new wtMessenger object. All default values will be set by 
		this function so it is best to call it when extending 
		from this class. Defaults to UDP, blocking, with 
		server port 4950 and a randomly selected client port."""
 
		self.message = OSCMessage()
        	self.mySubscriptions = []
		self.setConnected('False')
		self.conn=wtConnection(0)
		self.setClientPort(0)
		self.setServerPort(4950)
		self.setProtocol('UDP')
		self.setChannel('')
		self.setClearOnSend('True')
		self.setBlocking('True')
		
	def serverConnect(self,server,*channel):

		"""This function will open a connection to the specified server 
		and set the default channel. It returns 'True' on success and 'False' 
		otherwise. The server can be an IP Address or host name."""

		self.setServer(server)
		self.setChannel(channel[0])
		self.conn.create()
		self.setConnected('True')
		return 'True'

	def serverDisconnect(self):

		"""This function will close the connection if it is open. It also 
		unsubscribes from all subscribed channels so that multiple port 
		subscriptions do not linger."""

		for sub in self.mySubscriptions:
			self.unsubscribe(sub)
		if self.conn != '':
			self.conn.close()
		self.setConnected('False')

	def send(self,*channel):

		"""This function will send a message to the connected server. 
		If channel is not explicitly set, then the default channel will be 
		used. This function will prepend "/in/" to the channel unless the 
		explicit "/" is detected" """

		if len(channel)==0: # default to this object's channel
			chan=self.channel
		else:
			chan = channel[0] # only look at first channel passed in

		if chan[0]!='/':  # prepend default /in/
			chan = '/in/'+chan

		self.message.setAddress(chan)
		self.conn.setHostPort(self.serverport)
		self.conn.send(self.message.getBinary())

		# check to see if we should clear the message
		if self.cos == 'True':
			self.clear()

	def subscribe(self,*channel):

		"""This function is a special form of the send function. Set channel 
		to the name of a subscribe channel you wish to receive data from. The 
		specified channel will automatically be prepended with "/subscribe/" 
		so keep this in mind when setting the value. You may use an explicit channel 
		by starting the channel name with "/". If you omit channel (e.g. subscribe() )
		the current default channel will be used. """

		# no data in subscribe message
		self.clear()

		if len(channel)==0: # default to this object's channel
			chan=self.channel
			if len(self.mySubscriptions) == 0: # channel was set in serverConnect, but this is 1st
							   # subscription request
				self.mySubscriptions.append(chan)
		else:
			chan = channel[0] # only look at first channel passed in
                	self.mySubscriptions.append(chan)
			if self.channel == '': # make this the default channel
                        	self.setChannel(chan)
		if chan[0] != '/': # prepend default /subscribe/
			chan = '/subscribe/'+chan
		print "subscribing "+chan
		self.send(chan)

	def unsubscribe(self,*channel):

		"""This function is a special form of the send function. Set channel to the 
		name of an unsubscribe channel you wish to stop receiving data from. The 
		channel specified will automatically be prepended with "/unsubscribe/" 
		so keep this in mind when setting the value or use the explicit channel 
		notation (channel starts with a "/"). If you omit channel (e.g. unsubscribe() ),
		the default channel will be used."""

		# no data in unsubscribe message
		self.clear()
		if len(channel)==0: # default to this object's channel
			chan=self.channel
		else:
			chan = channel[0] # only look at first channel passed in

		if chan[0] != '/': # prepend default /unsubscribe
			chan = '/unsubscribe/'+chan
		print "unsubscribing "+chan
		self.send(chan)			

	def add(self,*data):

		"""This function will add a single value or an array of values to 
		the current message."""

		for datum in data:
			self.message.append(datum)

	def addChannel(self, channel, *description):
		"""This function will create a new channel on the server."""
		self.add("add")
		self.add(channel)
		self.add(description)
		self.send("/channels")

	def removeChannel(self, channel):
		"""This function will remove a channel from the server."""
		self.add("remove")
		self.add(channel)
		self.send("/channels")

	def clear(self):

		"""This function will clear the current message. This function is 
		automatically called after a send when the clear on send flag is set 
		to true."""

		self.message.clearData()

	def receive(self,received):

		"""This function is used to receive data from a wireTAP server. The 
		function will save a wtData object by reference into the received argument 
		whether data was received or not."""

		rc = 'False'
		if self.isConnected()=='True':
			data, address = self.conn.receive()
			for datum in OSC.decodeOSC(data):
				received.append(datum)
			rc = 'True'

		return rc
			
	def setServer(self,server):
		"""private"""
		# private method. set server to connect to.
		self.server=server
		self.conn.setHost(self.server)

	def setChannel(self,channel):

		"""This function will set the default channel."""

		self.channel=channel

	def setProtocol(self,protocol):

		"""This function will set the protocol used by serverConnect."""

		self.protocol=protocol
		self.conn.setProtocol(self.protocol)

	def setServerPort(self,port):

		"""This function will set the port used by serverConnect."""

		self.serverport=port
		# if we've already got a connection instantiated
		# set the port it should be sending to on the server
		if self.conn != '':
			self.conn.setHostPort(self.serverport)

	def setClientPort(self,port):

		"""This function will set the port used by the messenger object 
		to receive data on."""

		self.clientport=port
		self.conn.setClientPort(self.clientport)

	def setBlocking(self,block):

		"""This function will set the blocking mode used when receiving data 
		from a wireTAP server. """

		if block=='True':
			self.conn.block()
		else:
			self.conn.nonblock()
	
	def setClearOnSend(self,clear):

		"""This function will set the clear on send flag. When set to true, 
		the send function will automatically clear the current message after 
		each call. """

		self.cos=clear

	def setConnected(self,state):
		"""private"""
		# not part of the API. set connection state.
		self.connected=state

	def isConnected(self):

		"""This function returns 'True' if connected and 'False' otherwise."""

		return self.connected
