# -*- coding: utf-8 -*-
"""
UTc! module: src.camera

Purpose
=======
 Provides routines for creating and managing "camera"s in the OpenGL
 environment.
 
Legal
=====
 This code is largely an adaptation of code found at 
 http://www.flipcode.com/archives/OpenGL_Camera.shtml on March 16th, 2009.
 
 All other code, unless otherwise indicated, is original, and subject to the
 terms of the GPLv3, which is provided in COPYING.
 
 (C) Neil Tallim, 2009
"""
import OpenGL.GL as GL

class Camera(object):
	"""
	An objective representation of the procedural OpenGL eye-based rendering
	logic.
	"""
	_transform = None #: This camera's 4x4 transformation matrix.
	
	def __init__(self, position):
		"""
		Constructs the initial transformation matrix, using the identity and the
		eye-position of the camera.
		
		The Z-axis is inverted and the camera is rotated 180 degrees about the
		Y-axis, effectively making it look "backwards".
		
		@type position: sequence(3)
		@param position: The (x, y, z) position of the camera in game-space.
		"""
		self._transform = [
		 1.0, 0.0, 0.0, 0.0,
		 0.0, 1.0, 0.0, 0.0,
		 0.0, 0.0, -1.0, 0.0,
		 position[0], position[1], position[2], 1.0
		]
		self.rotate(0.0, 180.0, 0.0)
		
	def getTransformationMatrix(self):
		"""
		Returns this camera's 4x4 transformation matrix.
		
		@rtype: tuple(12)
		@return: The matrix against which to transform verteces for rendering.
		"""
		transform = self._transform
		return (
		 transform[0], transform[4], -transform[8], 0.0,
		 transform[1], transform[5], -transform[9], 0.0,
		 transform[2], transform[6], -transform[10], 0.0,
		 -(transform[0] * transform[12] + transform[1] * transform[13] + transform[2] * transform[14]),
		 -(transform[4] * transform[12] + transform[5] * transform[13] + transform[6] * transform[14]),
		 (transform[8] * transform[12] + transform[9] * transform[13] + transform[10] * transform[14]),
		 1.0
		)
		
	def rotate(self, deg_x, deg_y, deg_z):
		"""
		Updates the camera's orientation, based on Euler rotations.
		
		@type deg_x: float
		@param deg_x: The angle of offset along the X-axis.
		@type deg_y: float
		@param deg_y: The angle of offset along the Y-axis.
		@type deg_z: float
		@param deg_z: The angle of offset along the Z-axis.
		"""
		GL.glPushMatrix()
		GL.glLoadMatrixf(self._transform)
		GL.glRotatef(-deg_z, 0.0, 0.0, 1.0)
		GL.glRotatef(-deg_y, 0.0, 1.0, 0.0)
		GL.glRotatef(deg_x, 1.0, 0.0, 0.0)
		transform = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX)
		self._transform = [i for i in transform[0]] + [i for i in transform[1]] + [i for i in transform[2]] + [i for i in transform[3]]
		GL.glPopMatrix()
		
	def setPosition(self, position):
		"""
		Sets this camera's position in game-space.
		
		@type position: sequence(3)
		@param position: The (x, y, z) co-ordinates at which the camera is to be
		    placed.
		"""
		transform = self._transform
		transform[12] = position[0]
		transform[13] = position[1]
		transform[14] = position[2]
		
	def setView(self):
		"""
		Sets OpenGL's transformation matrix in terms of the position and
		orientation of this camera.
		"""
		GL.glLoadMatrixf(self.getTransformationMatrix())
		
def buildCameraTransformationMatrix(position, rotation):
	"""
	Provides a camera transformation matrix for an arbitrary position and
	rotation without actually constructing a Camera object. This function is
	useful for objects that need to be aware of the world they can see in a 
	local sense, such as missiles, rather than the absolute state of the global
	game-space.
	
	@type position: sequence(3)
	@param position: The (x, y, z) position aruond which the matrix should be
	    built in game-space.
	@type rotation: sequence(3)
	@param rotation: The (x, y, z) rotation to apply in the resulting matrix.
	
	@rtype: tuple(12)
	@return: The matrix against which to transform verteces for rendering.
	"""
	GL.glPushMatrix()
	GL.glLoadMatrixf((
	 1.0, 0.0, 0.0, 0.0,
	 0.0, 1.0, 0.0, 0.0,
	 0.0, 0.0, -1.0, 0.0,
	 position[0], position[1], position[2], 1.0
	))
	GL.glRotatef(-180.0, 0.0, 1.0, 0.0)
	
	GL.glRotatef(-rotation[2], 0.0, 0.0, 1.0)
	GL.glRotatef(-rotation[1], 0.0, 1.0, 0.0)
	GL.glRotatef(rotation[0], 1.0, 0.0, 0.0)
	transform = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX)
	transform = [i for i in transform[0]] + [i for i in transform[1]] + [i for i in transform[2]] + [i for i in transform[3]]
	GL.glPopMatrix()
	
	return (
	 transform[0], transform[4], -transform[8], 0.0,
	 transform[1], transform[5], -transform[9], 0.0,
	 transform[2], transform[6], -transform[10], 0.0,
	 -(transform[0] * transform[12] + transform[1] * transform[13] + transform[2] * transform[14]),
	 -(transform[4] * transform[12] + transform[5] * transform[13] + transform[6] * transform[14]),
	 (transform[8] * transform[12] + transform[9] * transform[13] + transform[10] * transform[14]),
	 1.0
	)
	