import { assert } from "../../../core/assert.js";
import Task from "../../../core/process/task/Task.js";
import TaskGroup from "../../../core/process/task/TaskGroup.js";
import { TaskSignal } from "../../../core/process/task/TaskSignal.js";
import { actionTask } from "../../../core/process/task/util/actionTask.js";
import { GridTaskGenerator } from "../GridTaskGenerator.js";

export class GridTaskApplyActionToCells extends GridTaskGenerator {

    /**
     *
     * @type {GridCellAction|null}
     */
    action = null;

    /**
     *
     * @type {CellSupplier|null}
     */
    fitness = null;

    /**
     *
     * @returns {GridTaskApplyActionToCells}
     * @param {CellSupplier} selector
     * @param {GridCellAction} action
     * @param {string} name
     */
    static from({ selector, action, name }) {
        assert.equal(selector.isCellSupplier, true, 'selector.isCellSupplier !== true');
        assert.equal(action.isGridCellAction, true, 'action.isGridCellAction !== true');

        const r = new GridTaskApplyActionToCells();

        r.action = action;
        r.fitness = selector;

        return r;
    }

    build(grid, ecd, seed) {
        const tInitialize = actionTask(() => {
            this.fitness.initialize(grid, seed);
            this.action.initialize(grid, seed);
        });

        let cursor = 0;

        const tMain = new Task({
            name: `Apply action to cells`,
            computeProgress: () => {
                const c = this.fitness.count();

                if (c <= 0) {
                    return 0;
                }

                return cursor / c;
            },
            cycleFunction: () => {
                const cell_data = this.fitness.next();

                if (cell_data === undefined) {
                    return TaskSignal.EndSuccess;
                }

                cursor++;

                const y = cell_data.y;
                const x = cell_data.x;

                this.action.execute(grid, x, y, cell_data.rotation);

                return TaskSignal.Continue;
            }
        });

        tMain.addDependency(tInitialize);

        return new TaskGroup([tInitialize, tMain]);
    }
}
