/*
 *  Flingbox - An OpenSource physics sandbox for Google's Android
 *  Copyright (C) 2009  Jon Ander Peñalba & Endika Gutiérrez
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.eside.flingbox.physics;

import edu.eside.flingbox.Preferences;
import edu.eside.flingbox.math.Vector2D;
import edu.eside.flingbox.physics.collisions.Collider;

/**
 * Abstract class with handles all properties that any 
 * physical body should have.
 */
public abstract class PhysicBody extends PhysicAtomicBody{
	/** Implements on move callback. */
	public interface OnMovementListener {
		/**
		 * Called when movement occurs.
		 * @param position new body's position
		 * @param rotation new body's angle
		 */
		public void onMovement(Vector2D position, float angle);
	}
	
	
	/** Objects with INFINITE_ANGULAR_MASS should be imposible to rotate */
	public final static float INFINITE_ANGULAR_MASS = Float.POSITIVE_INFINITY;
	
	/** Sets if objects can be rotated */
	protected boolean mIsRotable = true;

	/** Object's current rotated angle */
	protected float mAngle = 0f;
	
	/** body's mass by square unit */
	protected float mDensity = Preferences.defaultDensity;
	/** body's volume */
	protected final float mVolume;

	/** Body's angular mass */
	protected float mAngularMass = 0f;
	/** Body's current velocity */
	
	
	/** Body's current angular Velocity */
	protected float mAngularVelocity = 0.0f;
	/** acomulated Rotational impulse */
	protected float mAcomulatedRotationalImpulse = 0f;
	
	/** OnMovement call-back listener */
	protected OnMovementListener mListener;

	
	
	/**
	 * Local constructor for any abstract body
	 * 
	 * @param bodyMass body's mass
	 * @param position body's position
	 */
	protected PhysicBody(final float bodyVolume, final Vector2D position) {
		mVolume = bodyVolume;
		mMass = bodyVolume * mDensity;
		mPosition.set(position);
	}

	/**
	 * Called to refresh object's position
	 * @param time in seconds since last update
	 */
	public void onUpdateBody(float time) {
		if (!mIsEnabled)
			return;

		super.onUpdateBody(time);
		
		if (mIsRotable) {
			mAngularVelocity += mAcomulatedRotationalImpulse / mAngularMass;
			mAngle += mAngularVelocity * time;
			mAcomulatedRotationalImpulse = 0f;
		}

		if (mIsMoveable || mIsRotable) {
			mCollider.onMovement(mPosition, mAngle);
			mListener.onMovement(mPosition, mAngle);
		}
	}
	
	/**
	 * Applies force to the object
	 * 
	 * @param force Force
	 * @param applicationPoint relative point in wich force is applied
	 * @param dt time period while force is applied
	 */
	public void applyImpulse(Vector2D impulse, Vector2D applicationPoint) {
		mAcomulatedImpulse.add(impulse);
		
		mAcomulatedRotationalImpulse  += impulse.crossProduct(applicationPoint);
	}
	
	/** Fixs body, making impossible to move  */
	public void setBodyFixed(boolean fixed) {
		mIsMoveable = !fixed;
		mIsRotable = !fixed;

		if (fixed) {
			// Stop current object
			mVelocity.set(0f, 0f);
			mAngularVelocity = 0f;
			//This will make object fixed
		} 
		
	}
	
	/** @return true if body can is fixed */
	public boolean isFixed() {
		return !(mIsMoveable || mIsRotable);
	}
	
	/**
	 * @return Body's angular mass
	 */
	public float getAngularMass() {
		return mAngularMass;
	}
	
	public float getDensity() {
		return mDensity;
	}
	
	public void setDensity(float density) {
		mDensity = density;
		mMass = mVolume * density;
	}
	
	/**
	 * @return velocity
	 */
	public Vector2D getVelocity() {
		return mVelocity;
	}
	
	/**
	 * Sets velocity components
	 * 
	 * @param vx
	 * @param vy
	 */
	public void setVelocity(float vx, float vy) {
		mVelocity.set(vx, vy);
	}
	
	/** @return Body's angular velocity */
	public float getAngularVelocity() {
		return mAngularVelocity;
	}
	
	/**
	 * @return Body's angle
	 */
	public float getAngle() {
		return mAngle;
	}
	
	/**
	 * @param angle new object's angle
	 */
	public void setAngle(float angle) {
		mAngle = angle;
		onUpdateBody(0);
	}
	
	/** 
	 * Computes current object's energy
	 *  
	 * @return energy in juls
	 */
	public float getEnergy() {
		final float velocity = mVelocity.length() ;
		final float kineticEnergy = 0.5f * mMass * velocity * velocity;
		final float kineticRotationalEnergy = 0.5f * mAngularMass * mAngularVelocity * mAngularVelocity;
		return kineticEnergy + kineticRotationalEnergy;
	}
	

	
	/**
	 * @return the Collider
	 */
	public Collider getCollider() {
		return mCollider;
	}

}
