package ei.game.scene.units;


import java.util.LinkedList;

import ei.engine.effects.Particles;
import ei.engine.math.Vector2f;
import ei.engine.math.Vector2i;
import ei.engine.math.Vector3f;
import ei.engine.scene.Node;
import ei.game.algo.AStar;
import ei.game.algo.AStarNode;
import ei.game.gamestate.InGameState;
import ei.game.player.Player;
import ei.game.scene.GameEntity;
import ei.game.scene.Map;
import ei.game.scene.weapons.Weapon;
import ei.game.scene.weapons.WeaponHandler;

/**
 * The Unit class, handles the units in the game.
 * @author Jesper Lundin
 * @author Ziver koc
 *
 */
public abstract class Unit extends GameEntity{
	// The texture
	private Node unitNode;
	// Som temp pos for moving the unit
	private Vector2i oldPos;
	private Vector2i oldVect;
	// Order variables
	private Vector2f moveTo;
	private GameEntity attack;
	// The wepon reload timer
	private int weponTimer;
	// The path to travel
	private LinkedList<AStarNode> path;

	/**
	 * Creates a empty unit
	 * 
	 * @param l The max life of the unit
	 */
	public Unit(int l, Vector2i pos, Player p) {
		super(l, p);
		unitNode = new Node("unit");
		unitNode.setLocation(Map.getPixelByPos(pos.getX(), pos.getY()));
		setPos(pos.getX(), pos.getY());
	}
	
	public void setSelected(boolean b) {
		if(b) {
			unitNode.add(getSelection().getSelectNode());
		}
		else{
			unitNode.remove(getSelection().getSelectNode());
		}
	}
	
	public void setMouseOver(boolean b) {
		if(b) {
			unitNode.add(getSelection().getMouseOverNode());
		}
		else{
			unitNode.remove(getSelection().getMouseOverNode());
		}
	}
	
	/**
	 * Returns the sprite for the unit
	 * 
	 * @return The sprite for the unit
	 */
	public Node getNode(){
		return unitNode;
	}
	
	/**
	 * Changes the pos of the unit in the map
	 * 
	 * @param x The x pos to move to
	 * @param y The y pos to move to
	 */
	public void setPos(int x, int y) {
		if(oldPos!=null) {
			InGameState.getMap().removePos(oldPos.getX(), oldPos.getY());
		}
		oldPos = new Vector2i(x, y);
		InGameState.getMap().setPos(this, x, y);
		System.out.println(oldPos);
	}
	
	
	/**
	 * Moving a unit to the given pos
	 * 
	 * @param x The x pos to move to
	 * @param y The y pos to move to
	 */
	public void move(int x, int y){
		move(x,y,true);
	}
	
	/**
	 * Moving a unit to the given pos
	 * 
	 * @param x The x pos to move to
	 * @param y The y pos to move to
	 * @param b True to clear old orders else false
	 */
	private void move(int x, int y, boolean b) {
		if(b){
			attack = null;
		}
		path = (LinkedList<AStarNode>) new AStar().startSearch(oldPos,new Vector2i(x,y));

		if(path != null && !path.isEmpty() && moveTo == null){
			AStarNode temp = path.poll();
			oldVect = new Vector2i((int)unitNode.getLocation().getX(), (int)unitNode.getLocation().getY());
			moveTo = Map.getPixelByPos(temp.getX(), temp.getY());
			setPos(temp.getX(), temp.getY());
		}
	}
	
	/**
	 * Lets a unit attack another unit or object in the world;
	 */
	public void attack(Vector2i target) {
		attack = InGameState.getMap().getPos(target.getX(),target.getY());
	}
	
	public abstract Weapon getWeapon(Vector2f startPos);
	
	public abstract float getVelocity();
	
