# --------------------------------------------------------------------------- #
# $Id: pllcontroller.py,v 1.6 2004/01/11 21:15:50 pandr Exp $
# --------------------------------------------------------------------------- #

import libpointless
from pllshared import *
import weakref
import pllog

class Controller:
	""" 
		DESCRIPTION:

		The class is the base class for all controller objects. All controller 
		objects that are found in a NodeList will be attached to the Group node 
		in which the content of the NodeList end up. Each controller attached 
		to the Group node will able to do various actions on the Group in 
		various situations.

		METHODS:

		init
			called when the slideshow is reset

		set_node
			FIXME

		frame_update
			called at each frame update

		on_enter
			called whenever the step is entered

		on_leave
			called whenever the step is left

		PARAMETERS/VARIABLES:

		instant
			flag that indicates whether or not we want an 'instant' transition,
			i.e. from 0.0 to 1.0 or vice versa instantly. Useful for instance 
			when we transfter from slide N+1 to slide N.

	"""
	def __init__(self, parameters):
		self.node = None
		self.this = None
		self.slide_no = 0
		self.slide_step = 0
	def __repr__(self): return "Controller (%i,%i)" % (self.slide_no, 
		self.slide_step)
	def init(self): pass
	def set_node(self, node):
		self.node = weakref.proxy(node)
		assert(self.this == None)
		self.this = libpointless.PythonController_new(node.this, 
			self.frame_update)
		self.node.controllers.append(self)
	def frame_update(self, frame_time, delta_time):
		pllog.info(gt("Frame update: "), self.node.full_name())
	def on_enter(self, instant): pass
	def on_leave(self, instant): pass
	def set_active(self, active):
		libpointless.PythonController_set_active(self.this, active)

class Parametrize(Controller):
	""" 
		DESCRIPTION:

		FIXME

		METHODS:

		run
			called from on_enter/on_leave when the slide is entered from the 
			previous slide

		set
			called as part of the frame_update and supposed to set
			various properties of the group of nodes such as position,
			alpha value, ...

		on_enter_xxx
			a method than can be called if self.value is xxx when we enters

		on_leave_xxx
			a method than can be called if self.value was xxx when we left 
			the previous step

		PARAMETERS/VARIABLES:

		direction 
			a sign {-1,0,1} that indicates whether we move forward (1), has
			completed the move (0) or moves backward (-1)

		value
			a float between 0 and 1. It will move from 0 to 1 when we move
			forward and from 1 to 0 when we move backwards. The steps towards
			the end value will take place at each frame update.

		in_delay
			a hack introduced in order to make smooth transfers from step to
			another, with this we can skip the first couple of frame_updates 
			
		out_delay
			symmetric idea, but currently not used

		duration
			a factor that can speed up/slow down the time it takes for
			the direction to reach 0, i.e. make the controller idle
			until next step.

	"""
	def __init__(self, parameters):
		Controller.__init__(self, parameters)
		self.duration  = parameters.get("duration",  1.0)
		self.in_delay  = parameters.get("in_delay",  0.0)
		self.out_delay = parameters.get("out_delay", 0.0)
		self.value     = 0.0
		self.direction = 0
	def frame_update(self, frame_time, delta_time):
		if self.direction == 0:
			self.set_active(0)
			return
		if self.start_time > libpointless.clock_time(): return 
		if self.value == 0.0: self.on_leave_zero()
		if self.value == 1.0: self.on_leave_one()
		self.value += self.direction * delta_time / self.duration
		if self.value > 1.0:
			self.value     = 1.0
			self.direction = 0
			self.on_enter_one()
		elif self.value < 0.0:
			self.value     = 0.0
			self.direction = 0
			self.on_enter_zero()
		self.set()
	def on_enter(self, instant):
		self.set_active(1)
		if instant:
			## special case: we want an instant transition
			if self.value == 0.0:
				self.on_leave_zero()
			self.value = 1.0
			self.direction = 0
			self.on_enter_one()
			self.set()
		else:
			self.run(self.value,1)
	def on_enter_zero(self): pass
	def on_enter_one(self): pass
	def on_leave(self, instant):
		self.set_active(1)
		if instant:
			## special case: we want an instant transition
			if self.value == 1.0:
				self.on_leave_one()
			self.value = 0.0
			self.direction = 0
			self.on_enter_zero()
			self.set()
		else:
			self.run(self.value,-1)
	def on_leave_one(self): pass
	def on_leave_zero(self): pass
	def run(self, value, direction):
		self.direction  = direction
		self.value      = value	
		if direction > 0:
			self.start_time = libpointless.clock_time() + self.in_delay
		else:
			self.start_time = libpointless.clock_time() + self.out_delay
	def set(self): pass

class AlphaFader(Parametrize):
	""" 
		DESCRIPTION:
		
		This class serves two purposes. Firstly, it will set the alpha
		value to each time the set medthod is used. Secondly it can be
		used to move the group of nodes during the fade-in and/or
		fade-out process.

		PARAMETERS/VARIABLES:

		start_alpha/end_alpha/x_alpha
			FIXME

		move_in/move_out
			one can choose to move the group of nodes as long as the direction
			is not 0

	"""
	def __init__(self, parameters):
		Parametrize.__init__(self, parameters)
		self.start_alpha = parameters.get("start_alpha", 0.0)
		self.end_alpha   = parameters.get("end_alpha",   1.0)
		self.x_alpha     = parameters.get("x_alpha",     0.7) ## FIXME hardcoding
		## FIXME, shouldn't they be switched, move_in -> move_out and vice verse
		self.move_in     = parameters.get("move_in",   (0.0, 0.0, 0.0))
		self.move_out    = parameters.get("move_out",  (0.0, 0.0, 0.0))
		self.pos         = v3(0.0, 0.0, 0.0)
	def init(self):
		self.pos = self.node.get_pos()
	def on_enter_zero(self): 
		self.node.set_visible(0) 
	def on_leave_zero(self): 
		self.node.set_visible(1)
	def set(self):
		self.node.set_alpha(self.value)
		t = 1.0 - self.value
		if self.direction > 0:
			self.node.set_pos(
				v3(
				self.pos.x + self.move_out[0]*t, 
				self.pos.y + self.move_out[1]*t, 
				self.pos.z + self.move_out[2]*t
				)
			)
		else:
			self.node.set_pos(
				v3(
				self.pos.x - self.move_in[0]*t, 
				self.pos.y - self.move_in[1]*t, 
				self.pos.z - self.move_in[2]*t
				)
			)

# --------------------------------------------------------------------------- #

