package search;

import flixel.text.FlxText;
import flixel.FlxG;
import flixel.util.FlxColor;
import flash.geom.Matrix;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.geom.Point;
import flixel.util.FlxPoint;
import flixel.util.FlxRect;
import flixel.group.FlxGroup;
import flixel.FlxSprite;

class Area
{
	var searchTools:Array<Tool>;
	var gridSprite:FlxSprite;
	var drawingSearchToolIndex:Int;
	var inverseDrawMatrix:Matrix;
	var debugCanvas:CanvasSprite;
	var endSearchCountdown:Float = 0;
	var revealTargetSprites:Array<FlxSprite> = [];
	var helpText:FlxText;
	var helpTextBackSprite:FlxSprite;
	var havePlayedFailureSound:Bool;

	public var clock(default,null):Clock;
	public var group(default,null):FlxGroup;
	public var targets(default,null):Array<Target>;
	public var drawMatrix(default,null):Matrix;
	public var onePixel(default,null):Float;
	public var dim(default,null):Point;
	public var placer(default,null):Placer;
	public var rect(default,null):FlxRect;
	public var numPeopleSaved(default,null):Int;
	public var numTargetsRecovered(get,never):Int;
	public var config(default,null):Config;
	public var whenDone:Void->Void;

	public function new(rect_:FlxRect, dim_:Point, config_:Config=null)
	{
		rect = rect_;
		dim = dim_;
		config = config_;

		group = new FlxGroup();
		clock = new Clock();
		drawingSearchToolIndex = 0;

		drawMatrix = new Matrix();
		drawMatrix.scale(rect.width / dim.x, rect.height / dim.y);
		inverseDrawMatrix = drawMatrix.clone();
		inverseDrawMatrix.invert();

		onePixel = dim.x / rect.width;

		gridSprite = new FlxSprite(0,0);
		gridSprite.makeGraphic(Std.int(rect.width), Std.int(rect.height), Main.GREEN, true);
		gridSprite.drawFrame(true);
		gridSprite.setPosition(rect.x, rect.y);

		group.add(gridSprite);

		if (config != null)
		{
			var str = (config.numTargets == 1) ? "There is one wreck " : 'There are ${config.numTargets} wrecks ';
			str += "somewhere in this area.\n\n";
			str += "Use your resources to find " + (config.numTargets == 1 ? "it" : "them") + ".\n\n";
			str += "Rescue any survivors using a submersible.\n\n";
			str += "You have " + config.timeLimit + " seconds before all hope is lost.";
			helpText = Util.makeText(str, 400, Main.BLUE, "left");
			helpText.x = Math.floor(rect.x + rect.width/2 - helpText.width/2);
			helpText.y = Math.floor(rect.y + rect.height/2 - helpText.height/2);
			group.add(helpText);
		}
		else
		{
			var sizeText = Util.makeText('${dim.x}x${dim.y} KM SAMPLE', 400, Main.BLUE);
			//var sizeText = Util.makeText("SAMPLE", 400, Main.BLUE);
			sizeText.x = Math.floor(rect.left + rect.width/2 - sizeText.width/2);
			sizeText.y = rect.bottom - sizeText.height - 4;
			group.add(sizeText);

			drawGridDots();
		}

		placer = new Placer(this);

		targets = [];

		if (config != null)
		{
			for (i in 0...config.numTargets)
			{
				addTarget(new Target(this));
			}
		}

		searchTools = new Array<Tool>();
		debugCanvas = makeCanvasSprite();
		debugCanvas.visible = false;
	}

	function drawGridDots()
	{
		for (cy in 0...Std.int(dim.y))
		{
			for (cx in 0...Std.int(dim.x))
			{
				var p = drawMatrix.transformPoint(new Point(cx+0.5, cy+0.5));
				gridSprite.framePixels.setPixel32(Std.int(p.x), Std.int(p.y), Main.BLUE);
			}
		}
	}

	public function destroy()
	{
		placer.destroy();
		for (s in searchTools) s.destroy();
		searchTools = new Array<Tool>();
		group.destroy();
	}

	public function addTarget(target:Target)
	{
		target.isReal = true; // always, since this isn't really used any more
		targets.push(target);

		while (targets.length > revealTargetSprites.length)
		{
			var revealTargetSprite = new FlxSprite(0,0);
			revealTargetSprite.loadGraphic("assets/images/RevealTarget.png");
			revealTargetSprite.visible = false;
			revealTargetSprites.push(revealTargetSprite);
			group.add(revealTargetSprite);
		}
	}

	public function registerSubFinished(foundTarget:Target, foundTargetFirstRecovery:Bool)
	{
		if (config == null) return;

		var numFinishedSubs = 0;
		var numSubsTotal = 0;
		for (tool in searchTools)
		{
			if (Std.is(tool, ToolSub))
			{
				if (cast(tool, ToolSub).done) numFinishedSubs++;
			}
		}

		// @@ shameful hack
		for (toolConfig in PrepareState.selectedToolConfigs)
		{
			if (toolConfig.id == "sub") numSubsTotal = toolConfig.count;
		}

		if (endSearchCountdown == 0 && (numFinishedSubs == numSubsTotal || numTargetsRecovered == targets.length))
		{
			// all subs are done or all targets are recovered end the search
			endSearchCountdown = 4;
		}

		if (foundTarget != null && foundTargetFirstRecovery)
		{
			// save some people
			numPeopleSaved += Math.floor(Util.lerpScale(clock.time, config.timeLimit*0.5, config.timeLimit, config.numPeoplePerTarget, 0));
		}
	}