	/**
	 * Updating the unit
	 */
	public void update() {
		
		if(getLife()<=0) {
			unitNode.remove("Tank");
			getPlayer().removeUnit(this);
			InGameState.getMap().removePos(oldPos.getX(), oldPos.getY());
		}
		else if(moveTo != null) {
			Vector2i moveVect = new Vector2i((int)moveTo.getX(),(int)moveTo.getY());
			
			int moveXminus = moveVect.getX()-oldVect.getX();
			int moveYminus = moveVect.getY()-oldVect.getY();

			float divideY = (moveVect.getY()+2)/(oldVect.getY()+2);
			float divideX = (moveVect.getX()+2)/(oldVect.getX()+2);

			//The rotation animation is done here.
			if(moveVect.getX() < oldVect.getX() && divideY==1) {
				getSprite().setRotation(new Vector3f(0, 0, 90));
			}

			if(moveVect.getX() > oldVect.getX() && divideY==1) {
				getSprite().setRotation(new Vector3f(0, 0, -90));
			}
			
			if(moveVect.getY() < oldVect.getY() && divideX==1) {
				getSprite().setRotation(new Vector3f(0, 0, 180));
			}
			
			if(moveVect.getY() > oldVect.getY() && divideX==1) {
				getSprite().setRotation(new Vector3f(0, 0, 0));
			}
				

			//Diagonally.
			if(moveVect.getX() > oldVect.getX() && moveVect.getY() > oldVect.getY()
					&& moveXminus>=2 && moveYminus>=2) {
				unitNode.get("Tank").setRotation(new Vector3f(0, 0, -45));
			}

			if(moveVect.getX() < oldVect.getX() && moveVect.getY() < oldVect.getY()
					&& moveXminus<=-2 && moveYminus<=-2) {
				unitNode.get("Tank").setRotation(new Vector3f(0, 0, 135));
			}

			if(moveVect.getX() > oldVect.getX() && moveVect.getY() < oldVect.getY()
					&& moveXminus>=2 && moveYminus<=-2) {
				unitNode.get("Tank").setRotation(new Vector3f(0, 0, -135));
			}

			if(moveVect.getX() < oldVect.getX() && moveVect.getY() > oldVect.getY()
					&& moveXminus<=-2 && moveYminus>=2) {
				unitNode.get("Tank").setRotation(new Vector3f(0, 0, 45));
			}

			//System.out.println(unitNode.get("Tank").getRotation());


			//The moving is done here.
			if(moveTo.getX() > unitNode.getLocation().getX()) {
				unitNode.getLocation().add(getVelocity(), 0f, 0f);
			}
			if(moveTo.getX() < unitNode.getLocation().getX()) {
				unitNode.getLocation().add(-getVelocity(), 0f, 0f);
			}
			if(moveTo.getY() > unitNode.getLocation().getY()) {
				unitNode.getLocation().add(0f, getVelocity(), 0f);
			}
			if(moveTo.getY() < unitNode.getLocation().getY()) {
				unitNode.getLocation().add(0f, -getVelocity(), 0f);
			}
			
			if(attack != null && getWeapon(new Vector2f(unitNode.getLocation().getX(), unitNode.getLocation().getY()))
					.onRange(new Vector2f(attack.getNode().getLocation().getX(), attack.getNode().getLocation().getY()))){
				moveTo = null;
			}
			else if(Math.abs(moveTo.getX() - unitNode.getLocation().getX()) < getVelocity()+1
					&& Math.abs(moveTo.getY() - unitNode.getLocation().getY())< getVelocity()+1 ){
				if(path != null && !path.isEmpty()){
					AStarNode temp = path.poll();
					if(InGameState.getMap().isPosEmpty(temp.getX(), temp.getY())){
						oldVect = new Vector2i((int)moveTo.getX(), (int)moveTo.getY());
						moveTo = Map.getPixelByPos(temp.getX(), temp.getY());
						setPos(temp.getX(), temp.getY());
					}
					else if(!path.isEmpty()){
						move(path.getLast().getX(),	path.getLast().getY());
					}
				}
				else{
					moveTo = null;
				}
			}
		}
		else if(attack != null){
			Weapon wepon = getWeapon(new Vector2f(unitNode.getLocation().getX(), unitNode.getLocation().getY()));
			Vector2i enamyPos = InGameState.getMap().getPosIndex(attack);
			if(attack.getLife() <= 0 || enamyPos == null){
				attack = null;
			}
			else{
				if(wepon.onRange(new Vector2f(attack.getNode().getLocation().getX(), attack.getNode().getLocation().getY()))){
					if(weponTimer >= wepon.getReload()){
						wepon.launch(enamyPos);
						WeaponHandler.getInstance().addWeapon(wepon);
						weponTimer = 0;
					}
					else{
						weponTimer++;
					}
				}
				else{
					move(enamyPos.getX(),enamyPos.getY(), false);
				}
			}
		}
	}
}
