//Copyright (C) Ben Parbury - All Rights Reserved. Unauthorized copying of this file, via any medium is strictly prohibited. Written by Ben Parbury <ben@gridlockinteractive.com> 2014

function PushTracker(player)
{
	this.block = undefined;
	this.dx = undefined;
	this.dy = undefined;
	
	this.SetPush = function(block, x, y, tx, ty)
	{
		gUndoSystem.AddRecord(new UndoPushTracker(player, this.block, this.dx, this.dy));
		this.block = block;
		this.dx = tx - x;
		this.dy = ty - y;
	}
	
	this.IsNewPush = function(block, x, y, tx, ty)
	{
		if(this.block == block && block.switchPower == false)
		{
			var dx = tx - x;
			var dy = ty - y;
			if(this.dx == dx && this.dy == dy)
			{
				return false;
			}
		}
		
		return true;
	}
	
	this.Undo = function(block, dx, dy)
	{
		this.block = block;
		this.dx = dx;
		this.dy = dy;
	}
}

function EyeController()
{
	this.lerpPupilPos = new LerpPos(0.3);
	
	this.startIdles = 200;
	this.endIdles = this.startIdles + 90;
	this.idleUpdates = 0;
	this.idleX = 0;
	this.idleY = 0;
	this.idles = new Array();
	this.eyeLidIdles = new Array();
	this.regularIdle = true;
	
	this.topEyeLid = new LerpPoint(0.2);
	this.bottomEyeLid = new LerpPoint(0.2);
	
	this.teleportingFraction = 0;
	
	this.Load = function(player)
	{
		this.Resize(player);
	}
	
	this.Resize = function(player)
	{
		this.eyeRadius = kTileSize * 0.24;
		this.pupilRadius = kTileSize * 0.1;
		this.eyeMovement = this.eyeRadius - (kTileSize * 0.16);
		this.eyeLidMax = (this.eyeRadius * 1.5);
		this.eyeLidIdle = (this.eyeRadius * 1.1);
		this.eyeLidMin = 0;
		
		this.lerpPupilPos.SetPos(player.tx * this.eyeMovement, player.ty * this.eyeMovement);
		var eyeLidTarget = this.GetEyeLidTarget(player);
		this.topEyeLid.SetPos(eyeLidTarget);
		this.bottomEyeLid.SetPos(eyeLidTarget);
	}
	
	function StaticIdle(x, y)
	{
		this.x = x;
		this.y = y;
		
		this.GetX = function(counter)
		{
			return this.x;
		}
		
		this.GetY = function(counter)
		{
			return this.y;
		}
	}
	
	function SinCosIdle(speed, xscale, yscale)
	{
		this.speed = speed;
		this.xscale = xscale;
		this.yscale = yscale;
		
		this.GetX = function(counter)
		{
			return Math.sin(counter * this.speed) * this.xscale;
		}
		
		this.GetY = function(counter)
		{
			return Math.cos(counter * this.speed) * this.yscale;
		}
	}
	
	
	function FlickIdle(frequency)
	{
		this.frequency = frequency;
		
		this.GetX = function(counter)
		{
			return Math.round(counter / this.frequency) % 2 == 0 ? 1 : -1;
		}
		
		this.GetY = function(counter)
		{
			return Math.round(counter / this.frequency) % 2 == 0 ? 1 : -1;
		}
	}
	
	this.idles.push(
		new StaticIdle(-1, -1), new StaticIdle(0, 0), new StaticIdle(1, 1),
		new SinCosIdle(0.05, 1, 1), new SinCosIdle(0.05, -1, 1), new SinCosIdle(0.1, 1, 1), new SinCosIdle(0.1, -1, 1), new SinCosIdle(0.2, 1, 1), new SinCosIdle(0.2, -1, 1),
		new FlickIdle(12), new FlickIdle(30), new FlickIdle(42)
	);
	
	this.eyeLidIdles.push(
		new SinCosIdle(0.3, 0.8, 0.8), new StaticIdle(0.8, 0.8), new StaticIdle(0.0, 0.0)
	);
	
	this.Teleporting = function(fraction)
	{
		this.teleportingFraction = Math.min(fraction + 0.2, 1);
	}
	
	this.Update = function(player)
	{
		if(player.tx == 0 && player.ty == 0 && !gUndoSystem.IsUndoing())
		{
			this.idleUpdates++;
		}
		else
		{
			this.idleUpdates = 0;
		}
		
		if(this.idleUpdates == this.startIdles)
		{
			var regularIdle = (Math.random() >= 0.4);
			this.regularIdle = regularIdle;
			if(regularIdle)
			{
				this.idleX = Math.round(Math.randomRange(0, this.idles.length - 1));
				this.idleY = Math.round(Math.randomRange(0, this.idles.length - 1));
				this.endIdles = Math.randomRange(this.startIdles + 30, this.startIdles + 90);
			}
			else
			{
				this.idleX = Math.round(Math.randomRange(0, this.eyeLidIdles.length - 1));
				this.idleY = Math.round(Math.randomRange(0, this.eyeLidIdles.length - 1));
				this.endIdles = this.startIdles + 15;
			}
		}
		else if(this.idleUpdates > this.endIdles)
		{
			this.idleUpdates = 0;
		}
		else if(this.idleUpdates > this.startIdles)
		{
			var counter = this.startIdles - this.idleUpdates;
			if(this.regularIdle)
			{
				var x = this.idles[this.idleX].GetX(counter);
				var y = this.idles[this.idleY].GetY(counter);
				this.lerpPupilPos.SetTarget(x * this.eyeMovement, y * this.eyeMovement);
			}
			else
			{
				var x = this.eyeLidIdles[this.idleX].GetX(counter);
				var y = this.eyeLidIdles[this.idleY].GetY(counter);
				this.topEyeLid.SetTarget(x * this.eyeLidMax);
				this.bottomEyeLid.SetTarget(y * this.eyeLidMax);
			}
		}
		else
		{
			this.lerpPupilPos.SetTarget(player.tx * this.eyeMovement, player.ty * this.eyeMovement);
			
			var eyeLidTarget = this.GetEyeLidTarget(player);
			this.topEyeLid.SetTarget(eyeLidTarget);
			this.bottomEyeLid.SetTarget(eyeLidTarget);
			this.teleportingFraction = 0;
		}
		
		this.topEyeLid.Update();
		this.bottomEyeLid.Update();
		
		this.lerpPupilPos.Update();
	}
	
	this.GetEyeLidTarget = function(player)
	{
		var loadingScale = gEditor ? 1 : Math.min((gLevelSystem.transitionTimer/gLevelSystem.kTransitionTime) + 0.2, 1);
		var moveScale = player.pushing ? 0.8 : 1;
		return this.eyeLidIdle * (1-this.teleportingFraction) * loadingScale * moveScale
	}
}


