var dk;
if (!dk) dk = {};
if (!dk.qbrix) dk.qbrix = {};

dk.qbrix.Maze =
{
    Cell: function()
    {
        this.Element = null;
        this.X = null;
        this.Y = null;
        this.Visited = false;
    },

    Create: function(width, height, cellSize, left, top, backColor, borderColor)
    {
        if (arguments.length < 7) borderColor = '#000000';
        if (arguments.length < 6) backColor = '#eeeeee';
        if (arguments.length < 5) top = 200;
        if (arguments.length < 4) left = 200;
        if (arguments.length < 3) cellSize = 12; //Math.floor(Math.random() * 12 + 8);
        if (arguments.length < 2) height = 20; //Math.floor(Math.random() * 30 + 20);
        if (arguments.length < 1) width = 20; //Math.floor(Math.random() * 30 + 30);

        if (width > 200) width = 200;
        if (width < 2) width = 2;
        if (height > 200) height = 200;
        if (height < 2) height = 2;
        if (cellSize > 20) cellSize = 20;
        if (cellSize < 4) cellSize = 4;

        var _cells = [];
        var _frame = null;
        var _that = this;

        // Sort of properties
        //this.GetWidth = function() { return width; };
        //this.GetHeight = function() { return height; };

        this.Initialize = function()
        {
            this.CreateStyles();
            this.CreateFrame();
            this.CreateCells();
        };

        this.CreateStyles = function()
        {
            var head, ss, style1, style2;

            if (document.styleSheets.length == 0)
            {
                // Add dummy stylesheet
                head = document.getElementsByTagName('head')[0];
                style1 = document.createElement('style');
                style1.setAttribute('type', 'text/css');
                head.appendChild(style1);
            }

            // Get first stylesheet
            ss = document.styleSheets[0];

            style1 = 'position:absolute;' +
                     'border-color:' + borderColor + ';' +
                     'border-bottom-style:solid;' +
                     'border-right-style:solid;' +
                     'background-color:' + backColor + ';' +
                     'color:' + borderColor + ';' +
                     'z-index:10000;';
            style2 = 'position:absolute;' +
                     'border-color:' + borderColor + ';' +
                     'border-top-style:solid;' +
                     'border-left-style:solid;' +
                     'padding-left:1px;' +
                     'font-family:Verdana;' +
                     'font-size:11px;' +
                     'text-align:left;' +
                     'vertical-align:top;' +
                     'width:' + cellSize + 'px;' +
                     'height:' + cellSize + 'px;';
            if (ss.insertRule)
            {
                ss.insertRule('.dk-qbrix-maze-frame {' + style1 + '}', 0);
                ss.insertRule('.dk-qbrix-maze-cell {' + style2 + '}', 1);
            }
            else if (ss.addRule)
            {
                ss.addRule('.dk-qbrix-maze-frame', style1, 0);
                ss.addRule('.dk-qbrix-maze-cell', style2, 0);
            }
        };

        this.CreateFrame = function()
        {
            var body = document.getElementsByTagName('body')[0];

            _frame = document.createElement('div');
            _frame.className = 'dk-qbrix-maze-frame';
            _frame.style['width'] = (cellSize * width) + 'px';
            _frame.style['height'] = (cellSize * height) + 'px';
            _frame.style['top'] = top + 'px';
            _frame.style['left'] = left + 'px';
            body.appendChild(_frame);
        };

        this.CreateCells = function()
        {
            var n, m;
            var cell;

            for (n = 0; n < width; n++)
            {
                _cells.push([]);
                for (m = 0; m < height; m++)
                {
                    cell = new dk.qbrix.Maze.Cell();
                    cell.X = n;
                    cell.Y = m;
                    cell.Element = document.createElement('div');
                    cell.Element.className = 'dk-qbrix-maze-cell';
                    cell.Element.style['top'] = (cellSize * m) + 'px';
                    cell.Element.style['left'] = (cellSize * n) + 'px';
                    _frame.appendChild(cell.Element);
                    _cells[n].push(cell);
                }
            }

            //var elm = _cells[0][0].Element;
            //elm.appendChild(document.createTextNode('Q'));
            //elm = _cells[width - 1][height - 1].Element;
            //elm.appendChild(document.createTextNode('s'));
        };

        this.Generate = function()
        {
            var stack = [];
            var cell, next;
            var neighbors;
            var rnd, x, y;
            var count = 0, lastStart = 0;
            var test5 = false;

            // Create openings
            //cell = _cells[0][0];
            //cell.Element.style['borderTop'] = 'none';

            // Algorithm found on Wikipedia (http://en.wikipedia.org/wiki/Maze_generation_algorithm)
            // 1.Start at a particular cell and call it the "exit."
            // 2.Mark the current cell as visited, and get a list of its neighbors. For each neighbor, starting with a randomly selected neighbor:
            //     1.If that neighbor hasn't been visited, remove the wall between this cell and that neighbor, and then recurse with that neighbor as the current cell.

            x = Math.floor(Math.random() * width);
            y = Math.floor(Math.random() * height);
            cell = _cells[x][y];
            cell.Visited = true;
            //cell.Element.innerHTML = 'x';
            stack.push(cell);
            while (cell && stack.length > 0)
            {
                neighbors = cell.GetUnvisitedNeighbors(_cells);
                if (neighbors.length == 0)// || (count % 20 == 0 && test5))
                {
                    test5 = false;
                    // This is the proper wikipedia formulae...
                    //cell = stack.pop();

                    // ...but this creates harder-to-solve mazes
                    for (var n = lastStart; n < stack.length; n++)
                    {
                        cell = stack[n];
                        neighbors = cell.GetUnvisitedNeighbors(_cells);
                        if (neighbors.length > 0)
                        {
                            lastStart = n;
                            //cell.Element.innerHTML = 'o';
                            break;
                        }
                    }
                }
                else
                {
                    test5 = true;
                    rnd = Math.floor(Math.random() * neighbors.length);
                    next = neighbors[rnd];
                    next.Visited = true;
                    stack.push(next);
                    count++;

                    if (cell.X < next.X)
                        next.Element.style['borderLeftStyle'] = 'none';
                    else if (cell.X > next.X)
                        cell.Element.style['borderLeftStyle'] = 'none';
                    else if (cell.Y < next.Y)
                        next.Element.style['borderTopStyle'] = 'none';
                    else
                        cell.Element.style['borderTopStyle'] = 'none';

                    cell = next;
                }

                if (count == width * height - 1) break;
            }
        };

        this.Regenerate = function()
        {
            var n, m, cell;
            for (n = 0; n < width; n++)
            {
                for (m = 0; m < height; m++)
                {
                    cell = _cells[n][m];
                    cell.Visited = false;
                    cell.Element.style['borderLeftStyle'] = 'solid';
                    cell.Element.style['borderTopStyle'] = 'solid';
                }
            }

            _that.Generate();
        };

        this.Destroy = function()
        {
            var n, m, cell;
            var body = document.getElementsByTagName('body')[0];

            for (n = 0; n < width; n++)
            {
                for (m = 0; m < height; m++)
                {
                    cell = _cells[n][m];
                    _frame.removeChild(cell.Element);
                    cell = null;
                }
            }
            body.removeChild(_frame);
        };

        this.Initialize();
        this.Generate();
    }
};

dk.qbrix.Maze.Cell.prototype.GetUnvisitedNeighbors = function(cells)
{
    var arr = [];
    var cell;

    if (this.X > 0)
    {
        cell = cells[this.X - 1][this.Y];
        if (!cell.Visited) arr.push(cell);
    }
    if (this.X < cells.length - 1)
    {
        cell = cells[this.X + 1][this.Y];
        if (!cell.Visited) arr.push(cell);
    }
    if (this.Y > 0)
    {
        cell = cells[this.X][this.Y - 1];
        if (!cell.Visited) arr.push(cell);
    }
    if (this.Y < cells[0].length - 1)
    {
        cell = cells[this.X][this.Y + 1];
        if (!cell.Visited) arr.push(cell);
    }

    return arr;
};

