package de.rico.adventure.actions.custom.concurrent;

import javax.media.opengl.GL;

import de.rico.adventure.actions.ActionScheduler;
import de.rico.adventure.entity.character.AbstractCharacter;
import de.rico.engine.geometry.AbstractMesh;
import de.rico.engine.geometry.MeshVA;
import de.rico.engine.geometry.Point3D;
import de.rico.engine.geometry.modelloader.Animation;
import de.rico.engine.path.Path;
import de.rico.engine.path.PathFinder;

/**
 * A game action that represents walking to a new point
 * in the game location applying path finding
 * @author Frank Bruns
 *
 */
public class ConcurrentWalk extends AbstractConcurrentAction
{
	private Path pathToWalk = null;
	private float targX = 0f;
	private float targZ = 0f;
	private float speed = 0f;

	private String animName = null;
	private Animation backupAnim = null;

	// flag to signal that the entity was enabled initially
	private Boolean wasEnabled = null;

	private boolean hasArrivedAtTarget = false;

	/**
	 * Creates a new walking action with the specified (x,z)-coordinates
	 * as the target position.
	 * @param c character that ought to be walking
	 * @param x x-coordinate of the target position
	 * @param z z-coordinate of the target position
	 * @param speed speed factor to use for walking
	 */
	public ConcurrentWalk(AbstractCharacter c, float x, float z, float speed)
	{
		this(c,
				PathFinder.getPath(
						c.getMesh().getPosition(),
						new Point3D(x,0,z),
						c.getRoom().getGround().getSearchGrid()),
				speed);
	}

	/**
	 * Creates a new walking action with the specified (x,z)-coordinates
	 * as the target position.
	 * @param c character that ought to be walking
	 * @param x x-coordinate of the target position
	 * @param z z-coordinate of the target position
	 * @param speed speed factor to use for walking
	 * @param animName name of the animation to play while the
	 * action is active
	 */
	public ConcurrentWalk(AbstractCharacter c, float x, float z, float speed, String animName)
	{
		this(c,
				PathFinder.getPath(
						c.getMesh().getPosition(),
						new Point3D(x,0,z),
						c.getRoom().getGround().getSearchGrid()),
				speed,
				animName);
	}

	/**
	 * Creates a new walking action with the specified path
	 * that provides the waypoints to the target location.
	 * @param c the character that walks
	 * @param path path to walk for the character
	 * @param speed speed factor to use for walking
	 */
	public ConcurrentWalk(AbstractCharacter c, Path path, float speed)
	{
		super(c);
		this.pathToWalk = path;
		this.speed = speed;

		if(this.pathToWalk.getPoints().isEmpty() == false)
		{
			this.targX = this.pathToWalk.getPoints().getLast().x;
			this.targZ = this.pathToWalk.getPoints().getLast().z;
		}
		else
		{
			this.targX = c.getMesh().getPosX();
			this.targZ = c.getMesh().getPosZ();
		}
	}

	/**
	 * Creates a new walking action with the specified path
	 * that provides the waypoints to the target location.
	 * @param c the character that walks
	 * @param path path to walk for the character
	 * @param speed speed factor to use for walking
	 * @param animName name of the animation to play while the
	 * action is active
	 */
	public ConcurrentWalk(AbstractCharacter c, Path path, float speed, String animName)
	{
		this(c,path,speed);

		this.animName = animName;
	}

	/**
	 * Returns the x-coordinate of the walking target position
	 * @return target x-coordinate
	 */
	public float getTargetX()
	{
		return this.targX;
	}

	/**
	 * Returns the z-coordinate of the walking target position
	 * @return target z-coordinate
	 */
	public float getTargetZ()
	{
		return this.targZ;
	}

	/**
	 * Returns whether the character has arrived at its designated target
	 * location.
	 * @return has arrived at target location? --> true=yes, false=no
	 */
	public boolean hasArrivedAtTarget()
	{
		return this.hasArrivedAtTarget;
	}

	@Override
	public void update(long elapsedTime)
	{
		AbstractMesh mesh = this.getEntity().getMesh();

		if(this.wasEnabled == null)
			this.wasEnabled = this.getEntity().isEnabled();

		if(this.wasEnabled == true)
		{
			// disable entity for clicks while walking
			this.getEntity().setEnabled(false);
		}

		// backup the current animation, if animated mesh
		if(this.backupAnim == null
				&& mesh instanceof MeshVA)
		{
			MeshVA model = (MeshVA)mesh;
			this.backupAnim = model.getCurrentAnimation();
			model.getCurrentAnimation().stop();

			// set new animation
			if(this.animName != null)
				model.setAnimation(this.animName);
			model.getCurrentAnimation().start();
		}

		// we have to eliminate the y-coordinate for later checking
		// on equality with the current path point
		Point3D meshPos = new Point3D(mesh.getPosX(), 0f, mesh.getPosZ());

		if(this.pathToWalk.getCurrentPoint() != null)
		{
			mesh.moveTo(
					this.pathToWalk.getCurrentPoint().x,
					this.pathToWalk.getCurrentPoint().z,
					this.speed, 0.05f, elapsedTime);
		}

		if(this.pathToWalk.getPoints().isEmpty() == false)
		{
			if(meshPos.equals(this.pathToWalk.getCurrentPoint(),this.speed*elapsedTime))
			{
				this.pathToWalk.next();
			}
		}

		if(meshPos.equals(this.targX, 0f, this.targZ, this.speed*elapsedTime))
		{
			this.hasArrivedAtTarget = true;

			// remove walk action
			ActionScheduler.getInstance().removeConcurrentAction(this);
		}
	}

	/* (non-Javadoc)
	 * @see de.rico.adventure.actions.AbstractAction#draw(javax.media.opengl.GL)
	 */
	@Override
	public void draw(GL gl)
	{
		// do nothing
	}

	/* (non-Javadoc)
	 * @see de.rico.adventure.actions.AbstractAction#forceFinish()
	 */
	@Override
	public void finishUp()
	{
		if(this.wasEnabled == true)
		{
			// re-enable entity for clicks
			this.getEntity().setEnabled(true);
		}

		// reset to original animation
		if(this.backupAnim != null
				&& this.getEntity().getMesh() instanceof MeshVA)
		{
			MeshVA model = (MeshVA)this.getEntity().getMesh();

			// stop and reset animation to initial status
			model.getCurrentAnimation().stop();
			model.getCurrentAnimation().reset();

			// set old animation
			model.setAnimation(this.backupAnim.getName());
			model.getCurrentAnimation().start();
		}
	}


}

