Cantris = new Class({ 
	Implements: [Options],
	options: {
		width: 14,
		height:21,
		blockwidth:20,
		blockheight:20,
		spacer:1,
		pointMultiplier: .1
	},
	initialize: function(options){
		this.setOptions(options);
		this.buttons = true;
		window.addEvent('domready', function() {
			ctx.strokeRect(-1,-1,this.options.width*20+2,this.options.height*20+1);
			ctx.strokeRect(340,40,81,80);
		}.bind(this));
		this.line = [[0,1,2,3],[0,4,8,12],[0,1,2,3],[0,4,8,12],"#800000"];
		this.box =[[0,1,4,5],[0,1,4,5],[0,1,4,5],[0,1,4,5],"#0066CC"]
		this.lAngle = [[0,4,8,9],[4,5,6,2],[1,5,9,0],[0,1,2,4],"#990066"];//L shaped
		this.oAngle = [[1,5,9,8],[4,5,6,10],[1,5,9,2],[4,5,6,0],"#CCCCCC"];//oppsite
		this.cross = [[1,4,5,6],[9,1,5,6],[9,4,5,6],[9,4,5,1],"#FF9900"];
		this.shapes = [this.line,this.box,this.lAngle,this.oAngle,this.cross];
		
	},
	clearVars: function(){
		this.baseX = 0;//where the block is located on the map
		this.baseY = 0;
		this.prevShape = 0;//the previous shape/location saved for that block so it can be referenced to clear the block when moving
		this.currRotation = 0;//what is the rotation number currently in place
		this.tdx = 0;//absolute pixel location on map
		this.tdy = 0;
		this.toDraw = new Array();//array holding all the pixels of one block
		this.closeMe = false;//if we want to save this block in its current location set closeMe to true then next iteration will start
	},
	//clears pixel based on absolute map position
	clearPix: function(td,ty){
		ctx.clearRect(this.options.blockwidth*td+1,this.options.blockheight*ty,(this.options.blockheight-this.options.spacer),(this.options.blockheight-this.options.spacer));
	},
	//draw pixel based on absolute map position
	drawPix: function(td,ty){
		ctx.fillRect(this.options.blockwidth*td+1,this.options.blockheight*ty,(this.options.blockheight-this.options.spacer),(this.options.blockheight-this.options.spacer));

	},
	//this is used to start moving a new block down the map
	startLoop: function(){
		if(this.buttons == true && Button.buttons[0].text == 'Start Game'){
			
			Button.buttons[0].text = '   Restart ';
			Button.drawButton(0,Button.buttons[0].buttonStyle);
		}
		//if we have a block in the preview box use it otherwise find a random one
		if(this.currshape != -1)
			this.currshape = this.nextshape 
		else 
			this.currshape = Math.floor(Math.random()*5);
		this.nextshape = Math.floor(Math.random()*5);//get the next block randomly
		this.drawQueue();//draw the nexd block in the preview box
		this.clearVars();//clear out all the base variables so they can be used for the new block
		this.baseX = 7;//set where the x position of the block goes when being created
		if(this.looping)$clear(this.looping);//kill the loop process from the old block so they dont pile up
		this.looping = this.loopProc.periodical(1000,this);//start a new loop process
	},
	//draws block one position lower
	loopProc: function(){
		this.baseY++;
		this.drawShape(this.currshape,this.currRotation,this.baseX,this.baseY);
	},
	//check if block is hitting any sides or a pixel of another block(in this case save block and start a new one)
	checkSides: function(){
		retVal = true;
		//right wall
		if(this.tdx >=this.options.width){
			this.baseX -= 1;            
			retVal = false;
		//left wall
		}else if(this.tdx < 0){
			this.baseX += 1;
			retVal = false;
		//bottom
		}else if(this.tdy >=this.options.height){
			this.baseY -= 1;
			this.closeMe = true;
			retVal = false;
		}
		//check if hitting a block
		if(this.tetrisMap[this.tdx + (this.tdy+1)*this.options.width] ==1 && this.prevShape[3] != this.baseY){
			this.closeMe = true;
			this.baseY +=1;
		}
		
		return retVal;
	},
	//get the exact position of the pixel
	pixPos: function(pixel){
		switch(pixel){
			case 0: case 1: case 2: case 3:
				 todrawX = pixel;
				 todrawY = 0;
				break;
			case 4: case 5: case 6: case 7:
				todrawY = 1;
				todrawX = pixel -(4*todrawY);
				break;
			case 8: case 9: case 10: case 11:
				todrawY = 2;
				todrawX = pixel -(4*todrawY);
				break;
			case 12: case 13: case 14: case 15:
				todrawY = 3;
				todrawX = pixel  - (4*todrawY);
				break;
		}
		return [todrawX,todrawY];
	},
	//clear shape based on absolute map position
	clearShape: function(s,r,x,y){
		shape = this.shapes[s];
		for(i=0;i<shape.length;i++){
			toDrawL = this.pixPos(shape[r][i]);
			this.tdx = x + toDrawL[0];
			this.tdy = y + toDrawL[1];
			this.tetrisMap[this.tdx + this.tdy*this.options.width] = 0;
			this.clearPix(this.tdx,this.tdy);
		}
	},
	//draw the queue in the side box
	drawQueue: function(){
		this.clearShape(this.currshape,0,17,3);
		shape = this.shapes[this.nextshape];
		for(i=0;i<shape.length;i++){
			toDrawL = this.pixPos(shape[0][i]);
			this.tdx = 17 + toDrawL[0];
			this.tdy = 3 + toDrawL[1];
			this.drawPix(this.tdx,this.tdy);
		}
	},
	//draw the block on the map
	drawShape: function(s,r,x,y){
		shape = this.shapes[s];
		//if the block was already drawn remove old drawing
		if(this.prevShape != 0)
			this.clearShape(this.prevShape[0],this.prevShape[1],this.prevShape[2],this.prevShape[3]);
		for(i=0;i<shape.length;i++){
			//get pixel location within shape
			toDrawL = this.pixPos(shape[r][i]);
			//add both block location and pixel location to get pixels actual location
			this.tdx = x + toDrawL[0];
			this.tdy = y + toDrawL[1];
			//check if we are trying to rotate/move into blocks that are already in place (horizontal collision detection)
			if(this.prevShape !=0 && ((this.tetrisMap[this.tdx + this.tdy*this.options.width] == 1 && (this.prevShape[2]>x || this.prevShape[2]<x))//if we're hitting another block
				 || (r != this.prevShape[1] && this.tetrisMap[this.tdx + this.tdy*this.options.width] == 1))){//if the rotation results in overlap
				//this.drawMap();
				this.toDraw = new Array();
				s = this.prevShape[0];
				this.currRotation = this.prevShape[1];
				this.baseX = this.prevShape[2];
				this.baseY = this.prevShape[3]
				this.prevShape = 0;
				this.drawShape(s,this.currRotation,this.baseX,this.baseY);
				return null;
			}
			toRun = this.checkSides();
			//if theres no problems add pixel location to the to build section
			if(toRun== true){
				this.toDraw[this.toDraw.length] = [this.tdx,this.tdy];
			}else{//otherwise in the case of trying to go past the border redraw block in the correct location
				this.toDraw = new Array();
				this.drawShape(s,r,this.baseX,this.baseY);
				return null;
			}
		}
		//draw block if there were no problems
		for(i=0;i<shape.length;i++){
			this.tetrisMap[this.toDraw[i][0] + this.toDraw[i][1]*this.options.width] = 1;
			this.drawPix(this.toDraw[i][0],this.toDraw[i][1]);
		}
		//reset draw directions
		this.toDraw = new Array();
		//save shape position to remove shape if redrawn
		this.prevShape = [s,r,x,y];
		//if we need a new block save this one and start a new one also check for game conditions
		if(this.closeMe == true){
			this.closeMe = false;
			this.startLoop();
			this.checkLine();
		}
		
	},
	//check if getting points or game over
	checkLine: function(){
		//check if blocks reach the top
		for(i=0;i<this.options.width*2;i++){
			if(this.tetrisMap[i] != 0){
				$clear(this.looping);
				alert('Game Over');
				this.gameOver=true;
				this.resetGame();
			}
		}
		currLine = false;
		//check if there were any complete blocks
		for(i = 0;i< this.options.height;i++){
			for(k=0;k<this.options.width;k++){
				if(this.tetrisMap[k+(i*this.options.width)] == 0)
					break;
				if(k == this.options.width-1){
					this.clearLine(i);
					return null;
				}
			}
			
		}
	},
	//dev function to draw out map values in visible array
	drawMap: function(){
		
		$('tm').innerHTML = '';
		for(i = 0;i< this.options.height;i++){
			for(j =0;j<this.options.width;j++){
				
				$('tm').innerHTML += this.tetrisMap[j+ (i*this.options.width)];
					
			}
			$('tm').innerHTML += '<br/>';
		}
	},
	//clear the line then move all other blocks down
	clearLine: function(line){
		for(i = line;i>=0;i--){
			for(j =0;j<this.options.width;j++){
				this.clearPix(j,i);
				if(this.tetrisMap[j+ ((i-1)*this.options.width)] != undefined)
					this.tetrisMap[j+ i*this.options.width] = this.tetrisMap[j+ ((i-1)*this.options.width)];  
				if(this.tetrisMap[j+ i*this.options.width] != 0){
					this.drawPix(j,i);
				}
			}
		}
		//add to the score then reset the on screen value
		
		this.totalLines += 1; 
		if(this.totalLines%10==0)this.totalLevel += 1;
		this.totalPoints += (this.options.pointMultiplier*this.totalLevel) * 10;
		this.showScore(390,150);
		//check if there are any other lines that need to be reset
		this.checkLine();
	},
	//clear old score and show current one.
	showScore: function(x,y){
		ctx.clearRect(x,y,160,96);
		zeros = 4 - this.totalPoints.toString().length;
		totext = this.totalPoints.toString();
		for(i=0;i<zeros;i++){
			totext = '0' + totext; 
		}
		ctx.drawText(totext,x,y);
		
		zeros = 4 - this.totalLines.toString().length;
		totext = this.totalLines.toString();
		for(i=0;i<zeros;i++){
			totext = '0' + totext; 
		}
		ctx.drawText(totext,x,y+32);
		
		zeros = 4 - this.totalLevel.toString().length;
		totext = this.totalLevel.toString();
		for(i=0;i<zeros;i++){
			totext = '0' + totext; 
		}
		ctx.drawText(totext,x,y+64);
	},
	//clear everything so game can be reset
	resetGame: function(){
		if(this.button == true){
			Button.buttons[0].text = 'Start Game';
			Button.drawButton(0,Button.buttons[0].buttonStyle);
		}
		this.clearVars();
		this.pause = false;
		this.totalPoints = 0;
		this.totalLines = 0;
		this.totalLevel = 1;
		this.currshape=-1;
		this.gameOver = true;
		this.tetrisMap =new Array(this.options.width* this.options.height);
		for(i=0;i<this.tetrisMap.length;i++){
			this.tetrisMap[i] = 0;
		}
		ctx.clearRect(0,0,this.options.width*20,this.options.height*20);
		ctx.clearRect(341,41,79,78);
		this.showScore(390,150);
	}
});