/*
	Skeemo - Logic/Ruleset (Constructor)
	Attached to gameLogic of the Engine constructor
	The meat and potatoes of Skeemo
*/
var Skeemo = function(){
	this.board = new Object;
	this.messageLog = new Array;
};

/*
	PRIMARY FUNCTIONS
	These typically are used to call secondary functions
*/
// Interactions from UI (such as click), called from Report
Skeemo.prototype.Interact = function(rObj)
{
	this.determineClick(rObj.location);
};

// Determines which control to execute, called from Report
Skeemo.prototype.Control = function(rObj)
{
	console.log('controllinger');
};

// This starts/resumes a new game
Skeemo.prototype.Start = function(resume)
{
	// Should I resme?
	if(resume==true) console.log('don\'t know how to resume yet');
	// Or start a new game?
	else
	{
		// Basic Variables
		this.board.numCols = 7;
		this.board.numRows = 7;
		this.board.grid = new Array;
		
		// Populate an empty board
		for(var x=0; x<this.board.numRows; x++)
		{
			this.board.grid[x] = new Array;
			for(var y=0;y<this.board.numCols; y++)
			{
				this.board.grid[x][y] = new Array;
				this.board.grid[x][y]['color'] = null;
				this.board.grid[x][y]['available'] = false;
			}
		}
		
		this.minCombo = 3;
		this.difficulty = 0;
		this.rowCount = 0;
		this.colorOptions = 5;
		this.score = 0;
		this.multiplier = 0;
		this.nextPieces = new Object;
		this.select =
		{
			mode: false,
			loc: new Array
		};
		this.uidCounter = 0;
		this.toScore = 0;
		this.foundLine=false;
		this.rowQueue = new Array;
		
		// Create update object for UI
		var uObj = new Array;
		
		uObj[0] =
		{
			uType: 'board',
			uid:
			{
				type:'initboard',
				count: this.uidCounter
			},
			objects:this.board.grid,
			hud:
			{
				score:this.score
			}
		};
		
		this.uidCounter++;
		
		this.generateNext('color',uObj);
		this.generateNext('place',uObj);
	}
};

Skeemo.prototype.End = function(check)
{
	if(check)
	{
		// Assume the game is over because this funciton was called
		var gameOver = true;
		// Check for empty spaces
		for(var x=0; x<this.board.numRows; x++)
		{
			for(var y=0; y<this.board.numCols; y++)
			{
				// If there is an empty space, the game is NOT over
				if(this.board.grid[x][y]['color']==null) gameOver = false;
			};
		};
		// Call End without the check to end the game is over
		if(gameOver==true) this.End();
	}
	else console.log('game over man, game over!');
};

// SECONDARY FUNCTIONS
Skeemo.prototype.generateNext = function(type,uObj)
{
//	console.log('generating next:'+type);
	// PRIVATE GLOBALS
	// Determines next colors for preview
	var nextColor = function(instance,uObj)
	{
		instance.nextPieces = new Array;
		for(var x=0; x<instance.difficulty+3;x++) instance.nextPieces[x]=Math.floor(Math.random()*instance.colorOptions);
	};
	
	// Randomly places the preview pieces
	var placeNext = function(instance,uObj)
	{
		// To be soon sent to the UI object
		if(!uObj) var uObj = new Array;
				
		uObj[uObj.length] =
		{
			uType: 'piece',
			uid:
			{
				type:'randomPlace',
				count: instance.uidCounter
			}
		};
		
		instance.uidCounter++;
		// Returns available spaces in 1D array
		var getAvailable = function(theBoard)
		{
			var availableSpaces = new Array;
			// Make array of available
			for(var x=0; x<theBoard.numRows; x++)
			{
				for(var y=0; y<theBoard.numCols; y++)
				{
					if(theBoard.grid[x][y]['color']==null)
					{
						var tempLoc = availableSpaces.length;
						availableSpaces[tempLoc] = new Array;
						availableSpaces[tempLoc][0]=x;
						availableSpaces[tempLoc][1]=y;
					};
				};
			};
			return availableSpaces;
		};

		// Setup the pieces to be updated
		uObj[uObj.length-1].objects = new Array;
		for(var x=0; x<instance.nextPieces.length; x++)
		{
			var availableSpaces = getAvailable(instance.board);
			if(availableSpaces.length>0)
			{
				var newSpot = Math.floor(Math.random()*availableSpaces.length);
				newSpot = availableSpaces[newSpot];
				instance.board.grid[newSpot[0]][newSpot[1]]['color'] = instance.nextPieces[x];
				instance.rowQueue[x]=[newSpot[0],newSpot[1]];
				uObj[uObj.length-1].objects[x] =
				{
					utype: 'add',
					loc: newSpot,
					color: instance.nextPieces[x],
					animate: true
				};
			}
			else
			{
				uObj[uObj.length-1].nextColors = instance.nextPieces;
				instance.End();
				return;
			}
		};
		// Generate next colors
		instance.generateNext('color');

		uObj[uObj.length-1].nextColors = instance.nextPieces;
		
	};
	// END OF PRIVATE GLOBALS
	
	// Determine private global to call
	switch(type)
	{
		case 'color':
			nextColor(this,uObj);
			break;
		case 'place':
			placeNext(this,uObj);
			this.checkRows('ran',uObj);
			break;
		default:
			console.log('unknown case: '+type);
	};
};

