package search;

import flash.geom.Point;
import flixel.FlxSprite;

class Boat
{
	var sprite:FlxSprite;
	var area:Area;
	var curWaypointIndex:Int;
	var coords:Point;
	var waypoints:Array<Point>;
	var speedVarianceRate:Float;
	var speedVarianceOffset:Float;
	var heading(default,set):Point;
	var speed:Float;
	var loop:Bool;
	var targetCheckCountdown:Float;
	var targets:Array<Target>;
	var sightRadius:Float;
	var foundTarget:Bool;

	static var frameHeadings:Array<Point> = null;

	public var whenFoundTargetAndGetShouldLoiter:Target->Bool;

	public function new(
		area_:Area, imageFilename:String, speed_:Float,
		sightRadius_:Float, targets_:Array<Target>
	) {
		area = area_;
		speed = speed_;
		sightRadius = sightRadius_;
		targets = targets_;

		if (frameHeadings == null)
		{
			frameHeadings = [
				new Point(1,0),
				new Point(0,-1),
				new Point(-1,0),
				new Point(0,1)
			];
		}

		sprite = new FlxSprite(-100,-100);
		sprite.loadGraphic("assets/images/" + imageFilename, true);
		area.group.add(sprite);

		curWaypointIndex = -1;

		speedVarianceRate = Util.lerp(0.8, 1.2, Math.random());
		speedVarianceOffset = Util.lerp(0, 2*Math.PI, Math.random());

		heading = new Point(0,1);
		coords = new Point(-10,-10);
		targetCheckCountdown = 0;
	}

	public function destroy()
	{
		sprite.destroy();
	}

	public function update(clock:Clock)
	{
		if (curWaypointIndex >= 0 && curWaypointIndex < waypoints.length)
		{
			// move towards waypoint
			var wp = waypoints[curWaypointIndex];
			var prevCoords = coords.clone();

			var sv = (speed*0.25) * Math.cos(speedVarianceOffset + clock.time*speedVarianceRate);
			coords.x += heading.x * (speed + sv);
			coords.y += heading.y * (speed + sv);

			var dot = Util.dot(Util.sub(prevCoords, wp), Util.sub(coords, wp));
			if (dot <= 0)
			{
				// just passed waypoint
				coords = wp.clone();
				curWaypointIndex++;

				if (curWaypointIndex >= waypoints.length && loop)
				{
					curWaypointIndex = 0;
				}
				if (curWaypointIndex < waypoints.length)
				{
					heading = Util.normalize(Util.sub(waypoints[curWaypointIndex], coords));
				}
			}
		}

		if (!foundTarget)
		{
			targetCheckCountdown -= clock.dt;
			//if (targetCheckCountdown < 0)
			{
				// break off and hang around target if found
				targetCheckCountdown = Util.lerp(1, 3, Math.random());
				for (target in targets)
				{
					var targetCoords = target.getCoords(area.clock);
					if (Util.dist(targetCoords, coords) > sightRadius) continue;

					// target spotted
					var shouldLoiter = true;
					if (whenFoundTargetAndGetShouldLoiter != null)
					{
						// just notify and carry on
						shouldLoiter = whenFoundTargetAndGetShouldLoiter(target);
					}

					if (shouldLoiter)
					{
						// loiter here
						var numLoiterPoints = 5;
						var loiterPoints = new Array<Point>();
						var loiterStartAngle = Math.PI * 2 * Math.random();
						loiterPoints.push(coords.clone());
						for (i in 0...numLoiterPoints)
						{
							var loiterAngle = loiterStartAngle * 2 * Math.PI * i / numLoiterPoints;
							var loiterDist = Util.lerp(sightRadius*0.25, sightRadius*0.5, Math.random());
							loiterPoints.push(new Point(
								targetCoords.x + loiterDist * Math.cos(loiterAngle),
								targetCoords.y + loiterDist * Math.sin(loiterAngle)
							));
						}
						setWaypoints(loiterPoints, true);
						foundTarget = true;
						break;
					}
				}

			}
		}

		var spritePos = area.coordsToScreen(coords);
		sprite.setPosition(spritePos.x - sprite.width/2, spritePos.y - sprite.height/2);
	}

	public function setWaypoints(waypoints_:Array<Point>, loop_:Bool=false)
	{
		waypoints = waypoints_;
		loop = loop_;
		coords = waypoints[0].clone();
		curWaypointIndex = 1;
		heading = Util.normalize(Util.sub(waypoints[1], waypoints[0]));
	}

	public function getHeading():Point
	{
		return heading;
	}

	function set_heading(h:Point):Point
	{
		heading = h;
		var bestIndex = 0;
		var bestDot = Util.dot(heading, frameHeadings[0]);
		for (i in 1...frameHeadings.length)
		{
			var dot = Util.dot(heading, frameHeadings[i]);
			if (Math.abs(1-dot) < Math.abs(1-bestDot))
			{
				bestDot = dot;
				bestIndex = i;
			}
		}
		sprite.frame = sprite.framesData.frames[bestIndex];
		return heading;
	}
}
