from Client import Client
import Message as message
from Log import log

from zone import Zone
from clock import GameClock
import services
from sims.sim import Sim
from distributor import ops
from camera import _camera_position

from struct import unpack
from time import sleep
from random import choice

SIGNATURE		= b"RTBP"
VERSION_MAJOR	= 0
VERSION_MINOR	= 1

#GREETING_STRING = "Reading you loud and clear! (Communication established between The Sims 4 and RTBP)"

GREETINGS_STRING_PREFIXES = \
[
	"Reading you loud and clear.",
	"Hailing frequencies open.",
	"We're in the pipe, five by five.",
	"Giddyup!"
]

GREETINGS_STRING_SUFFIX = "Communication established between The Sims 4 and RTBP"

CONNECTION_ATTEMPTS			= 10
CONNECTION_FAILURE_TIMEOUT	= 10

_client			= None
_currentSpeed	= None

class SimulationClient(Client):
	def __init__(self,*args,**kargs):
		super().__init__(*args,**kargs)
	
	def log(self,string):
		self.send(message.Log(string))
		
	def setClockSpeed(self,speed):
		self.send(message.SetSpeed(speed))
		
	def onNewSimAdded(self,id,name):
		self.send(message.NewSimAdded(id,name))
		
	def onSimRemoved(self,id):
		self.send(message.SimRemoved(id))
		
	def onSimOutfitChanged(self,id):
		self.send(message.SimOutfitChanged(id))

def addClassMethodBeforeHook(Cls,method):
	def decorator(hook):
		def call(*args,**kargs):
			hook(*args,**kargs)
			
			return method(*args,**kargs)
		
		setattr(Cls,method.__name__,call)
		
		return hook
	
	return decorator

def addClassMethodAfterHook(Cls,method):
	def decorator(hook):
		def call(*args,**kargs):
			ret = method(*args,**kargs)
			
			hook(*args,**kargs)
			
			return ret
		
		setattr(Cls,method.__name__,call)
		
		return hook
	
	return decorator
	
#def addInstanceMethodHook(method):
	#def decorator(hook):
		#def call(*args,**kargs):
			#ret = method(*args,**kargs)
			
			#hook(method.__self__,*args,**kargs)
			
			#return ret
		
		#setattr(method.__self__,method.__name__,call)
		
		#return hook
	
	#return decorator
	
def start():
	global _client
	global _currentSpeed
	
	for i in range(CONNECTION_ATTEMPTS):
		try:
			_client = SimulationClient("127.0.0.1",8009)
			
			break
			
		except Exception as error:
			_client = None
			
			log("Unable to connect to RTBP's simulation server: {}".format(error))
			
			if i<CONNECTION_ATTEMPTS:
				log("Attempt {}/{}. Retrying in {} seconds.".format((i+1),CONNECTION_ATTEMPTS,CONNECTION_FAILURE_TIMEOUT))
				
				sleep(CONNECTION_FAILURE_TIMEOUT)
			
	else:
		return

	header = _client.read()

	(signature,versionMajor,versionMinor) = unpack("<4sBB",header)

	if signature!=SIGNATURE:
		raise Exception("Error communicating with RTBP simulation server: invalid protocol signature: \"{}\"".
			format(signature))

	if versionMajor!=VERSION_MAJOR or versionMinor!=VERSION_MINOR:
		raise Exception("Error communicating with RTBP simulation server: wrong protocol version: \"{}.{}\"".
			format(versionMajor,versionMinor))

	_client.log("{} ({})".format(choice(GREETINGS_STRING_PREFIXES),GREETINGS_STRING_SUFFIX))
	
	@addClassMethodBeforeHook(GameClock,GameClock._sync_clock_and_broadcast_gameclock)
	def onSetClockSpeed(self,*args,**kargs):
		global _client
		global _currentSpeed
		
		speed = self.clock_speed
		
		if _currentSpeed==speed:
			return
		
		_currentSpeed = speed
		
		_client.setClockSpeed(_currentSpeed)
		
	#@addClassMethodHook(GameClock,GameClock.request_pause)
	#def onRequestPause(self,source,*args,**kargs):
		#global _client
		
		#_client.setClockSpeed(0)
		
	@addClassMethodBeforeHook(Sim,Sim.on_add)
	def onNewSimAdded(self,*args,**kargs):
		global _client
		
		_client.onNewSimAdded(self.sim_id,self.full_name)
		
	@addClassMethodBeforeHook(Sim,Sim.on_remove)
	def onSimRemove(self,*args,**kargs):
		global _client
		
		_client.onSimRemoved(self.sim_id)
		
	@addClassMethodBeforeHook(Sim,Sim.on_outfit_changed)
	def onSimOutfitChanged(self,category_and_index,*args,**kargs):
		global _client
		
		#_client.log("Sim Outfit Changed: {}".format(self.sim_info.get_current_outfit())) # <- Works.
		#_client.log("Sim Outfit Changed: {}".format(self.sim_info)) # <- Works.
		
		_client.onSimOutfitChanged(self.sim_id)
		
		# for i in self.sim_info.get_part_ids_for_current_outfit():
		# 	_client.log("Part ID: {}".format(i))
	
start()