Skeemo.prototype.determineClick = function(loc)
{
	// PRIVATE GLOBALS
	var uObj = new Array;
	
	var selectPiece = function(loc,instance)
	{
		// To be used to update UI
		uObj[0] =
		{
			uType: 'piece',
			uid:
			{
				type:'select',
				count: instance.uidCounter
			}
		};
		instance.uidCounter++;
		uObj[0].objects = new Array;

		// Determine if there is already a piece selected
		if(instance.select.mode==true)
		{
			instance.checkGrid(false);
			// Queue up the unselected piece
			uObj[0].objects[uObj[0].objects.length] =
			{
				utype: 'unselect',
				loc: instance.select.loc,
				color: instance.select.color
			};

			// Check current and new location
			var currentL = instance.select.loc[0]+''+instance.select.loc[1];
			var newL = loc[0]+''+loc[1];
			// New piece to select
			if(currentL != newL)
			{
				instance.select.loc = loc;
				instance.select.color = instance.board.grid[loc[0]][loc[1]]['color'];
				uObj[0].objects[uObj[0].objects.length] =
				{
					utype: 'select',
					loc: loc,
					color: instance.board.grid[loc[0]][loc[1]]['color']
				};
				instance.checkGrid(loc);
			}
			// Same piece selected, going to unselect mode
			else
			{
				instance.select.mode = false;
				instance.select.loc = new Array;
				instance.select.color = null;
				instance.checkGrid(false);
			}
		}
		// Nothing yet selected, going into select mode
		else
		{
			instance.select.mode=true;
			instance.select.loc = loc;
			instance.select.color = instance.board.grid[loc[0]][loc[1]]['color'];
			uObj[0].objects[uObj[0].objects.length] =
			{
				utype: 'select',
				loc: loc,
				color: instance.board.grid[loc[0]][loc[1]]['color']
			};

			instance.checkGrid(loc);
		}
		
		uObj[0].available = instance.board.grid;
		
		// Update the UI
		UI.Game.Update(uObj,true);
	};
	
	var movePiece = function(loc,instance)
	{
		instance.checkGrid(false);
		// To be used to update UI
		uObj[0] =
		{
			uType: 'piece',
			uid:
			{
				type: 'move',
				count: instance.uidCounter
			}
		};
		instance.uidCounter++;
		uObj[0].objects = new Object;

		// Remove piece from old location
		instance.board.grid[instance.select.loc[0]][instance.select.loc[1]]['color'] = null;
		instance.board.grid[loc[0]][loc[1]]['color'] = instance.select.color;
		
		uObj[0].objects[0] =
		{
			utype: 'move',
			oldLoc: instance.select.loc,
			newLoc: loc,
			animate:false
		};
		
		// Game goes out of selection mode
		instance.select.mode = false;
		instance.select.loc = new Array;
		instance.select.color = null;
		instance.rowQueue[0]=loc;
		
		/*
			AStar Stuff
		*/
		// var test = AStar(instance.board.grid,[instance.select.loc[0],instance.select.loc[1]],[loc[0],loc[1]],'Manhatten');
		// console.log(test.length);
		
	};
	// END OF PRIVATE GLOBALS
	
	// Selects a piece when not in select mode
	if(this.select.mode==false && this.board.grid[loc[0]][loc[1]]['color']!=null) selectPiece(loc,this);
	// Selects a piece and unselects the previously selected piece
	else if(this.select.mode==true && this.board.grid[loc[0]][loc[1]]['color']!=null) selectPiece(loc,this);
	// Moves currently selected piece to new location
	else if(this.select.mode==true && this.board.grid[loc[0]][loc[1]]['color']==null && this.board.grid[loc[0]][loc[1]]['available']==true)
	{
		movePiece(loc,this);
		this.checkRows('move',uObj);
	}
};