	function get_numTargetsRecovered():Int
	{
		var n = 0;
		for (target in targets)
		{
			if (target.recovered) n++;
		}
		return n;
	}

	public function demoTool(toolSpec:ToolSpec)
	{
		for (s in searchTools) s.destroy();
		searchTools = new Array<Tool>();

		if (toolSpec != null)
		{
			targets = [];
			addTarget(new Target(this, true));

			var c0:Point = null;
			var c1:Point = null;
			switch (toolSpec.placerType)
			{
				case Placer.SearchPlacerType.GLOBAL:
				case Placer.SearchPlacerType.CIRCLE:
					c0 = new Point(dim.x*0.5, dim.y*0.5);
				case Placer.SearchPlacerType.LINE:
					c0 = new Point(dim.x*0.5, dim.y*0.5);
					c1 = new Point(dim.x*0.5+1, dim.y*0.5);
				case Placer.SearchPlacerType.SEGMENT:
					c0 = new Point(dim.x*0.1, dim.y*0.5);
					c1 = new Point(dim.x*0.1+1, dim.y*0.5);
				case Placer.SearchPlacerType.RAY:
					c0 = new Point(dim.x*0.5, dim.y*0.5);
					c1 = new Point(dim.x*0.5+1, dim.y*0.5);
			}

			addTool(toolSpec, c0, c1);
		}
	}

	public function addTool(toolSpec:ToolSpec, c0:Point, c1:Point)
	{
		var fixedCoords = Placer.fixCoords(this, toolSpec.placerType, c0, c1);
		var searchTool = Type.createInstance(Type.resolveClass(toolSpec.className), [this, fixedCoords.c0, fixedCoords.c1]);
		Util.playSound(toolSpec.dropSoundFilename);
		searchTools.push(searchTool);
	}

	private function updateRevealTargets(forDemo:Bool)
	{
		for (i in 0...targets.length)
		{
			var target = targets[i];
			var sprite = revealTargetSprites[i];
			if (target.recovered) continue;
			var screenPos = coordsToScreen(target.getCoords(clock));
			sprite.setPosition(Math.floor(screenPos.x-sprite.width/2), Math.floor(screenPos.y-sprite.width/2));

			if (forDemo)
			{
				if (searchTools.length == 0 || Type.getClassName(Type.getClass(searchTools[0])) == "search.ToolSub") sprite.visible = false;
				else sprite.visible = clock.getFlashing(1, 0.75);
			}
			else
			{
				sprite.visible = clock.getFlashing(5);
			}
		}
	}

	public function update()
	{
		if (helpText == null)
		{
			// don't update clock while reading helpText
			clock.update();
		}

		for (searchTool in searchTools)
		{
			searchTool.update();
		}

		if (searchTools.length > 0)
		{
			if (++drawingSearchToolIndex >= searchTools.length)
				drawingSearchToolIndex = 0;
			searchTools[drawingSearchToolIndex].draw();

			if (helpText != null)
			{
				// first tool was just added. remove help text and draw grid dots
				group.remove(helpText);
				helpText.destroy();
				helpText = null;
				drawGridDots();
			}
		}

		if (config != null && endSearchCountdown == 0 && clock.time > config.timeLimit)
		{
			// time's up
			endSearchCountdown = 3;
		}

//		if (FlxG.keys.pressed.T)
//		{
//			debugCanvas.visible = true;
//			var graphics = debugCanvas.beginSolidDraw(FlxColor.RED);
//			for (target in targets)
//			{
//				var p = target.getCoords(clock);
//				graphics.drawRect(p.x-onePixel, p.y-onePixel, 2*onePixel, 2*onePixel);
//			}
//			debugCanvas.endDraw();
//		}
//		else
//		{
//			debugCanvas.visible = false;
//		}

		placer.update();
		placer.draw();

		// remove non-existing group members
		for (i in 0...group.members.length)
		{
			if (group.members[i] != null && !group.members[i].exists) group.members[i] = null;
		}

		if (endSearchCountdown > 0)
		{
			if (endSearchCountdown < 3)
			{
				// reveal targets
				updateRevealTargets(false);
				var failed = false;
				for (i in 0...targets.length)
				{
					var target = targets[i];
					if (!target.recovered) failed = true;
				}
				if (failed && !havePlayedFailureSound)
				{
					havePlayedFailureSound = true;
					Util.playSound("Failed.mp3");
				}
			}

			endSearchCountdown -= clock.dt;
			if (endSearchCountdown <= 0 && whenDone != null)
			{
				endSearchCountdown = 10000;
				whenDone();
			}
		}
		else if (config == null)
		{
			// always show targets in demo mode
			updateRevealTargets(true);
		}
	}

	public function makeCanvasSprite():CanvasSprite
	{
		var s = new CanvasSprite(drawMatrix);
		s.makeGraphic(Std.int(rect.width), Std.int(rect.height), FlxColor.TRANSPARENT, true);
		s.drawFrame(true);
		s.x = rect.x;
		s.y = rect.y;
		group.add(s);
		return s;
	}

	public function coordsToScreen(coords:Point):FlxPoint
	{
		var p = drawMatrix.transformPoint(coords);
		return new FlxPoint(rect.x + p.x, rect.y + p.y);
	}

	public function screenToCoords(screen:FlxPoint):Point
	{
		var p = inverseDrawMatrix.transformPoint(new Point(screen.x-rect.x, screen.y-rect.y));
		return p;
	}
}
