//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
var gBlockSystem = new BlockSystem();

EBlockType =
{
	Normal	: "normal",
	Sliding	: "sliding",
	Static	: "static",
}

function BlockSystem()
{
	this.blocks = undefined;
	this.menuBlocks = new Array();
	this.deletingId = undefined;
	this.kDeletingIdMax = 32;
	this.scoreTable = undefined;
	this.score = undefined;
	this.transitionDistance = 0;
	this.haveSlidingBlocks = false;
	this.haveConnectedBlocks = false;
	
	this.Init = function()
	{
		gBlockRenderer.Init();
		this.Clear();
	}

	this.AddBlock = function(type, subType, rotation, value, x, y, switchPower, connected, fading)
	{
		var block = this.CreateBlock(type, subType, rotation, value, x, y, switchPower, connected, fading)
		this.InsertBlock(block);
	}
	
	this.CreateBlock = function(type, subType, rotation, value, x, y, switchPower, connected, fading)
	{
		switch(type)
		{
			case EBlockType.Normal:
				var block = new Block();
				break;

			case EBlockType.Sliding:
				var block = new SlidingBlock();
				this.haveSlidingBlocks = true;
				break;

			case EBlockType.Static:
				var block = new StaticBlock();
				break;
			
			default:
				gConsole.AssertNow("Unknown block type");
				break;
		}
		
		this.haveConnectedBlocks = this.haveConnectedBlocks || connected;

		block.Init(value, subType, rotation, x, y, switchPower, connected, fading);
		
		return block
	}
	
	this.InsertBlock = function(block)
	{
		gConsole.Assert(gTileSystem.GetTileAt(block.x, block.y) != null);

		this.blocks.push(block);
		
		block.Check();
	}
	
	this.UndoInsertBlock = function(block)
	{
		this.blocks.push(block);
	}
	
	this.RemoveBlock = function(block)
	{
		var index = this.blocks.indexOf(block);
		if(index != -1)
		{
			this.RemoveBlockAtIndex(index);
		}
	}
	
	this.RemoveBlockAtIndex = function(index)
	{
		var block = this.blocks[index];
		block.ScoredAndDeleted();
		block.redraw = true;
		block.DrawClear();
		gUndoSystem.AddRecord(new UndoBlockDelete(block));
		this.blocks.splice(index, 1);
	}
	
	this.Clear = function()
	{
		this.deletingId = 0;
		this.blocks = new Array();
		this.scoreTable = new Array();
		this.score = 0;
		this.haveSlidingBlocks = false;
		this.haveConnectedBlocks = false;
		gBlockRenderer.preRenders = new Array();
	}
	
	this.Update = function()
	{
		if(this.haveSlidingBlocks)
		{
			this.SortSliding();
		}
		
		for(var i = 0; i < this.blocks.length; i++)
		{
			if(this.blocks[i].Update())
			{
				this.RemoveBlockAtIndex(i);
				i--;
			}
		}
	}
	
	this.Debug = function()
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].Debug()
		}
	}
	
	this.SetFadingTimersForTime = function(time)
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i];
			if(block.startDeletingTime)
			{
				if(time < block.startDeletingTime)
				{
					block.deleting = false;
					block.deleteTime = 0;
					block.deletingId = undefined;
					block.startDeletingTime = undefined;
				}
				else if(block.fading)
				{
					block.deleteTime = (time - block.startDeletingTime);
					block.deleting = true;
				}
				else
				{
					block.deleteTime = gBlockDeleteTime;
					block.deleting = true;
				}
			}
		}

	}

	this.SortSliding = function()
	{
		this.blocks.sort(function(a, b)
		{
			var firstPlayer = gPlayerSystem.players[0];
			return distanceSqr(firstPlayer.x, firstPlayer.y, a.x, a.y) < distanceSqr(firstPlayer.x, firstPlayer.y, b.x, b.y);
		})
	}
	
	this.Redraw = function()
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].redraw = true;
		}
	}
	
	this.RebuildRenderCommands = function()
	{
		gBlockRenderer.preRenders = new Array();
		gBlockRenderer.arcCommands = new Array();
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i];
			block.InitOverlayLocation();
			gBlockRenderer.BuildArcCommands(block);
			gBlockRenderer.BuildPreRender(block);
		}
		for(var i = 0; i < this.menuBlocks.length; i++)
		{
			var block = this.menuBlocks[i];
			block.InitOverlayLocation();
			gBlockRenderer.BuildArcCommands(block);
			gBlockRenderer.BuildPreRender(block);
		}
	}
	
	this.DrawClear = function()
	{
		/* Currently redrawing all the blocks...
		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].DrawClear();
		}
		*/
		gDraw.context.clearRect(0, 0, gDraw.canvas.width, gDraw.canvas.height);

		if(this.haveConnectedBlocks)
		{
			this.DrawClearConnected();
		}
	}
	
	this.Draw = function()
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].Draw();
		}
		
		gDraw.context.save();
		this.PreDrawOverlay();

		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].DrawOverlay();
		}
		gDraw.context.restore();

		if(this.haveConnectedBlocks)
		{
			this.DrawConnected();
		}
	}
	
	this.PreDrawOverlay = function()
	{
		gDraw.context.font	 = GetFont(GetFontScale(32));
		gDraw.context.textAlign	 = 'center';
		gDraw.context.textBaseline	 = 'middle';
	
		gDraw.context.shadowOffsetX = 1;
		gDraw.context.shadowOffsetY = 1;
		gDraw.context.shadowBlur = 2;
	}
	
	this.DrawClearConnected = function()
	{
		gDraw.foreground.clearRect(0, 0, gDraw.canvas.width, gDraw.canvas.height);
	}
	
	this.DrawConnected = function()
	{
		gDraw.foreground.lineWidth = 10;
		gDraw.foreground.lineJoin = "round";
		gDraw.foreground.lineCap = "round";
		
		
		for(var i = 1; i < 10; i++)
		{
			var rgb = gStyles.GetBlockRGB(EBlockType.Normal, i, false);
			gDraw.foreground.strokeStyle =  gStyles.GetColorAlpha(rgb, 0.3);
			
			gDraw.foreground.beginPath();
			for(var j = 0; j < this.blocks.length; j++)
			{
				var block = this.blocks[j];
				if(block.connected && i == block.value)
				{
					var x = (block.x * kTileSize) + (kTileSizeD2) + block.ox;
					var y = (block.y * kTileSize) + (kTileSizeD2) + block.oy;
					gDraw.foreground.lineTo(x, y);
				}
			}
			gDraw.foreground.closePath();
			gDraw.foreground.stroke();
		}
	}
	
	this.StartTransition = function()
	{
		//Relative to tileSystem distance
		this.transitionDistance = gTileSystem.transitionDistance - 2;
	}
	
	this.DrawTransition = function(fraction)
	{
		this.DrawClear();
		
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i];
			var dist = distance(gLevelSystem.transitionX, gLevelSystem.transitionY, block.x, block.y);
			if(dist < this.transitionDistance * fraction)
			{
				block.Draw();
			}
		}
		
		gDraw.context.save();
		this.PreDrawOverlay();

		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i];
			var dist = distance(gLevelSystem.transitionX, gLevelSystem.transitionY, block.x, block.y);
			if(dist < this.transitionDistance * fraction)
			{
				block.DrawOverlay();
			}
		}
		gDraw.context.restore();
	}

	this.GetBlockAtOrMovingTo = function(x, y)
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i]
			if(block.IsBlockAt(x, y) || block.IsBlockMovingTo(x, y))
			{
				return block;
			}
		}
		
		return null;
	}
	
	this.GetBlockAt = function(x, y)
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i]
			if(block.IsBlockAt(x, y))
			{
				return block;
			}
		}
		
		return null;
	}
	
	this.GetBlockOn = function(x, y)
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i]
			if(block.IsBlockOn(x, y))
			{
				return block;
			}
		}
		
		return null;
	}
	
	this.AllocateDeletingId = function()
	{
		var returnId = this.deletingId;

		this.deletingId++;

		if(this.deletingId == this.kDeletingIdMax)
		{
			this.deletingId -= this.kDeletingIdMax;
		}

		this.scoreTable[returnId] = 0;
		return returnId;
	}
	
	this.MarkedAsDeleting = function(block)
	{
		var deletingId = block.deletingId;
		if(deletingId != undefined) //Only have a deletingId if the player deleted it
		{
			
			gUndoSystem.AddRecord(new UndoScoreTable(deletingId, this.scoreTable[deletingId]));
			if(this.scoreTable[deletingId])
			{
				this.scoreTable[deletingId]++;
			}
			else
			{
				this.scoreTable[deletingId] = 1;
			}
			
			if(!gRulesSystem.IsComplete())
			{
				var score = this.CalculateScore(this.scoreTable[deletingId], block.value);
				gConsole.Log(11, "[SCORE] +"  + score);
				gStatsSystem.UpdateStat(EStatsType.Score, score);
				var chain = block.value == 1 ? 0 : score - block.value;
				gStatsSystem.SetStatMax(EStatsType.Chain, chain);
				//gParticleSystem.Spawn("ScoreParticle", {block:block, score:score, chain:chain});
			}
		}
	}
	
	this.LevelComplete = function()
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			var block = this.blocks[i];
			block.deleting = true;
			block.deletingId = gBlockSystem.AllocateDeletingId();
			if(!block.fading)
			{
				block.deleteTime = gBlockDeleteTime;
			}
		}
	}
	
	this.CalculateScore = function(count, value)
	{
		var score = value == 1 ? 1 : Math.max(count, value);
		return score;
	}
	
	this.CountBlocksOfValue = function(value)
	{
		var count = 0;
		
		var blockArray = this.blocks;
		for(var i = 0; i < blockArray.length; i++)
		{
			var block = blockArray[i];
			if(!block.deleting && block.value == value)
			{
				count++;
			}
		}
		
		return count;
	}
	
	this.CountBlocksOfType = function(type)
	{
		var count = 0;
		
		var blockArray = this.blocks;
		for(var i = 0; i < blockArray.length; i++)
		{
			var block = blockArray[i];
			if(!block.deleting && block.type == type)
			{
				count++;
			}
		}
		
		return count;
	}

	
	this.CountBlocksWithDeletingId = function(deletingId)
	{
		var count = 0;
		for(var i = 0; i < this.blocks.length; i++)
		{
			if(this.blocks[i].deletingId == deletingId)
			{
				count++;
			}
		}
		
		return count;
	}
	
	this.GetBlockWithDeletingId = function(deletingId)
	{
		var count = 0;
		for(var i = 0; i < this.blocks.length; i++)
		{
			if(this.blocks[i].deletingId == deletingId)
			{
				return this.blocks[i];
			}
		}
		
		return null;
	}
	
	this.GetNonDeletingBlockCount = function()
	{
		var count = 0;
		var blockArray = this.blocks;
		for(var i = 0; i < blockArray.length; i++)
		{
			if(!blockArray[i].deleting)
			{
				count++;
			}
		}
		
		return count;
	}
	
	this.Reverse = function(moveObject)
	{
		var blocksLength = this.blocks.length;
		var blockArray = this.blocks;
		for(var i = 0; i < blockArray.length; i++)
		{
			var block = blockArray[i];
			if(block.switchPower == true && block.tween && block.moveObject == moveObject)
			{
				gStatsSystem.UpdateStat(EStatsType.Pushes, 1);
				this.Reverse(block);
				block.tween.Reverse();
			}
		}
	}
}