Skeemo.prototype.checkGrid = function(loc)
{
	// PRIVATE GLOBALS
	var toCheckTemp = new Array;
	
	var checkInit = function(toCheck,theBoard)
	{
		toCheckTemp = new Array;
		if(toCheck.length>0)
		{
			for(var x=0; x<toCheck.length; x++)
			{
				// Check Top
				checkPosition(
				[
					toCheck[x][0]-1,
					toCheck[x][1]
				],theBoard,toCheckTemp);
				// Check Right
				checkPosition(
				[
					toCheck[x][0],
					toCheck[x][1]+1
				],theBoard,toCheckTemp);
				// Check Bottom
				checkPosition(
				[
					toCheck[x][0]+1,
					toCheck[x][1]
				],theBoard,toCheckTemp);
				// Check Left
				checkPosition(
				[
					toCheck[x][0],
					toCheck[x][1]-1
				],theBoard,toCheckTemp);
			}
			// Recall self to continue checking!
			checkInit(toCheckTemp,theBoard);
		}
	};
	
	// Universal location check
	var checkPosition = function(loc,theBoard,toCheckTemp)
	{
		// Make sure that it is a valid location, that there is no piece there and that it has not previously been updated
		if(loc[0]>=0 && loc[0]<theBoard.numRows && loc[1]>=0 && loc[1]<theBoard.numRows && theBoard.grid[loc[0]][loc[1]]['color']==null && theBoard.grid[loc[0]][loc[1]]['available']==false)
		{
			// Update available grid, and send the location off to have it's neighbors checked
			theBoard.grid[loc[0]][loc[1]]['available']=true;
			var tempIndex = toCheckTemp.length;
			toCheckTemp[tempIndex] = new Array;
			toCheckTemp[tempIndex][0] = loc[0];
			toCheckTemp[tempIndex][1] = loc[1];
		}
	};
	
	var resetAvailable = function(instance)
	{
		for(var x=0;x<instance.board.numRows;x++) for(var y=0;y<instance.board.numCols;y++) instance.board.grid[x][y]['available'] = false;
	};
	// END OF PRIVATE GLOBALS
	
	if(loc!=false)
	{
		toCheckTemp[0]= new Array;
		toCheckTemp[0][0] = parseInt(loc[0],10);
		toCheckTemp[0][1] = parseInt(loc[1],10);
	
		checkInit(toCheckTemp,this.board);
	}
	else resetAvailable(this);
};

