
#-
# ==========================================================================
# Copyright (C) 2008 Autodesk, Inc. and/or its licensors.  All 
# rights reserved.
#
# The coded instructions, statements, computer programs, and/or related 
# material (collectively the "Data") in these files contain unpublished 
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
# licensors, which is protected by U.S. and Canadian federal copyright 
# law and by international treaties.
#
# The Data is provided for use exclusively by You. You have the right 
# to use, modify, and incorporate this Data into other products for 
# purposes authorized by the Autodesk software license agreement, 
# without fee.
#
# The copyright notices in the Software and this entire statement, 
# including the above license grant, this restriction and the 
# following disclaimer, must be included in all copies of the 
# Software, in whole or in part, and all derivative works of 
# the Software, unless such copies or derivative works are solely 
# in the form of machine-executable object code generated by a 
# source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
# AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
# WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
# NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
# TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
# BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
# AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
# OR PROBABILITY OF SUCH DAMAGES.
#
# ==========================================================================
#+

# This is a simple example of how to set up a client/server system within
# Python. The example server takes requests for shots to work on and
# responds with the information for a new shot.
#
# To try out the example, do the following:
#
# 1) Change the SERVER setting below to reflect the system on which you
#    will run the server.
#
# 2) On the server system, execute the startServer() function.
#
# 3) On a client system, create a Shot instance to serve as your "old"
#    shot (you can use the 'makeSampleShot' function if you're lazy)
#    and pass it to the requestNewShot() function. That will send your
#    request to the server and return a new Shot instance from the server.
#
# 4) Print the 'shot' field of the returned Shot and you'll see that it
#    is different from the old shot.
#
# 5) On any system, execute the stopServer() function to shut down the
#    server.
#
# In addition, saveShotDefaults() can be used to save a shot to the
# 'shotDefaults' file in the user's home directory. You can then exit
# Maya/Python and the next time you run it use loadShotDefaults() to
# get the shot info back.


# The SERVER setting below assumes that the server and the clients are
# all running on the same machine. If that is not the case then change
# then set SERVER to be either the name or IP address of the machine on
# which the server is running.
SERVER = 'localhost'
PORT = 50507


import cPickle
import os
import socket

#----------------- Classes ------------------------
class Shot:
	def __init__(self):
		self.user = None
		self.production = None
		self.sequence = None
		self.scene = None
		self.shot = None


class ShotRequest:
	def __init__(self):
		self.type = None
		self.shot = None


#----------------- Save/Restore Shot Info ---------

# Save the shot to the user's 'shotDefaults' file.
def saveShotDefaults(shotInfo):
	try:
		home = os.environ['HOME']
		fileName = os.path.join(home, 'shotDefaults')
		file = open(fileName, 'wb')
		pickler = cPickle.Pickler(file, 2)
		pickler.dump(shotInfo)
		file.close()
	except:
		pass


# Load the shot info from the user's 'shotDefaults' file.
def loadShotDefaults():
	try:
		home = os.environ['HOME']
		fileName = os.path.join(home, 'shotDefaults')
		file = open(fileName, 'rb')
		unpickler = cPickle.Unpickler(file)
		shotInfo = unpickler.load()
		file.close()
	except:
		# We couldn't get the user's defaults so
		# let's just create an empty Shot.
		shotInfo = Shot()

	return shotInfo



#----------------- Server Functions ---------------

# Start the shot server.
def startServer():
	# Create a socket for listening for requests.
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	host = ''
	port = PORT
	s.bind((host, port))
	s.listen(5)

	while True:
		# Wait for a sender to connect.
		conn, sender = s.accept()

		# Get the request string and unpickle it.
		requestStr = conn.recv(4096)
		request = cPickle.loads(requestStr)

		# Handle the request.
		if request.type == 'Stop':
			conn.close()
			break
		elif request.type == 'NewShot':
			newShot = _getNextShot(request.shot)

			# Pickle the new shot into a string
			# and send it back to the requestor.
			responseStr = cPickle.dumps(newShot)
			conn.send(responseStr)

		# Close the connection to the client.
		conn.close()

	# Close our listening socket.
	s.close()


# Shut down the shot server.
def stopServer():
	# Connect to the server's socket.
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	host = SERVER
	port = PORT
	s.connect((host, port))
	
	# Create a stop request.
	request = ShotRequest()
	request.type = 'Stop'

	# Pickle the request into a string and send it
	# to the server.
	requestStr = cPickle.dumps(request, 2)
	s.send(requestStr)

	# There won't be a response, so close the connection.
	s.close()


# Trivial implementation which simply increments
# the shot and scene fields. A proper implementation
# would maintain a database of shots and search it
# for the next available one.
def _getNextShot(oldShot):
	if oldShot.shot == None:
		oldShot.shot = 1
	else:
		print "%s completed shot (%s, %d, %d, %d)" % (oldShot.user, oldShot.production, oldShot.scene, oldShot.sequence, oldShot.shot)
		oldShot.shot += 1

	if oldShot.shot > 8:
		oldShot.shot = 1

		if oldShot.scene == None:
			oldShot.scene = 1
		else:
			oldShot.scene += 1

	print "%s assigned shot (%s, %d, %d, %d)" % (oldShot.user, oldShot.production, oldShot.scene, oldShot.sequence, oldShot.shot)

	return oldShot


#----------------- Client Functions ---------------

# Convenience function to generate a sample Shot.
def makeSampleShot():
	shot = Shot()
	shot.user = "Kristine Middlemiss"
	shot.production = "2009 Games Developer Conference"
	shot.sequence = 1
	shot.scene = 1
	shot.shot = 0
	return shot


# Convenience function to display a Shot object's contents.
def dumpShot(shot):
	print
	print "User:       %s" % shot.user
	print "Production: %s" % shot.production
	print "Scene:      %d" % shot.scene
	print "Sequence:   %d" % shot.sequence
	print "Shot:       %d" % shot.shot
	print


# Request a new shot to work on. You have to pass in your old
# shot so that the server can give you a shot which is close
# to it. (E.g. same scene)
def requestNewShot(oldShot):
	if oldShot == None:
		raise ValueError, 'Old shot is not valid.'

	# Create the request object.
	request = ShotRequest()
	request.type = 'NewShot'
	request.shot = oldShot

	# Create a socket and connect it to the server.
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	host = SERVER
	port = PORT
	s.connect((host, port))
	
	# Pickle the request into a string and send it
	# to the server.
	requestStr = cPickle.dumps(request, 2)
	s.send(requestStr)

	# Read the server's response into a string and
	# unpickle it into a Shot object.
	responseStr = s.recv(4096)
	newShot = cPickle.loads(responseStr)

	s.close()

	return newShot

