
/**
 * @constructor
**/
function Definition(data) {
    /** @type {Array.<string>} */
    this.text = data["text"];
    /** @type {number} */
    this.direction = data["pos"];
    /** @type {Array.<Array.<number>>|null} */
    this.word = null;
}

/** @const */
Definition.RIGHT_HORIZONTAL = 1;
/** @const */
Definition.RIGHT_VERTICAL = 2;
/** @const */
Definition.BOTTOM_HORIZONTAL = 3;
/** @const */
Definition.BOTTOM_VERTICAL = 4;

/**
 * @constructor
**/
function Cell() {
    /** @type {boolean} */
    this.isBlack = false;
    /** @type {Array.<Definition>} */
    this.definitions = null;
    /** @type {boolean} */
    this.found = false;
    /** @type {string|null} */
    this.letter = null;
}

Cell.prototype.update = function(data) {
    if (data["type"] === null) {
        this.isBlack = true;
    } else if (data["definitions"] !== undefined) {
        this.definitions = [];
        data["definitions"].forEach(function(definition) {
            this.definitions.push(new Definition(definition));
        }.bind(this));
    } else if (data["letter"]) {
        this.letter = data["letter"];
        this.found = true;
        return data["v"];
    }
    return 0;
};

/**
 * @constructor
**/
function Grid(data) {
    /** @type {string} */
    this.title = data["title"] || "";
    /** @type {number} */
    this.difficulty = data["difficulty"];
    /** @type {number} */
    this.width = data["w"];
    /** @type {number} */
    this.height = data["h"];

    this.words = [];
    this.grid = [];
    for (var i =0; i < this.width; i++) {
        this.grid[i] = [];
        for (var j =0; j < this.height; j++) {
            this.grid[i][j] = new Cell();
        }
    }
}

Grid.prototype.computeWord = function(x, y, dx, dy) {
    if (!this.grid[x +dx] || !this.grid[x +dx][y +dy] || this.grid[x +dx][y +dy].definitions || this.grid[x +dx][y +dy].isBlack) {
        return [[x, y]];
    }
    var word = this.computeWord(x +dx, y +dy, dx, dy);
    word.unshift([x, y]);
    return word;
};

Grid.prototype.getWord = function(x, y) {
    var words = [];
    this.words.forEach(function(word) {
        for (var i =0, nbLetters =word.length; i < nbLetters; i++) {
            if (word[i][0] == x && word[i][1] == y) {
                words.push(word);
                break;
            }
        }
    });
    return words;
};

/**
 * @return {number|null}
**/
Grid.prototype.update = function(data) {
    var maxVersion = null
        ,grid = this.grid
        ,topologyUpdated = false;

    data.forEach(function(cellData) {
        var updateResult = grid[cellData["x"]][cellData["y"]].update(cellData);
        maxVersion = Math.max(maxVersion || 0, updateResult);
        if (updateResult === 0) {
            topologyUpdated = true;
        }
    });
    if (topologyUpdated) {
        var words = [];
        for (var i =0; i < this.width; i++) {
            for (var j =0; j < this.height; j++) {
                if (grid[i][j].definitions) {
                    grid[i][j].definitions.forEach(function(definition) {
                        var word;
                        switch (definition.direction) {
                            case Definition.RIGHT_VERTICAL:
                                word = this.computeWord(i +1, j, 0, 1);
                                break;

                            case Definition.RIGHT_HORIZONTAL:
                                word = this.computeWord(i +1, j, 1, 0);
                                break;

                            case Definition.BOTTOM_VERTICAL:
                                word = this.computeWord(i, j +1, 0, 1);
                                break;

                            case Definition.BOTTOM_HORIZONTAL:
                                word = this.computeWord(i, j +1, 1, 0);
                                break;

                        }
                        words.push(word);
                        definition.word = word;
                    }.bind(this));
                }
            }
        }
        this.words = words;
    }
    return maxVersion;
};