Skeemo.prototype.checkRows = function(from,uObj)
{
	// PRIVATE GLOBALS
	// Contains the pieces to be removed
	var lineInfoQueue = new Array;
	
	// Checks for horizontal rows
	var horizontal = function(loc,instance)
	{
		//	Initial Local Variables Needed for The Function
		var discCount = 0;
		var lastDisc = null;
		var lineInfo = new Array;
		var endLine=0;
		//	This loop runs through the entire horizontal row
		for (var x=0; x<instance.board.numCols; x++)
		{
			if (instance.board.grid[loc[0]][x]['color'] == lastDisc && lastDisc != null) discCount++;
			else discCount = 0;
			
			lineInfo[discCount] = [loc[0],x];
			lastDisc = instance.board.grid[loc[0]][x]['color'];
			if (discCount >= instance.minCombo)
			{
				if(x == instance.board.numCols-1) endLine=1;
				if(endLine!=1 && instance.board.grid[loc[0]][x]['color'] != instance.board.grid[loc[0]][x+1]['color']) endLine=1;
			}
			if(discCount >= instance.minCombo && endLine==1)
			{
				instance.multiplier++;
				instance.toScore+=(20+((discCount - instance.minCombo)*5));
				instance.foundLine=true;
				instance.rowCount++;
				lineInfoQueue=lineInfoQueue.concat(lineInfo);
			}
		}
		//	Onto the next row check!
		vertical(loc,instance);
	};

	// Checks for vertical rows
	var vertical = function(loc,instance)
	{
		//Initial Local Variables Needed for The Function
		var discCount = 0;
		var lastDisc = null;
		var lineInfo = new Array;
		var endLine=0;

		//	Detection Function
		for (var x=0; x<instance.board.numRows; x++)
		{
			if (instance.board.grid[x][loc[1]]['color'] == lastDisc && lastDisc != null) discCount++;
			else discCount = 0;
			
			lineInfo[discCount] = [x,loc[1]];
			lastDisc = instance.board.grid[x][loc[1]]['color'];
			
			if (discCount >= instance.minCombo)
			{
				if (x == instance.board.numRows-1) endLine=1;
				if (endLine!=1 && instance.board.grid[x][loc[1]]['color'] != instance.board.grid[x+1][loc[1]]['color']) endLine=1;
			}
			if(discCount >= instance.minCombo && endLine==1)
			{
				instance.multiplier++;
				instance.toScore+=(20+((discCount - instance.minCombo)*5));
				instance.foundLine=true;
				instance.rowCount++;
				lineInfoQueue=lineInfoQueue.concat(lineInfo);
			}
		}
		//	Onto the next row check!
		diagLeft(loc,instance);
	};

	// Checks for left diagonal rows
	var diagLeft = function(loc,instance)
	{
		//	Initial Local Variables Needed for The Function
		var discCount = 0;
		var lastDisc = null;
		var lineInfo = new Array;
		var endLine=0;
		var Height = -1;
		var Width = instance.board.numCols;

		//	Setting Variables for Negative Y Axis
		var Colpos = loc[1] + 1;
		var Rowpos = loc[0] - (2*loc[0]) - 1;
		var startRow;
		var startCol;

		//Increment Sets the offset for the starting position
		var increment = Width - Colpos;
		
		if ((Rowpos+increment)>Height)
		{
			increment = Height - Rowpos;
		}

		// Setting Start Positions
		startRow=(((Rowpos+increment)* -1)-1);
		startCol=((Colpos+increment)-1);

		// Detection Function
		while (startRow < instance.board.numRows && startCol >=0)
		{
			if (instance.board.grid[startRow][startCol]['color'] == lastDisc && lastDisc != null) discCount++;
			else discCount = 0;
			
			lineInfo[discCount] = [startRow,startCol];
			lastDisc = instance.board.grid[startRow][startCol]['color'];
			
			if (discCount >= instance.minCombo)
			{
				if (startCol == 0 || startRow == (instance.board.numRows-1)) endLine=1;
				if (endLine!=1 && instance.board.grid[startRow][startCol]['color'] != instance.board.grid[startRow+1][startCol-1]['color']) endLine=1;
			}
			if(discCount >= instance.minCombo && endLine==1) 
			{
				instance.multiplier++;
				instance.toScore+=(20+((discCount - instance.minCombo)*5));
				instance.foundLine=true;
				instance.rowCount++;
				lineInfoQueue=lineInfoQueue.concat(lineInfo);
			}
			startRow++;
			startCol--;
		}

		// Annnd onwards!
		diagRight(loc,instance);
	};


	// Checks for right diagonal rows
	var diagRight = function(loc,instance)
	{
		// Initial Local Variables Needed for The Function
		var discCount = 0;
		var lastDisc = null;
		var lineInfo = new Array;
		var endLine=0;
		var Height = -1;
		var Width = instance.board.numCols;

		// Setting Variables for Negative Y Axis
		var Colpos = loc[1] + 1;
		var Rowpos = loc[0] - (2*loc[0]) - 1;
		var startRow;
		var startCol;

		//Increment is setting the offset from the original piece for starting point
		var increment = Colpos;
		if (Colpos > Math.abs(Rowpos)) increment = Math.abs(Rowpos);

		// Determined Starting Positions for Check
		startRow=((Rowpos+increment)* -1);
		startCol=(Colpos-increment);

		//Detection Function
		while (startRow < instance.board.numRows && startCol < instance.board.numCols)
		{
			if (instance.board.grid[startRow][startCol]['color'] == lastDisc && lastDisc != null) discCount++;
			else discCount = 0;

			lineInfo[discCount] = [startRow,startCol];
			lastDisc = instance.board.grid[startRow][startCol]['color'];

			if (discCount >= instance.minCombo)
			{
				if (startCol == (instance.board.numCols-1) || startRow == (instance.board.numRows-1)) endLine=1;
				if (endLine!=1 && instance.board.grid[startRow][startCol]['color'] != instance.board.grid[startRow+1][startCol+1]['color']) endLine=1;
			}
			if(discCount >= instance.minCombo && endLine==1)
			{
				instance.multiplier++;
				instance.toScore+=(20+((discCount - instance.minCombo)*5));
				instance.foundLine=true;
				instance.rowCount++;
				lineInfoQueue=lineInfoQueue.concat(lineInfo);
			}
			startRow++;
			startCol++;
		};
	};
	
	// Clears duplicates pieces
	var removeDupes = function(loc,instance)
	{
		for(var x=0; x<lineInfoQueue.length; x++)
		{
			for(var y=0;y<lineInfoQueue.length;y++)
			{
				if(y>x && lineInfoQueue[x][0]==lineInfoQueue[y][0] && lineInfoQueue[x][1]==lineInfoQueue[y][1])
				{
					lineInfoQueue.splice(y,1);
					removeDupes();
					return;
				}
			}
		}
	};
	
	// Removes row, and sends out uObj
	var remove = function(instance,uObj)
	{
		if(instance.foundLine==true)
		{
			instance.foundLine = false;
			// Create object to send to UI
			if(!uObj) var uObj = new Array;
			
			uObj[uObj.length] =
			{
				uType: 'piece',
				uid:
				{
					type:'remove',
					count: this.uidCounter
				}
			};
			
			uObj[uObj.length-1].objects = new Array;
			uObj[uObj.length-1].hud = new Object;
			
			// Update score
			instance.score = instance.score + ((instance.toScore*(instance.difficulty+1))*instance.multiplier);
			instance.toScore = 0;
			
			uObj[uObj.length-1].hud = { score: instance.score };
			
			// Remove Duplicates
			removeDupes();
			
			// Update board and uObj with objects to remove
			for(var x=0;x<lineInfoQueue.length;x++)
			{
				// Remove from engine
				instance.board.grid[lineInfoQueue[x][0]][lineInfoQueue[x][1]]['color'] = null;
				// To Remove from UI
				uObj[uObj.length-1].objects[x] = 
				{
					utype: 'delete',
					loc: lineInfoQueue[x],
					animate: true
				};
			}
			
			instance.levelCheck(uObj);
			return UI.Game.Update(uObj,true);
		}
		else if(from=='move')
		{
			instance.multiplier=0;
			instance.generateNext('place',uObj);
		}
		else if(from=='ran')
		{
			instance.End(true);
			return UI.Game.Update(uObj,true);
		}
		else return UI.Game.Update(uObj,true);
	};
	//END OF PRIVATE GLOBALS
	// And the baby that executes above private functions
	for(var x=this.rowQueue.length-1; x>=0;x--) horizontal(this.rowQueue[x],this);
	// Erase rowQueue
	this.rowQueue = new Array;
	// Finish off with remove
	remove(this,uObj);
};

// Checks if a Level change is in order, and updates the board as necesary
Skeemo.prototype.levelCheck = function(uObj)
{	
	uObj[uObj.length-1].nextColors = this.nextPieces;
	
	if(this.difficulty==0 && this.rowCount>40)
	{
		this.difficulty++;
		this.nextPieces[this.nextPieces.length] = Math.floor(Math.random()*this.colorOptions);
		uObj[uObj.length-1].nextColors = this.nextPieces;
	}
	else if(this.difficulty==1 && this.rowCount>100)
	{
		this.difficulty++;
		this.nextPieces[this.nextPieces.length] = Math.floor(Math.random()*this.colorOptions);
		uObj[uObj.length-1].nextColors = this.nextPieces;
	}
	else if(this.difficulty==2 && this.rowCount>180)
	{
		this.difficulty++;
		this.nextPieces[this.nextPieces.length] = Math.floor(Math.random()*this.colorOptions);
		uObj[uObj.length-1].nextColors = this.nextPieces;
	}
	else if(this.difficulty==3 && this.rowCount>280)
	{
		this.difficulty++;
		this.nextPieces[this.nextPieces.length] = Math.floor(Math.random()*this.colorOptions);
		uObj[uObj.length-1].nextColors = this.nextPieces;
	}	
};