package gui.simulator;

import gui.simulator.facilities.SimulatorStage;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.Serializable;
import java.util.List;


public class Visitor implements Serializable {
	private Point2D positie, target;
	private double rotation, speed;
	private WalkingPath currentpath;
	private int currentpoint;
	private boolean walkInversed;
	private double food, drink, pis;
	Images.ImageType image = Images.ImageType.Visitor;
	
	public Visitor(Point2D positie, Point2D starttarget) {
		this.positie = positie;
		this.rotation = 0;
		double number = Math.random()*2;
		if(number < 1){
			this.speed = 1;
		} else {
			this.speed = number;
		}
		this.target = starttarget;
		this.food = (int)(Math.random()*100);
		this.drink = (int)(Math.random()*100);
		this.pis = (int)(Math.random()*100);
	}

	void update(List<Visitor> visitors, List<DrawEngine> buildings, WalkingPathArrayList walkingpaths)
	{
		Point2D difference = new Point2D.Double(
				target.getX() - positie.getX(),
				target.getY() - positie.getY()
				);
		rotation %= 360;
		double newRotation = Math.atan2(difference.getY(), difference.getX());
		double rotDifference = rotation - newRotation;
		while(rotDifference > Math.PI)
			rotDifference -= 2 * Math.PI;
		while(rotDifference < -Math.PI)
			rotDifference += 2 * Math.PI;
		
		if(Math.abs(rotDifference) < 0.1)
			rotation = newRotation;
		else if(rotDifference < 0)
			rotation += 0.1;
		else if(rotDifference > 0)
			rotation -= 0.1;
		
		Point2D oldPositie = positie;
		
		positie = new Point2D.Double(
				positie.getX() + speed * Math.cos(rotation),
				positie.getY() + speed * Math.sin(rotation)
				);
		
		this.food = (int)(Math.random()*100);
		this.drink = (int)(Math.random()*100);
		this.pis = (int)(Math.random()*100);
		
		if(hasCollision(visitors) || hasCollisionObject(buildings)) // collision with a ridged building or a visitor, rotate
		{
			positie = oldPositie;
			rotation += 0.5*Math.random();
		}
		if(currentpath != null){
			DrawEngine object = collisionObject(buildings);
			if(object != null){
				if((currentpath.getObject1() == object && walkInversed) || (currentpath.getObject2() == object && !walkInversed)){ //end point reached, what to do?
					if(object.type == SimulatorPane.Objects.STAGE){
						if(((SimulatorStage) object).getPlayingact() == null){
							walkNewRoute(walkingpaths, object);
						}
					}else if(object.type == SimulatorPane.Objects.DRINK){
						drink += 50;
						walkNewRoute(walkingpaths, object);
					}else if(object.type == SimulatorPane.Objects.SNACKBAR){
						food += 50;
						walkNewRoute(walkingpaths, object);
					}else if(object.type == SimulatorPane.Objects.RESTROOM){
						pis = 100;
						walkNewRoute(walkingpaths, object);
					}else if(object.type == SimulatorPane.Objects.WAYPOINT){
						walkNewRoute(walkingpaths, object);
					}
				}
			}
			if(positie.distance(currentpath.get(currentpoint)) < 11){ //collision with a path-point
				if(walkInversed && currentpoint > 0){
					currentpoint--;
				}else if(currentpoint < currentpath.getPath().size()-1){
					currentpoint++;
				}
				target = currentpath.get(currentpoint);
			}
		}else{ //entrance
			DrawEngine object = collisionObject(buildings);
			if(object != null){
				walkNewRoute(walkingpaths,object);
			}
		}
	}
	
	public void paint(Graphics2D g)
	{
		AffineTransform tx = new AffineTransform();
		tx.translate(positie.getX()-8, positie.getY()-11);
		tx.rotate(rotation, 4, 6);
		g.drawImage(Images.getImage(image), tx ,null);
	}
	private boolean hasCollision(List<Visitor> visitors) { // check collision with other visitors
		for(Visitor b : visitors)
		{

			if(b != this && b.positie.distance(positie) < 11){
				double rotdifference = Math.toDegrees(Math.abs(rotation-b.rotation));
				if(!(rotdifference > 160 && rotdifference < 210)){
					return true;
				}
			}
			
		}
		return false;
	}

	private boolean hasCollisionObject(List<DrawEngine> objects){ // check collision with rigid objects
		for(DrawEngine o:objects){
			if(o.type != SimulatorPane.Objects.WAYPOINT && o.type != SimulatorPane.Objects.EXIT && o.type != SimulatorPane.Objects.ENTRANCE )
				if (o.contains(positie))
					return true;
		}
		return false;
	}
	private DrawEngine collisionObject(List<DrawEngine> objects){
		for(DrawEngine o:objects){
			if (o.containsWalkArea(positie) || o.contains(positie) )
				return o;
		}
		return null;
	}
	private void walkRoute(WalkingPath p, DrawEngine object){
		if(p.getObject1() == object){
			currentpoint = 1;
			walkInversed = false;
		}else{
			currentpoint = p.getPath().size()-2;
			walkInversed= true;
		}
		target = p.get(currentpoint);
		currentpath = p;
	}
	private void walkNewRoute(WalkingPathArrayList walkingpaths, DrawEngine object){
		WalkingPathArrayList connectedWalkingPaths = walkingpaths.getWalkingPadObject(object);
		connectedWalkingPaths.removeType(object, SimulatorPane.Objects.ENTRANCE);
		if(food > 25)
			connectedWalkingPaths.removeType(object, SimulatorPane.Objects.SNACKBAR);
		if(drink > 25)
			connectedWalkingPaths.removeType(object, SimulatorPane.Objects.DRINK);
		if(pis > 25)
			connectedWalkingPaths.removeType(object, SimulatorPane.Objects.RESTROOM);
		if(!connectedWalkingPaths.isEmpty()){
			walkRoute(connectedWalkingPaths.get((int)(Math.random()*connectedWalkingPaths.size())), object);
		}
	}

	public double getFood() {
		return food;
	}

	public void setFood(double food) {
		if(food >= 0 && food <= 100)
			this.food = food;
	}

	public double getDrink() {
		return drink;
	}

	public void setDrink(double drink) {
		if(drink >= 0 && drink <= 100)
			this.drink = drink;
	}

	public double getPis() {
		return pis;
	}

	public void setPis(double pis) {
		if(pis >= 0 && pis <= 100)
			this.pis = pis;
	}
	
}