function Eye(controller)
{
	this.lerpDrawPos = new LerpPos(0.3);
	this.controller = controller;
	
	this.Load = function(player)
	{
		this.Resize(player)
	}
	
	this.Resize = function(player)
	{
		this.eyePos = new Vec2(0, -kTileSize * 0.16);
		this.lerpDrawPos.SetPos(player.GetXFromPoint(this.eyePos), player.GetYFromPoint(this.eyePos))
	}

	this.Update = function(player)
	{
		this.lerpDrawPos.SetTarget(player.GetXFromPoint(this.eyePos), player.GetYFromPoint(this.eyePos))
		this.lerpDrawPos.Update();
	}
	
	this.Draw = function(player)
	{
		gDraw.player.save();
		var x = (player.x * kTileSize) + kTileSizeD2 + this.lerpDrawPos.x;
		var y = (player.y * kTileSize) + kTileSizeD2 + this.lerpDrawPos.y;

		gDraw.player.beginPath();
		var eyeLidStartX = x - this.controller.eyeRadius;
		var eyeLidEndX = x + this.controller.eyeRadius;
		var eyeLidY = y;

		var cpYTop = this.controller.topEyeLid.point;
		var cpYBottom = this.controller.bottomEyeLid.point;
		gDraw.player.moveTo(eyeLidStartX, y);
		gDraw.player.bezierCurveTo(eyeLidStartX, eyeLidY + cpYBottom, eyeLidEndX, eyeLidY + cpYBottom, eyeLidEndX, eyeLidY);
		gDraw.player.bezierCurveTo(eyeLidEndX, eyeLidY - cpYTop, eyeLidStartX, eyeLidY - cpYTop, eyeLidStartX, eyeLidY);
		gDraw.player.clip();
				
		gDraw.player.beginPath();
		var eyeGrad = gDraw.player.createRadialGradient(x, y, 0, x, y, this.controller.eyeRadius);
		eyeGrad.addColorStop(0, "rgb(255, 255, 255)");
		eyeGrad.addColorStop(0.8, "rgb(220, 220, 220)");
		eyeGrad.addColorStop(1, "rgb(150, 150, 150)");
		gDraw.player.fillStyle = eyeGrad;//"rgb(255, 255, 255)";
		gDraw.player.arc(x, y, this.controller.eyeRadius, 0, Math.PI * 2);
		gDraw.player.fill();

		gDraw.player.beginPath();
		gDraw.player.fillStyle = "rgb(0, 0, 0)";
		var moveScale = player.pushing ? 0.8 : 1;
		gDraw.player.arc(x + this.controller.lerpPupilPos.x, y + this.controller.lerpPupilPos.y, this.controller.pupilRadius * moveScale, 0, Math.PI * 2);
		gDraw.player.fill();

		this.DrawEyeReflections(player, x, y);
		
		gDraw.player.restore();
		
		if(!player.ghost)
		{
			//Coverup the clip pixelation with smooth curve (look bad with alpha)
			gDraw.player.beginPath();
			gDraw.player.moveTo(eyeLidStartX, y);
			gDraw.player.bezierCurveTo(eyeLidStartX, eyeLidY + cpYBottom, eyeLidEndX, eyeLidY + cpYBottom, eyeLidEndX, eyeLidY);
			gDraw.player.bezierCurveTo(eyeLidEndX, eyeLidY - cpYTop, eyeLidStartX, eyeLidY - cpYTop, eyeLidStartX, eyeLidY);
			gDraw.player.lineWidth = kTileSize * 0.03;
			gDraw.player.strokeStyle = player.playerGrad;
			gDraw.player.stroke();
		}
	}
	
	this.DrawEyeReflections = function(player, x, y)
	{
		var eyePX = x + this.controller.lerpPupilPos.x;
		var eyePY = y + this.controller.lerpPupilPos.y;
		var lightCount = gStyles.lightSources.length;
		var reflectionDistance = this.controller.pupilRadius;
		var reflectionRadius = 1;
		for(var i = 0; i < lightCount; i++)
		{
			var lightSource = gStyles.lightSources[i];
			var lightPX = lightSource.GetPixelX();
			var lightPY = lightSource.GetPixelY();
			var lightRange = lightSource.GetPixelRadius();
			var dist = distance(lightPX, lightPY, eyePX, eyePY);
			var maxBrightness = 20;
			if(dist < lightRange)
			{
				var vX = lightPX - eyePX;
				var vY = lightPY - eyePY;
				var angle = Math.atan2(-vY, vX) + (Math.PI * 0.5)
				var fractionOfRange = dist/lightRange;
				gDraw.player.beginPath();
				var finalX = eyePX + (Math.sin(angle) * reflectionDistance * fractionOfRange);
				var finalY = eyePY + (Math.cos(angle) * reflectionDistance * fractionOfRange);
				gDraw.player.arc(finalX, finalY, reflectionRadius, 0, Math.PI * 2);
				var brightness = lightSource.GetBrightness(player.x, player.y);
				
				brightness = Math.clamp(brightness, 0, maxBrightness);
				var alpha = (brightness/maxBrightness)
				gDraw.player.fillStyle = "rgba(255, 255, 255, " + alpha + ")";
				gDraw.player.fill();
				
			}
		}
	}
}

