"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Board = void 0;
const constants_1 = require("@scrabble-solver/constants");
const Cell_1 = require("./Cell");
const Tile_1 = require("./Tile");
class Board {
    static fromStringArray(stringArray) {
        return new Board({
            rows: stringArray.map((row, y) => row.split('').map((character, x) => new Cell_1.Cell({
                isEmpty: !character || character === constants_1.EMPTY_CELL,
                tile: character === constants_1.EMPTY_CELL ? Tile_1.Tile.Null : new Tile_1.Tile({ character }),
                x,
                y,
            }))),
        });
    }
    constructor({ rows }) {
        this.rows = rows;
        this.columnsCount = rows[0].length;
        this.rowsCount = rows.length;
    }
    get center() {
        const x = Math.floor(this.columnsCount / 2);
        const y = Math.floor(this.rowsCount / 2);
        return this.rows[y][x];
    }
    clone() {
        const rows = this.rows.map((row) => row.map((cell) => cell.clone()));
        return new Board({ rows });
    }
    collides(cell) {
        return this.collidesUp(cell) || this.collidesDown(cell) || this.collidesLeft(cell) || this.collidesRight(cell);
    }
    collidesDown({ x, y }) {
        return y < this.rowsCount - 1 && !this.rows[y + 1][x].isEmpty;
    }
    collidesLeft({ x, y }) {
        return x > 0 && !this.rows[y][x - 1].isEmpty;
    }
    collidesRight({ x, y }) {
        return x < this.columnsCount - 1 && !this.rows[y][x + 1].isEmpty;
    }
    collidesUp({ x, y }) {
        return y > 0 && !this.rows[y - 1][x].isEmpty;
    }
    equals(other) {
        return (this.columnsCount === other.columnsCount &&
            this.rowsCount === other.rowsCount &&
            this.rows.every((row, rowIndex) => {
                return row.every((cell, cellIndex) => {
                    return cell.equals(other.rows[rowIndex][cellIndex]);
                });
            }));
    }
    getBlanksCount() {
        return this.rows.reduce((count, row) => {
            return count + row.reduce((rowCount, cell) => rowCount + (cell.tile.isBlank ? 1 : 0), 0);
        }, 0);
    }
    getColumn(index) {
        return this.rows.map((row) => row[index]);
    }
    getRow(index) {
        return this.rows[index];
    }
    getTilesCount() {
        return this.rows.reduce((count, row) => {
            return count + row.reduce((rowCount, cell) => rowCount + (cell.hasTile() ? 1 : 0), 0);
        }, 0);
    }
    getWords() {
        const horizontalWords = getHorizontalWords(this.rows);
        const verticalWords = getHorizontalWords(transpose(this.rows));
        return [...horizontalWords, ...verticalWords];
    }
    isEmpty() {
        return this.rows.every((row) => row.every(({ isEmpty }) => isEmpty));
    }
    toJson() {
        return this.rows.map((row) => row.map((cell) => cell.toJson()));
    }
    toString() {
        return this.rows.map((row) => row.map(String).join('')).join('\n');
    }
    updateCell(x, y, updateCell) {
        this.rows[y][x] = updateCell(this.rows[y][x]);
    }
    updateRow(y, updateRow) {
        this.rows[y] = updateRow(this.rows[y]);
    }
}
exports.Board = Board;
Board.create = (width, height) => {
    const rows = Array(height);
    const emptyRow = Array(width).fill(' ').join('');
    const emptyRows = rows.fill(emptyRow);
    return Board.fromStringArray(emptyRows);
};
Board.fromJson = (json) => {
    return new Board({
        rows: json.map((row) => row.map(Cell_1.Cell.fromJson)),
    });
};
const transpose = (array) => {
    const rows = array.length;
    const cols = array[0].length;
    const transposed = Array(cols)
        .fill(null)
        .map(() => Array(rows));
    for (let y = 0; y < rows; ++y) {
        for (let x = 0; x < cols; ++x) {
            transposed[x][y] = array[y][x];
        }
    }
    return transposed;
};
const getHorizontalWords = (cells) => {
    const words = [];
    for (const row of cells) {
        let currentWord = [];
        for (const cell of row) {
            if (!cell.isEmpty) {
                currentWord.push(cell);
            }
            else if (currentWord.length > 0) {
                if (currentWord.length > 1) {
                    words.push(wordToString(currentWord));
                }
                currentWord = [];
            }
        }
        if (currentWord.length > 1) {
            words.push(wordToString(currentWord));
        }
    }
    return words;
};
const wordToString = (word) => {
    return word.map((cell) => cell.tile.character).join('');
};
