/**
 * @author xiangshouding
 */

class Box {
    constructor(x, y, icon) {
        this.x = x;
        this.y = y;
        this.icon = icon;
    }
}

class Board {
    constructor(w, h) {
        this.w = w;
        this.h = h;
        this.p = [];
    }

    fill() {
        var x, y;
        var icon = this.getIcon();
        for (y = 0; y < this.h; y++) {
            for (x = 0; x < this.w; x++) {
                if (!this.p[x]) {
                    this.p[x] = [];
                }

                this.p[x][y] = new Box(x, y, icon());
            }
        }
    }

    isBlocked(x, y) {
        try {
            var current = this.p[x][y];
            return current.icon;
        } catch (err) {
            //console.error(err);
        }

        return false;
    }

    /**
     * check horizon
     */
    vertical(p0, p1) {
        if (p0.x == p1.x && p0.y == p1.y) {
            return false;
        }

        if (p0.x != p1.x) {
            return false;
        }

        var start = Math.min(p0.y, p1.y), end = Math.max(p0.y, p1.y);

        if (end - start == 1) {
            return true;
        }

        for (var y = start + 1; y < end; y++) {
            if (this.isBlocked(p0.x, y)) {
                return false;
            }
        }

        return true;
    }

    horizon(p0, p1) {
        if (p0.x == p1.x && p0.y == p1.y) {
            return false;
        }

        if (p0.y != p1.y) {
            return false;
        }

        var start = Math.min(p0.x, p1.x), end = Math.max(p0.x, p1.x);

        if (end - start == 1) {
            return true;
        }

        for (var x = start + 1; x < end; x++) {
            if (this.isBlocked(x, p0.y)) {
                return false;
            }
        }

        return true;

    }

    turnOnce(p0, p1) {
        if (p0.x == p1.x && p0.y == p1.y) {
            return false;
        }

        var ret = false;

        if (!this.isBlocked(p0.x, p1.y)) {
            ret = this.horizon(p0, {x: p0.x, y: p1.y}) && this.vertical({x: p0.x, y: p1.y}, p1);
        }

        if (!this.isBlocked(p1.x, p0.y)) {
            ret = this.horizon(p0, {x: p1.x, y: p0.y}) && this.vertical({x: p1.x, y: p0.y}, p1);
        }

        return ret;
    }

    turnTwice(p0, p1) {
        if (p0.x == p1.x && p0.y == p1.y) {
            return false;
        }

        for (var x = 0; x <= this.w; x++) {
            for (var y = 0; y <= this.h; y++) {
                if (x != p0.x && x != p1.x && y != p1.y && y != p1.y) {
                    continue;
                }

                if ((x == p0.x && y == p0.y) || (x == p1.x && y == p1.y)) {
                    continue;
                }

                if (this.isBlocked(x, y)) {
                    continue;
                }

                if (this.turnOnce(p0, {x: x, y: y}) && (this.horizon({x: x, y: y}, p1) || this.vertical({x: x, y: y}, p1))) {
                    return true;
                }

                if (this.turnOnce({x: x, y: y}, p1) && (this.horizon(p0, {x: x, y: y}) || this.vertical(p0, {x: x, y: y}))) {
                    return true;
                }
            }
        }

        return false;
    }

    clear(x0, y0, x1, y1) {
        var p0 = {x: x0, y: y0}, p1 = {x: x1, y: y1};

        if (this.p[x0][y0].icon != this.p[x1][y1].icon) {
            return false;
        }

        if (this.horizon(p0, p1)) {
            return true;
        }

        if (this.vertical(p0, p1)) {
            return true;
        }

        if (this.turnOnce(p0, p1)) {
            return true;
        }

        if (this.turnTwice(p0, p1)) {
            return true;
        }

        return false;
    }

    remove(x0, y0, x1, y1) {
        if (this.clear(x0, y0, x1, y1)) {
            this.p[x0][y0] = null;
            this.p[x1][y1] = null;
            return true;
        }

        return false;
    }

    render() {
        this.fill();
        var x, y;
        var app = document.getElementById('app');
        for (y = 0; y < this.h; y++) {
            for (x = 0; x < this.w; x++) {
                var node = document.createElement('div');
                node.setAttribute('class', 'box');
                node.setAttribute('x', x);
                node.setAttribute('y', y);
                node.setAttribute('ref', 'box');
                node.innerText = this.p[x][y].icon;
                app.appendChild(node);
            }
            var br = document.createElement('br');
            app.appendChild(br);
        }
    }

    shuffle(array) {

        var currentIndex = array.length
            , temporaryValue
            , randomIndex;

        while (0 !== currentIndex) {

            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;

            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }

        return array;
    }

    getIcon() {
        var icons = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
            'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
        'W', 'X', 'Y', 'Z', '@', '#', '$', '%', '&', '!', '*'];
        var m = icons.length, cls = Math.floor(m / 2 + Math.random() * m);
        var times = Math.floor(this.w * this.h / cls);

        if (times % 2 != 0) {
            times -= 1;
        }

        if (times === 0) {
            times = 2;
        }

        var array = [];

        for (var i = 0; i < this.w * this.h; i += times) {
            for (var j = 0; j < times; j++) {
                array.push(icons[i % m]);
            }
        }

        array = this.shuffle(array);
        var index = 0;

        return function (x, y) {
            return array[index++]; 
        };
    }
}

function main() {
    var board = new Board(4, 4);
    board.render();

    return board;
}


function getPosFromDom(elm, axial) {
    return parseInt(elm.getAttribute(axial));
}

function mount() {
    var board = main();
    var queue = [];
    window.addEventListener('click', function (e) {
        if (e.target.getAttribute('ref') != 'box') {
            return;
        }

        if (queue.length != 2) {
            queue.push(e.target);

            if (queue.length == 2) {
                try {
                    var isClear = board.remove(
                        getPosFromDom(queue[0], 'x'),
                        getPosFromDom(queue[0], 'y'),
                        getPosFromDom(queue[1], 'x'),
                        getPosFromDom(queue[1], 'y')
                    );

                    if (isClear) {
                        queue[0].setAttribute('class', 'box done');
                        queue[0].innerHTML = '&nbsp;';
                        queue[1].setAttribute('class', 'box done');
                        queue[1].innerHTML = '&nbsp;';
                    } else {
                        alert('Error, Please choice two same icon box.')
                    }
                } catch (err) {
                    console.error(err);
                }

                queue = []; // clear stack
            }
        } else {
            queue = [];
        }
    })
}

function clear() {

}

window.onload = mount;