package edu.uprm.walsaip.vte.core.camera;

import static edu.uprm.walsaip.vte.core.datatypes.Vector.X;
import static edu.uprm.walsaip.vte.core.datatypes.Vector.Y;
import static edu.uprm.walsaip.vte.core.datatypes.Vector.Z;
import static java.lang.System.out;

import java.awt.Color;

import javax.media.opengl.GL;

import edu.uprm.walsaip.vte.core.ModuleInfo;
import edu.uprm.walsaip.vte.core.datatypes.Light;
import edu.uprm.walsaip.vte.core.datatypes.Position;
import edu.uprm.walsaip.vte.core.datatypes.Vertex;
import edu.uprm.walsaip.vte.core.messagebus.Message;
import edu.uprm.walsaip.vte.core.messagebus.MessageBus;
import edu.uprm.walsaip.vte.core.messagebus.MessageTimer;


/**
 * Class that represents the virtual camera.
 * 
 * @author Ricardo Veguilla (veguilla@ece.uprm.edu)
 * 
 */
public class Fully3DCamera implements Camera {
	
	public static final String ID =  Fully3DCamera.class.getSimpleName();
	public static final String TYPE = Camera.ID;
	public static final ModuleInfo<Fully3DCamera> MODULE_INFO = new ModuleInfo<Fully3DCamera>() {
		
		protected void setInfo() {
			name = Fully3DCamera.ID;
			description = "A Generic Camera Implementation.";
			moduleClass = Fully3DCamera.class;
			supported = true;
		}
	};
			
	
	private Position position;
	
	private Light light;

	/**
	 * Default constructor.
	 */
	private static float deltaAngle = 1.0f;

	private static float movementOffset = 100.0f;
	private int movementDelay = 50;
	
	private int rotationDelay = 10;
	private static float zoomFactor = 1000f;

	private float cameraMatrix[] = new float[16];
	private MessageTimer messageTimer;

//	private float maxModelElevation = 0;
	private MessageTimer.Action actionMoveForward;

	private MessageTimer.Action actionRollRight;

	private MessageTimer.Action actionRollLeft;

	private MessageTimer.Action actionRotateRight;

	private MessageTimer.Action actionRotateLeft;

	private MessageTimer.Action actionRotateDown;

	private MessageTimer.Action actionRotateUp;

	private MessageTimer.Action actionMoveDown;

	private MessageTimer.Action actionMoveUp;

	private MessageTimer.Action actionMoveRight;

	private MessageTimer.Action actionMoveLeft;

	private MessageTimer.Action actionMoveBackward;
	
