
function GridCell(x, y) {
    this.px = x;
    this.py = y;
    this.version = 0;
}

GridCell.prototype.toStatic = function() {
    return { x: this.px, y: this.py };
};

function EmptyCell(x, y) {
    GridCell.call(this, x, y);
}
EmptyCell.prototype = Object.create(GridCell);
EmptyCell.prototype.constructor = EmptyCell;

EmptyCell.prototype.toStatic = function() {
    var ret = GridCell.prototype.toStatic.call(this);
    ret["type"] = null;
    return ret;
};

function LetterCell(x, y, letter) {
    GridCell.call(this, x, y);
    this.letter = letter;
    this.found = false;
}
LetterCell.prototype = Object.create(GridCell);
LetterCell.prototype.constructor = LetterCell;

LetterCell.prototype.toStatic = function() {
    var ret = GridCell.prototype.toStatic.call(this);
    ret.letter = this.letter;
    ret.v = this.version;
    return ret;
};

var debugFound = {};

function Definition(position, alignment, text) {
    this.position = position;
    this.alignment = alignment;
    this.textArray = text;
}

Definition.POSITION_BOTTOM = {};
Definition.POSITION_RIGHT = {};

Definition.ALIGNMENT_HORIZONTAL = {};
Definition.ALIGNMENT_VERTICAL = {};

Definition.prototype.toStatic = function() {
    var ret = {
        text: this.textArray
    };
    if (this.position === Definition.POSITION_RIGHT && this.alignment === Definition.ALIGNMENT_HORIZONTAL) {
        ret.pos = 1;
    } else if (this.position === Definition.POSITION_RIGHT && this.alignment === Definition.ALIGNMENT_VERTICAL) {
        ret.pos = 2;
    } else if (this.position === Definition.POSITION_BOTTOM && this.alignment === Definition.ALIGNMENT_HORIZONTAL) {
        ret.pos = 3;
    } else {
        ret.pos = 4;
    }
    return ret;
};

function DefinitionCell(x, y, code, definitionArray, currentDefinition) {
    if (debugFound[code]) debugFound[code].push({x:x,y:y}); else debugFound[code] = [{x:x,y:y}];
    GridCell.call(this, x, y);
    this.definitions = [];
    switch (code) {
        case 0: // a
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
        break;

        case 1: // b
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
        break;

        case 2: // c
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
        break;

        case 3: // d
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
        break;

        case 4: // e
        case 5: // f
        case 6: // g
        case 7: // h
        case 8: // i
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
        break;

        case 9: // j
        case 10: // k
        case 11: // l
        case 12: // m
        case 13: // n
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
        break;

        case 14: // o
        case 15: // p
        case 16: // q
        case 17: // r
        case 18: // s
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
        break;

        case 19: // t
        case 20: // u
        case 21: // v
        case 22: // w
        case 23: // x
            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
        break;
    }
}
DefinitionCell.prototype = Object.create(GridCell);
DefinitionCell.prototype.constructor = DefinitionCell;

DefinitionCell.prototype.toStatic = function() {
    var ret = GridCell.prototype.toStatic.call(this);
    ret.definitions = [];
    this.definitions.forEach((def) => {
        ret.definitions.push(def.toStatic());
    });
    return ret;
};

function parseGrid(grid, definitions, w, h) {
    var currentDefinition = 0
        ,resultGrid = []
        ,firstCharCode = 'a'.charCodeAt(0);

    for (let i =0; i < h; i++) {
        for (let j =0; j < w; j++) {
            let c = grid[i][j];
            if (c == c.toUpperCase()) {
                resultGrid.push(new LetterCell(j, i, c));
            } else if (c == 'z') {
                resultGrid.push(new EmptyCell(j, i));
            } else {
                let cell = new DefinitionCell(j, i, c.charCodeAt(0) -firstCharCode, definitions, currentDefinition);
                currentDefinition += cell.definitions.length;
                resultGrid.push(cell);
            }
        }
    }
    return resultGrid;
}

function Grid(publicId, data) {
    this.title = data["titre"];
    this.difficulty = data["force"];
    this.width = data["nbcaseslargeur"];
    this.height = data["nbcaseshauteur"];
    this.grid = parseGrid(data["grille"], data["definitions"], this.width, this.height);
    this.publicId = publicId;
    this.minVersion = 0;
};

Grid.prototype.getCell = function(x, y) {
    for (var i =0, nbCells = this.grid.length; i < nbCells; i++) {
        var cell = this.grid[i];
        if (cell.px === x && cell.py === y)
            return cell;
    }
    return null;
};

Grid.prototype.toStatic = function(v) {
    var ret = {};
    if (!v) {
        ret.title = this.title;
        ret.difficulty = this.difficulty;
        ret.w = this.width;
        ret.h = this.height;
        ret.v = this.minVersion;
    }
    var grid = [];
    this.grid.forEach((cell) => {
        if (cell instanceof DefinitionCell || cell instanceof EmptyCell) {
            if (!v) {
                grid.push(cell.toStatic());
            }
        } else if (cell.found && cell.version > v) {
            grid.push(cell.toStatic());
        }
    });
    if (grid.length)
        ret.grid = grid;
    return grid.length || !v ? ret : null;
};

module.exports.Grid = Grid;
module.exports.LetterCell = LetterCell;

