import { ConfigType } from '../config.js';
import GridData from '../grid.js';
import { Color, ORIENTATIONS, Orientation, State, orientationToggle, } from '../primitives.js';
import MultiEntrySymbol from './multiEntrySymbol.js';
class MyopiaSymbol extends MultiEntrySymbol {
    /**
     * **Viewpoint Numbers count visible cells in the four directions**
     * @param x - The x-coordinate of the symbol.
     * @param y - The y-coordinate of the symbol.
     * @param diagonals - Whether the symbol should consider diagonal directions.
     * @param directions - The directions in which an arrow is pointing.
     */
    constructor(x, y, diagonals, directions) {
        super(x, y);
        Object.defineProperty(this, "diagonals", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: diagonals
        });
        Object.defineProperty(this, "directions", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: directions
        });
        this.directions = directions;
        this.diagonals =
            directions[Orientation.DownLeft] ||
                directions[Orientation.DownRight] ||
                directions[Orientation.UpLeft] ||
                directions[Orientation.UpRight]
                ? true
                : diagonals;
    }
    get id() {
        return `myopia`;
    }
    get placementStep() {
        return 1;
    }
    get explanation() {
        return this.diagonals
            ? '*Framed myopia arrows* point to *all* the closest opposite cells in 8 directions'
            : '*Myopia arrows* point to *all* the closest cells of the opposite color';
    }
    get configs() {
        return MyopiaSymbol.CONFIGS;
    }
    createExampleGrid() {
        return this.diagonals
            ? MyopiaSymbol.EXAMPLE_DIAGONAL_GRID
            : MyopiaSymbol.EXAMPLE_GRID;
    }
    validateSymbol(grid) {
        const tile = grid.getTile(this.x, this.y);
        if (!tile.exists || tile.color === Color.Gray)
            return State.Incomplete;
        const allDirections = this.diagonals
            ? ORIENTATIONS
            : [Orientation.Up, Orientation.Down, Orientation.Left, Orientation.Right];
        const map = {
            up: { min: 0, max: 0, complete: true },
            down: { min: 0, max: 0, complete: true },
            left: { min: 0, max: 0, complete: true },
            right: { min: 0, max: 0, complete: true },
            'up-left': { min: 0, max: 0, complete: true },
            'up-right': { min: 0, max: 0, complete: true },
            'down-left': { min: 0, max: 0, complete: true },
            'down-right': { min: 0, max: 0, complete: true },
        };
        const pos = { x: this.x, y: this.y };
        allDirections.forEach(direction => {
            grid.iterateDirectionAll(pos, direction, t => !t.exists || t.color === tile.color, () => {
                map[direction].min++;
            });
            let stopped = false;
            grid.iterateDirectionAll(pos, direction, t => {
                if (!t.exists)
                    return true;
                if (t.color === tile.color || t.color === Color.Gray)
                    return true;
                stopped = true;
                return false;
            }, t => {
                map[direction].max++;
                if (t.exists && t.color === Color.Gray)
                    map[direction].complete = false;
            });
            if (!stopped)
                map[direction].max = Number.MAX_SAFE_INTEGER;
        });
        const pointedDirections = allDirections.filter(d => this.directions[d]);
        const otherDirections = allDirections.filter(d => !this.directions[d]);
        for (let i = 0; i < pointedDirections.length; i++) {
            const direction1 = pointedDirections[i];
            for (let j = i + 1; j < pointedDirections.length; j++) {
                const direction2 = pointedDirections[j];
                if (map[direction1].min > map[direction2].max)
                    return State.Error;
                if (map[direction2].min > map[direction1].max)
                    return State.Error;
            }
        }
        if (Math.min(...otherDirections.map(d => map[d].max)) <=
            Math.max(...pointedDirections.map(d => map[d].min)))
            return State.Error;
        if (pointedDirections.length === 0 &&
            otherDirections.some(d => map[d].max !== Number.MAX_SAFE_INTEGER))
            return State.Error;
        if (pointedDirections.some(d => map[d].complete && map[d].max === Number.MAX_SAFE_INTEGER))
            return State.Error;
        if (pointedDirections.length > 0 &&
            otherDirections.length > 0 &&
            pointedDirections.every(d => map[d].complete) &&
            Math.max(...pointedDirections.map(d => map[d].max)) <
                Math.min(...otherDirections.map(d => map[d].min)))
            return State.Satisfied;
        if (allDirections.every(d => map[d].complete))
            return State.Satisfied;
        return State.Incomplete;
    }
    copyWith({ x, y, diagonals, directions, }) {
        return new MyopiaSymbol(x ?? this.x, y ?? this.y, diagonals ?? this.diagonals, directions ?? this.directions);
    }
    withDirections(directions) {
        return this.copyWith({ directions });
    }
    withDiagonals(diagonals) {
        return this.copyWith({ diagonals });
    }
}
Object.defineProperty(MyopiaSymbol, "CONFIGS", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze([
        {
            type: ConfigType.Number,
            default: 0,
            field: 'x',
            description: 'X',
            configurable: false,
        },
        {
            type: ConfigType.Number,
            default: 0,
            field: 'y',
            description: 'Y',
            configurable: false,
        },
        {
            type: ConfigType.Boolean,
            default: false,
            field: 'diagonals',
            description: 'Include diagonal directions',
            configurable: true,
        },
        {
            type: ConfigType.OrientationToggle,
            default: orientationToggle(Orientation.Up, Orientation.Right),
            field: 'directions',
            description: 'Directions',
            configurable: true,
        },
    ])
});
Object.defineProperty(MyopiaSymbol, "EXAMPLE_GRID", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze(GridData.create(['wbwww', 'bwwwb', 'wwwww', 'wbwww']).withSymbols([
        new MyopiaSymbol(1, 1, false, orientationToggle(Orientation.Up, Orientation.Left)),
        new MyopiaSymbol(4, 3, false, orientationToggle(Orientation.Up)),
    ]))
});
Object.defineProperty(MyopiaSymbol, "EXAMPLE_DIAGONAL_GRID", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: Object.freeze(GridData.create(['wbwww', 'bwwwb', 'wwwww', 'wbwww']).withSymbols([
        new MyopiaSymbol(1, 2, true, orientationToggle(Orientation.UpLeft, Orientation.Down)),
        new MyopiaSymbol(3, 2, true, orientationToggle(Orientation.UpRight)),
    ]))
});
export default MyopiaSymbol;
export const instance = new MyopiaSymbol(0, 0, false, orientationToggle(Orientation.Up, Orientation.Right));
