    package com.techlabs.puzzle {

        import away3d.containers.ObjectContainer3D;

        import com.techlabs.puzzle.events.PuzzleEvent;

        import flash.display.Bitmap;

        /**
         * ...
         * @author Timo Virtanen
         */
        public class PuzzleBoard extends ObjectContainer3D {
            private var _boardWidth:int;
            private var _boardHeight:int;

            private var _pieceWidth:Number;
            private var _pieceHeight:Number;

            private var _padding:int;
            private var _moving:Boolean;

            private var _pieces:Array = new Array();

            public function PuzzleBoard() {
                _boardWidth = SlidingPuzzle.size + SlidingPuzzle.padding * SlidingPuzzle.subdivisions;
                _boardHeight = SlidingPuzzle.size + SlidingPuzzle.padding * SlidingPuzzle.subdivisions;

                _pieceWidth = SlidingPuzzle.size / SlidingPuzzle.subdivisions;
                _pieceHeight = SlidingPuzzle.size / SlidingPuzzle.subdivisions;

                _padding = SlidingPuzzle.padding;
            }

            public function createPuzzle(images:Array):void {
                clearBoard();

                var pieceCount:int = 0;
                var lastPiece:int = SlidingPuzzle.subdivisions * SlidingPuzzle.subdivisions;

                for(var yp:int = 0; yp < SlidingPuzzle.subdivisions; yp++) {
                    for(var xp:int = 0; xp < SlidingPuzzle.subdivisions; xp++) {
                        if (++pieceCount == lastPiece)
                            break;
                        var image:Bitmap = images[yp][xp];
                        var piece:PuzzlePiece = new PuzzlePiece(image, {width:_pieceWidth, height:_pieceHeight, segmentsH:3, segmentsW:3});
                        piece.addEventListener(PuzzleEvent.CLICK, clickHandler);

                        piece.addEventListener(PuzzleEvent.MOVE, moveHandler);
                        piece.addEventListener(PuzzleEvent.READY, moveHandler);

                        piece.x = xp * _pieceWidth + xp * _padding;
                        piece.z = -(yp * _pieceHeight + yp * _padding);

                        addChild(piece);
                        _pieces.push(piece);
                    }
                }
                centerBoard();
            }

            public function shuffle():void {
                var p:PuzzlePiece;
                var direction:int;
                for(var i:int = 0; i < 200; i++) {
                    do {
                        p = _pieces[int(Math.random() * _pieces.length - 1)];
                        direction = checkNeighbours(p);
                    } while(direction == -1);
                    p.move(direction, false);
                }
            }

            private function clickHandler(e:PuzzleEvent):void {
                if (_moving)
                    return ;
                var direction:int = checkNeighbours(e.piece);
                if (direction > 0) {
                    e.piece.move(direction);
                }
            }

            private function moveHandler(e:PuzzleEvent):void {
                if (e.type == PuzzleEvent.MOVE)
                    _moving = true;
                else if (e.type == PuzzleEvent.READY)
                    _moving = false;
            }

            private function checkNeighbours(piece:PuzzlePiece):int {
                var empty:Boolean;

                // LEFT
                if (piece.x > 0) {
                    empty = isEmptySpace(piece.x - _pieceWidth - _padding, piece.z);
                    if (empty)
                        return PuzzlePiece.LEFT;
                }

                // RIGHT
                if (piece.x < _boardWidth - _pieceWidth - _padding) {
                    empty = isEmptySpace(piece.x + _pieceWidth + _padding, piece.z);
                    if (empty)
                        return PuzzlePiece.RIGHT;
                }

                // DOWN
                if (piece.z < 0) {
                    empty = isEmptySpace(piece.x, piece.z + _pieceHeight + _padding);
                    if (empty)
                        return PuzzlePiece.UP;
                }

                // UP
                if (piece.z > -_boardHeight + _pieceHeight + _padding) {
                    empty = isEmptySpace(piece.x, piece.z - _pieceHeight - _padding);
                    if (empty)
                        return PuzzlePiece.DOWN;
                }

                return -1;
            }

            private function isEmptySpace(xp:int, zp:int):Boolean {
                for each(var p:PuzzlePiece in _pieces) {
                    if (p.x == xp) {
                        if (p.z == zp) {
                            return false;
                        }
                    }
                }
                return true;
            }

            private function clearBoard():void {
                for each(var p:PuzzlePiece in _pieces) {
                    p.removeEventListener(PuzzleEvent.CLICK, clickHandler);
                    p.removeEventListener(PuzzleEvent.MOVE, moveHandler);
                    p.removeEventListener(PuzzleEvent.READY, moveHandler)
                    removeChild(p);
                }
                _pieces = new Array();
            }

            private function centerBoard():void {
                x = -(_boardWidth * .5 - _pieceWidth * .5);
                z = _boardHeight * .5 - _pieceHeight * .5;
            }

        }

    }