	public Fully3DCamera() {
		super();
		this.position = new Position();
		//this.position.setAngle(new Angle(180f, 0f, 0f));
		// this.position.setPosition( new Vertex (0.0f,0.0f,0.0f));
		this.light = new Light(GL.GL_LIGHT1);
		this.light.setAmbient(Color.RED);
		MessageBus.addListener(this,Camera.ID);
		messageTimer = new MessageTimer();
		initEventActions();
	}

	
	
	
	private void initEventActions() {
		
		actionMoveForward = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getForward(),-zoomFactor);		
					}
				};
		actionMoveBackward = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getForward(),zoomFactor);
					}
				};
		actionMoveLeft = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getRight(),-movementOffset);
					}
				};
		actionMoveRight = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getRight(),movementOffset);
					}
				};
		actionMoveUp = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getUp(),	movementOffset);
					}
				};
		actionMoveDown = messageTimer.new Action() {
					@Override
					public void exec() {
						position.getPosition().multiplyAndAdd(position.getUp(),	-movementOffset);
					}
				};
		actionRotateUp = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisX(deltaAngle / 1.0f);
					}
				};
		actionRotateDown = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisX(-deltaAngle / 1.0f);
					}
				};
		actionRotateLeft = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisY(-deltaAngle);
					}
				};
		actionRotateRight = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisY(deltaAngle);
					}
				};
		actionRollLeft = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisZ(-deltaAngle);
					}
				};
		actionRollRight = messageTimer.new Action() {
					@Override
					public void exec() {
						position.rotateAroundAxisZ(deltaAngle);
					}
				};
		
	
		
	}
	
	public void init(GL gl) {
		// light.init(GL.GL_LIGHT1);

		// light.setConstantAttenuation(0.8f);
		out.println(Fully3DCamera.ID+" Initialization: completed");
		
	}

	public int render(GL gl) {

		// this.light.getPosition().setAngle(this.getPosition().getAngle());
		// light.draw(gl);

		updateCameraMatrix();
		gl.glMultMatrixf(cameraMatrix, 0);
		return 0;
	}

	private void updateCameraMatrix() {

		float forward[] = position.getForward().getAsArray();
		float right[] = position.getRight().getAsArray();
		float up[] = position.getUp().getAsArray();
		float pos[] = position.getPosition().getAsArray();

		cameraMatrix[0] = right[X];
		cameraMatrix[4] = right[Y];
		cameraMatrix[8] = right[Z];
		cameraMatrix[12] = -Vertex.dotProduct(pos, right);

		cameraMatrix[1] = up[X];
		cameraMatrix[5] = up[Y];
		cameraMatrix[9] = up[Z];
		cameraMatrix[13] = -Vertex.dotProduct(pos, up);

		cameraMatrix[2] = forward[X];
		cameraMatrix[6] = forward[Y];
		cameraMatrix[10] = forward[Z];
		cameraMatrix[14] = -Vertex.dotProduct(pos, forward);

		cameraMatrix[3] = 0.0f;
		cameraMatrix[7] = 0.0f;
		cameraMatrix[11] = 0.0f;
		cameraMatrix[15] = 1.0f;

	}

	/**
	 * @return Returns the position.
	 */
	public Position getPosition() {
		return position;
	}

	/**
	 * @param position
	 *            The position to set.
	 */
	public void setPosition(Position position) {
		this.position = position;
	}

	public Light getLight() {

		return light;
	}

	public void setLight(Light light) {
		this.light = light;
	}

	public void processEvent(Message<?> e) {

		
			String name = e.getName();
			String type = e.getType();
			Object value = e.getValue();

				
				if (type.equals(Message.MSG_START)) { 
					if (name.equals(MOVE_FORWARD)) 
						messageTimer.startEvent(actionMoveForward, movementDelay);	
					else if (name.equals(MOVE_BACKWARD)) 
						messageTimer.startEvent(actionMoveBackward, movementDelay);	
					else if (name.equals(MOVE_LEFT)) 
						messageTimer.startEvent(actionMoveLeft, movementDelay);	
					else if (name.equals(MOVE_RIGHT)) 
						messageTimer.startEvent(actionMoveRight, movementDelay);	
					else if (name.equals(MOVE_UP)) 
						messageTimer.startEvent(actionMoveUp, movementDelay);	
					else if (name.equals(MOVE_DOWN)) 
						messageTimer.startEvent(actionMoveDown, movementDelay);	
					else if (name.equals(ROTATE_UP)) 
						messageTimer.startEvent(actionRotateUp, rotationDelay);	
					else if (name.equals(ROTATE_DOWN)) 
						messageTimer.startEvent(actionRotateDown, rotationDelay);	
					else if (name.equals(ROTATE_LEFT)) 
						messageTimer.startEvent(actionRotateLeft, rotationDelay);	
					else if (name.equals(ROTATE_RIGHT)) 
						messageTimer.startEvent(actionRotateRight, rotationDelay);	
					else if (name.equals(ROLL_LEFT)) 
						messageTimer.startEvent(actionRollLeft, rotationDelay);	
					else if (name.equals(ROLL_RIGHT)) 
						messageTimer.startEvent(actionRollRight, rotationDelay);	
					
				}		
				else if (type.equals(Message.MSG_STOP)) {
					
					messageTimer.stopEvent();
				}
				else if (type.equals(Message.MSG_INSTANT) && !messageTimer.isTimerStarted()) {
					
					
					if (name.equals(MOVE_FORWARD)) 
						actionMoveForward.exec();
					else if (name.equals(MOVE_BACKWARD)) 
						actionMoveBackward.exec();
					else if (name.equals(MOVE_LEFT)) 
						actionMoveLeft.exec();
					else if (name.equals(MOVE_RIGHT)) 
						actionMoveRight.exec();
					else if (name.equals(MOVE_UP)) 
						actionMoveUp.exec();
					else if (name.equals(MOVE_DOWN)) 
						actionMoveDown.exec();
					else if (name.equals(ROTATE_UP)) 
						actionRotateUp.exec();
					else if (name.equals(ROTATE_DOWN)) 
						actionRotateDown.exec();
					else if (name.equals(ROTATE_LEFT)) 
						actionRollLeft.exec();
					else if (name.equals(ROTATE_RIGHT)) 
						actionRollRight.exec();
					else if (name.equals(ROLL_LEFT)) 
						actionRollLeft.exec();
					else if (name.equals(ROLL_RIGHT)) 
						actionRollRight.exec();
					else if (name.equals(SET_MOVEMENT_OFFSET)) 
						movementOffset = Float.class.cast(value); 
					else if (name.equals(SET_ANGLE_DELTA)) 
						deltaAngle = Float.class.cast(value);
					else if (name.equals(SET_POSITION)) { 
						
						//position.getPosition().setZ(-Float.class.cast(value));
						position = Position.class.cast(value);
						//System.out.println(position);
					}
					else if (name.equals(SET_MAX_MODEL_ELEVATION)) { 
						//maxModelElevation = Float.class.cast(value)*2f;
					}
				} 
				
					
					
				
					

	}


}