function Mouth(player)
{
	this.lerpDrawPos = new LerpPos(0.3);
	this.mouthWidth = new LerpPoint(0.2);
	this.mouthDown = new LerpPoint(0.2);
	this.mouthUp = new LerpPoint(0.2);
	this.mouthLeftCorner = new LerpPos(0.2);
	this.mouthRightCorner = new LerpPos(0.2);

	this.Load = function(player)
	{
		this.Resize(player);
	}
	
	this.Resize = function(player)
	{
		this.mouthPos = new Vec2(0, kTileSize * 0.22);
		this.lerpDrawPos.SetPos(player.GetXFromPoint(this.mouthPos), player.GetYFromPoint(this.mouthPos))
		
		var data = this.GetMouthPositions(player);
		this.mouthWidth.SetPos(data.mouthWidth);
		this.mouthDown.SetPos(data.mouthDown);
		this.mouthUp.SetPos(data.mouthUp);
		this.mouthLeftCorner.SetPos(data.mouthLeftCorner.x, data.mouthLeftCorner.y);
		this.mouthRightCorner.SetPos(data.mouthRightCorner.x, data.mouthRightCorner.y);
	}
	
	this.Update = function(player)
	{
		var data = this.GetMouthPositions(player);
		this.mouthWidth.SetTarget(data.mouthWidth);
		this.mouthDown.SetTarget(data.mouthDown);
		this.mouthUp.SetTarget(data.mouthUp);
		this.mouthLeftCorner.SetTarget(data.mouthLeftCorner.x, data.mouthLeftCorner.y);
		this.mouthRightCorner.SetTarget(data.mouthRightCorner.x, data.mouthRightCorner.y);
		
		this.mouthWidth.Update();
		this.mouthDown.Update();
		this.mouthUp.Update();
		this.mouthLeftCorner.Update();
		this.mouthRightCorner.Update();
		
		this.lerpDrawPos.SetTarget(player.GetXFromPoint(this.mouthPos), player.GetYFromPoint(this.mouthPos))
		this.lerpDrawPos.Update();
	}
	
	this.GetMouthPositions = function()
	{
		var data = {}
		if(gRulesSystem.IsComplete())
		{
			data.mouthWidth = (kTileSize * 0.4);
			data.mouthDown = (kTileSize * 0.16);
			data.mouthUp = (-kTileSize * 0.02);
			data.mouthLeftCorner = new Vec2(-kTileSize * 0.08, kTileSize * 0.08);
			data.mouthRightCorner = new Vec2(kTileSize * 0.08, kTileSize * 0.08);
		}
		else if(player.pushing || player.tx + player.ty != 0 && player.tween == null)
		{
			data.mouthWidth = (kTileSize * 0.28);
			data.mouthDown = (0);
			data.mouthUp = (0);
			data.mouthLeftCorner = new Vec2(kTileSize * 0.06, 0);
			data.mouthRightCorner = new Vec2(-kTileSize * 0.06, 0);
		}
		else
		{
			data.mouthWidth = (kTileSize * 0.32);
			data.mouthDown = (kTileSize * 0.04);
			data.mouthUp = (0);
			data.mouthLeftCorner = new Vec2(0, kTileSize * 0.04);
			data.mouthRightCorner = new Vec2(0, kTileSize * 0.04);
		}
		return data;
	}
	
	this.Draw = function(player)
	{
		gDraw.player.save();
		var x = (player.x * kTileSize) + kTileSizeD2 + this.lerpDrawPos.x;
		var y = (player.y * kTileSize) + kTileSizeD2 + this.lerpDrawPos.y;

		gDraw.player.beginPath();
		
		var leftCorner = x - this.mouthWidth.point/2;
		var rightCorner = x + (this.mouthWidth.point/2);
		gDraw.player.moveTo(rightCorner + this.mouthRightCorner.x, y - this.mouthRightCorner.y);
		gDraw.player.bezierCurveTo(rightCorner, y - this.mouthUp.point, leftCorner, y - this.mouthUp.point, leftCorner + this.mouthLeftCorner.x, y - this.mouthLeftCorner.y);
		gDraw.player.bezierCurveTo(leftCorner, y + this.mouthDown.point, rightCorner, y + this.mouthDown.point, rightCorner + this.mouthRightCorner.x, y - this.mouthRightCorner.y);
		
		gDraw.player.fillStyle = "rgb(0, 0, 0)";;
		gDraw.player.strokeStyle = "rgb(0, 0, 0)";
		gDraw.player.fill();
		gDraw.player.stroke();
		
		gDraw.player.restore();
	}
}

