import { ConfigType } from '../config.js';
import GridData from '../grid.js';
import { array } from '../dataHelper.js';
import { Color, State } from '../primitives.js';
import TileData from '../tile.js';
import Rule from './rule.js';
class RegionAreaRule extends Rule {
    /**
     * **All &lt;color&gt; regions have area &lt;size&gt;**
     *
     * @param color - The color of the regions.
     * @param size - The area of the regions.
     */
    constructor(color, size) {
        super();
        Object.defineProperty(this, "color", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: color
        });
        Object.defineProperty(this, "size", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: size
        });
        this.color = color;
        this.size = size;
    }
    get id() {
        return `region_area`;
    }
    get explanation() {
        return `All ${this.color} regions have area ${this.size}`;
    }
    get configs() {
        return RegionAreaRule.CONFIGS;
    }
    createExampleGrid() {
        if (this.size < RegionAreaRule.EXAMPLE_GRID_DARK.length) {
            switch (this.color) {
                case Color.Dark:
                    return RegionAreaRule.EXAMPLE_GRID_DARK[this.size];
                case Color.Light:
                    return RegionAreaRule.EXAMPLE_GRID_LIGHT[this.size];
                case Color.Gray:
                    return RegionAreaRule.EXAMPLE_GRID_GRAY[this.size];
            }
        }
        else {
            let count = this.size;
            const tiles = array(5, 4, (x, y) => {
                count--;
                return new TileData(true, false, count >= 0
                    ? this.color
                    : this.color === Color.Gray
                        ? x % 2 !== y % 2
                            ? Color.Dark
                            : Color.Light
                        : this.color === Color.Light
                            ? Color.Dark
                            : Color.Light);
            });
            return new GridData(5, 4, tiles);
        }
    }
    get searchVariants() {
        return RegionAreaRule.SEARCH_VARIANTS;
    }
    validateGrid(grid) {
        const visited = array(grid.width, grid.height, (i, j) => !(grid.getTile(i, j).exists && grid.getTile(i, j).color === this.color));
        let complete = true;
        while (true) {
            const seed = grid.find((_tile, x, y) => !visited[y][x]);
            if (!seed)
                break;
            const completed = [];
            const gray = [];
            grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === this.color, (_, x, y) => {
                completed.push({ x, y });
                visited[y][x] = true;
            });
            if (this.color === Color.Gray) {
                gray.push(...completed);
            }
            else {
                grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === Color.Gray || tile.color === this.color, (_, x, y) => {
                    gray.push({ x, y });
                });
            }
            if (completed.length > this.size) {
                return { state: State.Error, positions: completed };
            }
            else if (gray.length < this.size) {
                return { state: State.Error, positions: gray };
            }
            else if (completed.length !== this.size ||
                completed.length !== gray.length) {
                complete = false;
            }
        }
        return complete ? { state: State.Satisfied } : { state: State.Incomplete };
    }
    copyWith({ color, size }) {
        return new RegionAreaRule(color ?? this.color, size ?? this.size);
    }
    withColor(color) {
        return this.copyWith({ color });
    }
    withSize(size) {
        return this.copyWith({ size });
    }
}
Object.defineProperty(RegionAreaRule, "CONFIGS", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze([
        {
            type: ConfigType.Color,
            default: Color.Dark,
            allowGray: false,
            field: 'color',
            description: 'Color',
            configurable: true,
        },
        {
            type: ConfigType.Number,
            default: 2,
            min: 0,
            field: 'size',
            description: 'Region Size',
            configurable: true,
        },
    ])
});
Object.defineProperty(RegionAreaRule, "EXAMPLE_GRID_DARK", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze([
        GridData.create(['wwwww', 'wwwww', 'wwwww', 'wwwww']),
        GridData.create(['bwwbw', 'wbwwb', 'bwbww', 'wbwwb']),
        GridData.create(['bbwbb', 'wwbww', 'wwbww', 'wwwbb']),
        GridData.create(['bbwww', 'bwbwb', 'wbbwb', 'wwwwb']),
        GridData.create(['bwbbb', 'bbwwb', 'bwbbw', 'wwbbw']),
        GridData.create(['bbbww', 'bbwbw', 'wwbbb', 'wwwbw']),
        GridData.create(['bbbww', 'bbbwb', 'wwwbb', 'wwbbb']),
        GridData.create(['bbbbb', 'bbwww', 'wwwbb', 'bbbbb']),
        GridData.create(['bbbbw', 'bbbww', 'bwwww', 'wwwww']),
        GridData.create(['wwwww', 'bbbbb', 'bbwbb', 'wwwww']),
        GridData.create(['bbbbb', 'bbbww', 'bbwww', 'wwwww']),
    ])
});
Object.defineProperty(RegionAreaRule, "EXAMPLE_GRID_LIGHT", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze(RegionAreaRule.EXAMPLE_GRID_DARK.map(grid => new GridData(grid.width, grid.height, grid.tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark))))))
});
Object.defineProperty(RegionAreaRule, "EXAMPLE_GRID_GRAY", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze(RegionAreaRule.EXAMPLE_GRID_DARK.map(grid => new GridData(grid.width, grid.height, grid.tiles.map((row, y) => row.map((tile, x) => tile.withColor(tile.color === Color.Dark
        ? Color.Gray
        : x % 2 !== y % 2
            ? Color.Dark
            : Color.Light))))))
});
Object.defineProperty(RegionAreaRule, "SEARCH_VARIANTS", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: [
        new RegionAreaRule(Color.Dark, 2).searchVariant(),
        new RegionAreaRule(Color.Light, 2).searchVariant(),
    ]
});
export default RegionAreaRule;
export const instance = new RegionAreaRule(Color.Dark, 2);
