export default class boardConstructor {
    constructor(boardSize = 10, availableItems) {
        this.boardSize = boardSize;
        this.availableItems = availableItems
    }

    generateItems() {
        let items = {};
        let itemsCount = 4;
        let curentSize = 1;
        for (let i = itemsCount; i > 0; i--) {
            let key = this.__generateItemId();
            items[key] = {
                id: key,
                size: curentSize,
                type: "fish",
                name: this.availableItems.pop(Math.floor(Math.random() * this.availableItems.length) + 1),
                orientation: "h",
                margin: 1,
                cells: [],
                marginCells: [],
            };
            if (i <= 1) {
                i = itemsCount;
                itemsCount--;
                curentSize++;
            }
        }
        return items;
    }

    generateBoard(items) {
        Object.keys(items).map((id) => {
            items[id].cells = [];
            items[id].marginCells = [];
        });
        let itemlist = Object.values(items);
        itemlist.sort((a, b) => b.size - a.size);
        while (itemlist.length > 0) {
            let currentItem = { ...itemlist.shift() };
            currentItem.orientation = Math.random() < 0.5 ? "h" : "v";
            while (!this.isItemAlreadyOnBoard(items[currentItem.id])) {
                let targetCell = Math.floor(
                    Math.random() * this.boardSize * this.boardSize
                );
                currentItem.cells = this.getItemCells(targetCell, currentItem);
                currentItem.marginCells = this.getItemMarginCells(currentItem);
                if (
                    this.isItemHasMarginCollision(currentItem, items) ||
                    this.isItemHasCellsCollision(currentItem, items)
                )
                    continue;
                items[currentItem.id] = currentItem;
            }
        }
        return items;
    }

    getItemCells(targetCell, item) {
        let cells = [];
        let cellRow = Math.floor(targetCell / this.boardSize);
        for (let i = 0; i <= item.size - 1; i++) {
            if (item.orientation == "h") {
                let currentCell = targetCell + i;
                if (
                    currentCell <=
                    cellRow * this.boardSize + (this.boardSize - 1)
                ) {
                    cells.push(currentCell);
                } else {
                    cells.push(
                        targetCell +
                            (cellRow * this.boardSize +
                                (this.boardSize - 1) -
                                currentCell)
                    );
                }
            } else if (item.orientation == "v") {
                let currentCell = targetCell + this.boardSize * i;
                if (currentCell < this.boardSize * this.boardSize) {
                    cells.push(currentCell);
                } else {
                    cells.push(targetCell - this.boardSize * (item.size - i));
                }
            }
        }
        return cells;
    }

    getItemMarginCells(item) {
        if (item.margin <= 0 || item.cells.length <= 0) return [];
        let marginCells = item.cells.map((cell) => {
            return this.__getCellMargin(cell, item.margin);
        });
        return marginCells
            .flat(1)
            .filter((cell) => item.cells.indexOf(cell) < 0);
    }

    findItemIdInBoardCell(targetCell, items) {
        let item = Object.keys(items).filter((id) => {
            return items[id].cells.includes(targetCell);
        });
        return item[0];
    }

    isItemAlreadyOnBoard(item) {
        return Object.values(item.cells).length > 0;
    }

    isItemHasMarginCollision(item, items) {
        if (item.cells?.length <= 0 && item.cells.marginCells?.length <= 0)
            return false;
        return (
            Object.keys(items).filter((id) => {
                let collisionWithBoardItem =
                    items[id].marginCells.filter((itemMarginCell) =>
                        item.cells.includes(itemMarginCell)
                    ).length > 0;
                let collisionWithCurrentItem =
                    items[id].cells.filter((itemCell) =>
                        item.marginCells.includes(itemCell)
                    ).length > 0;
                return collisionWithBoardItem || collisionWithCurrentItem;
            })?.length > 0
        );
    }

    isItemHasCellsCollision(item, items) {
        if (item.cells?.length <= 0) return false;
        return (
            Object.keys(items).filter((id) => {
                return (
                    items[id].cells.filter((itemCell) =>
                        item.cells.includes(itemCell)
                    ).length > 0
                );
            })?.length > 0
        );
    }

    isAllItemsReady(items) {
        return (
            Object.keys(items).filter((id) => {
                return items[id].cells?.length <= 0;
            })?.length <= 0
        );
    }

    __getCellMargin(cell, margin = 0) {
        let cellMargin = [];
        let cellRow = Math.floor(cell / this.boardSize);
        let maxCells = this.boardSize * this.boardSize - 1;
        let calcCell = null;
        for (
            let rowMargin = margin;
            rowMargin >= -Math.abs(margin);
            rowMargin--
        ) {
            let currentRow = cellRow + rowMargin;
            let rowOffset = this.boardSize * rowMargin;
            let rowMin = currentRow * this.boardSize;
            let rowMax = rowMin + this.boardSize - 1;
            for (
                let colMargin = margin;
                colMargin >= -Math.abs(margin);
                colMargin--
            ) {
                calcCell = cell + rowOffset + colMargin;
                if (
                    calcCell >= 0 &&
                    calcCell <= maxCells &&
                    calcCell >= rowMin &&
                    calcCell <= rowMax
                )
                    cellMargin.push(calcCell);
            }
        }
        return cellMargin;
    }

    __generateItemId() {
        return Math.random().toString(36).replace("0.", "");
    }
}