function PlayerKeyState(key, altKey, tx, ty)
{
	this.key = key;
	this.altKey = altKey;
	this.downTimeMs = 0;
	this.tx = tx;
	this.ty = ty;
}

function Player(ghost)
{
	this.ghost = ghost;
	
	//current position
	this.x = 0;
	this.y = 0;
	
	//Position last drawn
	this.px = 0;
	this.py = 0;
	
	//target position
	this.tx = 0;
	this.ty = 0;
	
	this.tween = null;
	
	this.pushTracker = new PushTracker(this)
	
	this.eyeController = new EyeController();
	this.eye = new Eye(this.eyeController);
	this.mouth = new Mouth(this);
	
	this.pushing = false;
	
	this.moveSounds = false;
	this.moveLoopSound = gSounds.Get("playerMoveLoop");
	this.moveStartSound = gSounds.Get("playerMoveStart");
	this.moveStopSound = gSounds.Get("playerMoveStop");
	
	this.keyStates = new Array();
	this.keyStates.push(new PlayerKeyState(EKeyCode.left, EKeyCode.a, -1, 0));
	this.keyStates.push(new PlayerKeyState(EKeyCode.right, EKeyCode.d, 1, 0));
	this.keyStates.push(new PlayerKeyState(EKeyCode.up, EKeyCode.w, 0, -1));
	this.keyStates.push(new PlayerKeyState(EKeyCode.down, EKeyCode.s, 0, 1));
	
	this.lightedTiles = new Array();
	
	//Powers
	this.strength = true;
	
	this.IsPlayerAt = function(x, y)
	{
		var playerX = Math.round(this.x);
		var playerY = Math.round(this.y);
		return (playerX == x && playerY == y) || (this.tween && this.tween.tx == x && this.tween.ty == y);
	}

	this.Load = function(node)
	{
		var x = Number(node.getAttribute("x"));
		var y = Number(node.getAttribute("y"));
		this.LoadToPosition(x, y);
		this.strength = node.hasAttribute("strength") ? (node.getAttribute("strength") == 1) : true;
		this.mouth.Load(this);
		this.eyeController.Load(this);
		this.eye.Load(this);
	}

	this.Write = function()
	{
		var playerX = Math.round(this.x);
		var playerY = Math.round(this.y);
		var powers = "";
		powers += (this.strength) ? 'strength="1" ' : '';
		return '\t\t<Player x="' + playerX + '" y="' + playerY + '" ' + powers + '/>\r';
	}
	
	this.LoadToPosition = function(x, y)
	{
		this.tween = null;
		this.SetPosition(x, y);
	}
	
	this.SetPosition = function(x, y)
	{
		gConsole.Assert(gTileSystem.GetTileAt(Math.round(x), Math.round(y)) != null);
		this.x = x;
		this.y = y;
	}
	
	this.GetX = function()
	{
		return Math.round(this.x);
	}

	this.GetY = function()
	{
		return Math.round(this.y);
	}
	
	this.GetPixelX = function()
	{
		return (this.x * kTileSize) + kTileSizeD2;
	}

	this.GetPixelY = function()
	{
		return (this.y * kTileSize) + kTileSizeD2;
	}
	
	this.IsMoving = function()
	{
		return this.tween != null;
	}
	
	this.TweenComplete = function()
	{
		this.tween = null;
		this.x = Math.round(this.x);
		this.y = Math.round(this.y);
	}
	
	this.Update = function()
	{
		this.UpdateMovement();
		
		this.lightedTiles = [];
		gTileSystem.GetAdjacent(this.lightedTiles, this.GetX(), this.GetY());
		
		if(!this.ghost)
		{
			var tile = gTileSystem.GetTileAt(this.GetX(), this.GetY());
			if(tile && tile.PlayerActivate)
			{
				tile.PlayerActivate(this);
			}
		}
		
		this.eyeController.Update(this);
		this.eye.Update(this);
		this.mouth.Update(this);

		var lerpPointsLength = this.lerpPoints.length;
		for(var i = 0; i < lerpPointsLength; i++)
		{
			var tx = this.GetXFromPoint(this.drawPoints[i]);
			var ty = this.GetYFromPoint(this.drawPoints[i]);
			this.lerpPoints[i].SetSpeed(this.pushing ? 0.4 : 0.2);
			this.lerpPoints[i].SetTarget(tx, ty);
			this.lerpPoints[i].Update();
		}
	}
	
	this.UpdateMovementInput = function()
	{
		if(!gUndoSystem.IsUndoing())
		{
			var recentKeyState = this.GetMostRecentKeyState();
			if(recentKeyState)
			{
				this.tx = recentKeyState.tx;
				this.ty = recentKeyState.ty;
			}
			else
			{
				this.tx = 0;
				this.ty = 0;
			}
		}
		else
		{
			this.tx = 0;
			this.ty = 0;
		}
	}
	
	this.UpdateMovement = function()
	{
		this.UpdateMovementInput();
		
		if(this.tween)
		{
			if(this.tween.CanReverse() && (this.tx == -this.tween.dx && this.ty == -this.tween.dy))
			{
				if(!this.ghost)
				{
					gBlockSystem.Reverse(this);
				}
				this.tween.Reverse();
				this.pushing = false;
			}
			this.tween.Update();
		}
		else
		{
			this.pushing = false;
		}
		
		if(!this.tween)
		{
			if(this.tx != 0 || this.ty != 0)
			{
				var targetX = this.x + this.tx;
				var targetY = this.y + this.ty;
				var tile = gTileSystem.GetTileAt(targetX, targetY);
				if(tile && tile.CanMovePlayerTo())
				{
					var block = gBlockSystem.GetBlockAt(targetX, targetY);
					if(block && !this.ghost)
					{
						if(block.type == EBlockType.Sliding && block.switchPower == false && block.dx == this.tx && block.dy == this.ty)
						{
							gUndoSystem.AddRecord(new UndoPlayerMovement(this, this.x, this.y));
							this.TweenPlayer(targetX, targetY, false);
						}
						else if(block.CanMove(this, this.tx, this.ty))
						{
							gUndoSystem.AddRecord(new UndoPlayerPush(this, this.x, this.y, block));
							this.TweenPlayer(targetX, targetY, !block.switchPower);
							block.Move(this, this.tx, this.ty);
							if(this.pushTracker.IsNewPush(block, this.x, this.y, targetX, targetY))
							{
								this.pushTracker.SetPush(block, this.x, this.y, targetX, targetY);
								gStatsSystem.UpdateStat(EStatsType.Pushes, 1);
							}
						}
						else if(block.dx == this.tx && block.dy == this.ty)
						{
							gUndoSystem.AddRecord(new UndoPlayerMovement(this, this.x, this.y));
							this.TweenPlayer(targetX, targetY, false);
						}
					}
					else
					{
						gUndoSystem.AddRecord(new UndoPlayerMovement(this, this.x, this.y));
						this.TweenPlayer(targetX, targetY, false);
					}
				}
			}
		}
		
		if(this.tween == null && this.moveSounds)
		{
			if(this.moveLoopSound.IsPlaying())
			{
				this.moveLoopSound.Stop();
				this.moveStopSound.Play();
			}
		}
	}
	
	this.GetMostRecentKeyState = function()
	{
		var recent = null;
		var downTimeMs = 0;
		var count = this.keyStates.length;
		for(var i = 0; i < count; i++)
		{
			var state = this.keyStates[i];
			if(state.downTimeMs > downTimeMs)
			{
				recent = state;
				downTimeMs = state.downTimeMs;
			}
		}
		
		return recent;
	}
	
	this.TweenPlayer = function(targetX, targetY, pushing)
	{
		this.pushing = pushing;
		this.tween = new Tween(this, this.x, this.y, targetX, targetY, gTweenSteps);
		if(!this.moveLoopSound.IsPlaying() && this.moveSounds)
		{
			this.moveStartSound.Play();
			this.moveLoopSound.Play();
		}
	}
	
	this.DrawClear = function()
	{
		var padding = kTileSize * 0.1;
		gDraw.player.clearRect(((this.x - 1) * kTileSize) - padding, ((this.y - 1) * kTileSize) - padding, (kTileSize * 3) + (padding * 2), (kTileSize * 3) + (padding * 2));
		
		var lightedLength = this.lightedTiles.length;
		for(var i = 0; i < lightedLength; i++)
		{
			this.lightedTiles[i].DrawClear();
		}
	}
	
	this.InitDraw = function()
	{
		var drawPointCount = 32;
		this.drawPoints = new Array();
		this.lerpPoints = new Array();
		var radius = kTileSizeD2 - 1;
		for(var i = 0; i < drawPointCount; i++)
		{
			var angle = Math.PI * 2 * (i/drawPointCount);
			var nextPoint = new Vec2(Math.sin(angle) * radius, Math.cos(angle) * radius);
			this.drawPoints.push(nextPoint);
			var lerpPos = new LerpPos(0.1);
			lerpPos.SetPos(nextPoint.x, nextPoint.y);
			this.lerpPoints.push(lerpPos);
			
		}
	}
	this.InitDraw();
	
	this.Draw = function()
	{
		if(this.ghost)
		{
			gDraw.player.save();
			gDraw.player.globalAlpha = 0.2;
		}
		
		var lightedLength = this.lightedTiles.length;
		for(var i = 0; i < lightedLength; i++)
		{
			this.lightedTiles[i].Draw();
			if (this.lightedTiles[i].DrawOverlay)
			{
				this.lightedTiles[i].DrawOverlay();
			}
		}
		
		var x = (this.x * kTileSize);
		var y = (this.y * kTileSize);
		this.px = x;
		this.py = y;
		x += kTileSizeD2;
		y += kTileSizeD2;
		gDraw.player.strokeStyle = gStyles.GetPlayerFill(this);
		this.playerGrad = gDraw.player.createRadialGradient(x + this.eye.lerpDrawPos.x, y + (this.eye.lerpDrawPos.y * 1.5), 0, x, y, kTileSize/2);
		
		this.playerGrad.addColorStop(0, gStyles.playerFill.GetAdjusted(-100).GetColor());
		this.playerGrad.addColorStop(0.7, gStyles.playerFill.GetColor());
		this.playerGrad.addColorStop(1, gStyles.playerFill.GetAdjusted(-64).GetColor());

		gDraw.player.fillStyle = this.playerGrad;
		
		gDraw.player.lineWidth = 2;
		gDraw.player.lineCap = "round";
		gDraw.player.lineJoin = "round";
		gDraw.player.beginPath();
		var lerpPointsLength = this.lerpPoints.length;
		gDraw.context.moveTo(x + this.lerpPoints[0].x, y + this.lerpPoints[0].y);
		for(var i = 0; i < lerpPointsLength; i++)
		{
			gDraw.player.lineTo(x + this.lerpPoints[i].x, y + this.lerpPoints[i].y);
		}
		gDraw.player.closePath();
		//gDraw.player.stroke();
		gDraw.player.fill();
		
		this.eye.Draw(this);
		this.mouth.Draw(this);

		if(this.ghost)
		{
			gDraw.player.restore();
		}
	}
	
	this.Resize = function()
	{
		this.InitDraw();
		this.eyeController.Resize(this);
		this.eye.Resize(this);
		this.mouth.Resize(this);
	}
	
	this.GetXFromPoint = function(point)
	{
		return this.GetFromPoint(point.x, this.tween && this.tween.dx, this.tx, this.pushing);
	}

	this.GetYFromPoint = function(point)
	{
		return this.GetFromPoint(point.y, this.tween && this.tween.dy, this.ty, this.pushing);
	}
	
	this.GetFromPoint = function(point, tween, input, pushing)
	{
		var amount = pushing ? kTileSize * 0.16 : kTileSize * 0.1;
		var stepsize = (kTileSize/gTweenSteps)
		var offset = tween ? point + (tween * amount) : point + (input * stepsize);
		if(!tween || (tween && pushing))
		{
			var radius = tween ? kTileSizeD2 + (Math.abs(tween) * stepsize) : kTileSizeD2 - 1;
			offset = Math.clamp(offset, -radius, radius);
		}
		return offset;
	}
	
	this.GetCurrentTimeMs = function()
	{
		return (new Date()).getTime();
	}
	
	this.KeyDown = function(key)
	{
		if(this.ghost)
		{
			key -= gGhostKeyOffset;
		}
		this.SetKeyState(key, this.GetCurrentTimeMs());
	}
	
	this.KeyUp = function(key)
	{
		if(this.ghost)
		{
			key -= gGhostKeyOffset;
		}
		this.SetKeyState(key, 0);
	}
	
	this.SetKeyState = function(key, downTimeMs)
	{
		var count = this.keyStates.length;
		for(var i = 0; i < count; i++)
		{
			var state = this.keyStates[i];
			if(key == state.key || key == state.altKey)
			{
				state.downTimeMs = downTimeMs;
			}
		}
	}
	
	this.Debug = function()
	{
		gDraw.WatchAt(this.x * kTileSize, this.y * kTileSize, "Player (" + this.GetX() + ", " + this.GetY() +") " + (this.pushing ? "Pushing" : ""));
	}
}