package {
	import flash.events.EventDispatcher;
	//управляет состоянием игры
	internal class Tetris extends EventDispatcher {
		//массив доски
		private var _board:Array;
		private var _width:uint;
		private var _height:uint;
		private var _startx:uint;
		public var _figure:Figure;
		private var _nextFigure:Figure;
		
		public function Tetris(width:uint,height:uint) {
			
			_width = width;
			_height = height;
			_startx = Math.floor( ( _width - 1 ) * 0.5 );
			//заполняем доску пустыми ячейками
			_board = new Array();
			for ( var i:uint = 0; i < _width; ++i )
			{
				_board[i] = new Array();
				for ( var j:uint = 0; j < _height; ++j )
				{
					_board[i][j] = new CellState( Shape.CLEAR, 0, 0 );
				}
			}
			
			//создаем две фигуры
			_figure = new Figure( this );
			_nextFigure = new Figure( this );
			
		}
		
		
		//удаляет выстроенную линию
		private function removeLines():void
		{
			// this bit finds which lines to remove
			// array will hold reference to each completed line
			var linesToRemove:Array = new Array();
			var i:int, j:int, k:int;
			for ( j = _height; j--; )
			{
				// initially pressume to remove line
				var remove:Boolean = true;
				for ( i = _width; i--; )
				{
					if ( isCellClear( new CellPos( i, j ) ) )
					{
						// if we find a clear cell in the line, don't remove it
						remove = false;
						break;
					}
				}
				// if removable, add to array of lines to remove
				if ( remove )
				{
					linesToRemove.push( j );
				}
			}
			
			// this bit removes the lines
			var numlines:uint = linesToRemove.length;
			if ( numlines )
			{
				// array to hold cells which have changed
				var update:Array = new Array();
				// move lines down and add cells to update if necessary
				for ( i = 1; i <= numlines; ++i )
				{
					var max:Number = linesToRemove[ i-1 ] - 1;
					var min:Number = ( i == numlines ? 0 : linesToRemove[i] + 1 );
					for ( j = max; j >= min; --j )
					{
						for ( k = 0; k < _width; ++k )
						{
							// add cells to the update array
							update.push( new Cell( new CellPos( k, j+i ), _board[k][j] ) );
						}
					}
				}
				// add blank lines at top
				for ( j = numlines; j--; )
				{
					for ( k = 0; k < _width; ++k )
					{
						if ( _board[k][j] != Shape.CLEAR )
						{
							update.push( new Cell( new CellPos( k, j ), new CellState( Shape.CLEAR, 0, 0 ) ) );
						}
					}
				}
				// update the cells
				updateCells( update );
				
				// broadcast how many lines have been removed
				var ev:TetrisEvent = new TetrisEvent( TetrisEvent.LINES_REMOVED );
				ev.lines = numlines;
				dispatchEvent( ev );
			}
		}
		
		private function reset():void {
			//очищаем игровое поле 
			for ( var i:Number = 0; i < _width; ++i )
			{
				for ( var j:Number = 0; j < _height; ++j )
				{
					_board[i][j] = new CellState( Shape.CLEAR, 0, 0 );
				}
			}
			
			_figure.reset();
			_nextFigure.reset();
			var ev:TetrisEvent = new TetrisEvent(TetrisEvent.UPDATE_NEXT);
			ev.shape = _nextFigure.shape;
			dispatchEvent(ev);
			
		}
		
		internal function restart():void {
			reset();
			newFigure();
		}
		
		//проверка на пустую ячейку
		internal function isCellClear( pos:CellPos ):Boolean
		{
			var shape:Shape = getCellShape( pos );
			return( shape == Shape.CLEAR || shape == Shape.CURRENT );
		}
		
		//получаем состояние фигуры
		private function getCellShape( pos:CellPos ):Shape
		{
			if ( pos.x < 0 || pos.x >= _width || pos.y < 0 || pos.y >= _height )
			{
				// if the cell is outside of the game board
				return Shape.OUT;
			}
			else
			{
				// the value in the cell
				return _board[pos.x][pos.y].shape;
			}
		}
		
		//двигаем влево
		public function moveLeft():void {
			_figure.moveLeft();
		}
		//двигаем вправо
		public function moveRight():void {
			_figure.moveRight();
		}
		//поворот
		public function rotateLeft():void {
			_figure.rotateLeft();
		}
		public function dropDown():void {
			_figure.drop();
		}
		
		//фигура приземлилась
		internal function figureLanded():void
		{
			
			var temp:Array = _figure.getCells();
			var shape:Shape = _figure.shape;
			for ( var i:uint = temp.length; i--; )
			{
				temp[i].state.shape = shape;
				_board[temp[i].location.x][temp[i].location.y] = temp[i].state;
			}
			// broadcast that the tetrad has landed
			dispatchEvent( new TetrisEvent( TetrisEvent.TETRAD_LANDED ) );
			// удаляем готовую линию
			removeLines();
			newFigure();
		}
		
		
		//шаг игры
		internal function stepGame():void {
			_figure.drop();
			//trace(_figure._rotation);
		}
		
		internal function newFigure():void
		{
			
			_figure.reset( _nextFigure.shape );
			
			_nextFigure.reset();
			var ev:TetrisEvent = new TetrisEvent( TetrisEvent.UPDATE_NEXT );
			ev.shape = _nextFigure.shape;
			dispatchEvent( ev );
			
			moveFigureToStart();
		}
		
		private function moveFigureToStart():void
		{
			if ( !_figure.setLocation( new CellPos( _startx, 0 ) ) )
			{
				
				dispatchEvent( new TetrisEvent( TetrisEvent.GAME_OVER ) );
			}
		}
		
		internal function figureMoved( cells:Array ):void
		{
			
			updateCells( cells );
			
			dispatchEvent( new TetrisEvent( TetrisEvent.TETRAD_MOVED ) );
		}
		
		private function updateCells( cells:Array ):void
		{
			for ( var i:uint = 0; i < cells.length; ++i )
			{
				_board[cells[i].location.x][cells[i].location.y] = cells[i].state;
				
				if (cells[i].state.shape == Shape.CURRENT)
				{
					cells[i].state = new CellState( _figure.shape, cells[i].state.number, cells[i].state.rotation );
				}
			}
			
			var ev:TetrisEvent = new TetrisEvent( TetrisEvent.UPDATE_CELLS );
			ev.cells = cells;
			dispatchEvent( ev );
		}
		
	}
}