import { assert } from "../../../../core/assert.js";

/**
 * Allows you to declare resources for the pass, reading/writing/creating resources that will be used by the pass.
 *
 * Should not be created directly, instead use {@link FrameGraph.add}
 *
 * @example
 *      const pass_data = {};
 *      const pass_builder = graph.add("pass", pass_data, (data, resources, context) => { ... });
 *      pass_data.resource_a = pass_builder.create("A", {});
 * @see FrameGraph
 */
export class FramePassBuilder {

    /**
     *
     * @type {FrameGraph}
     * @private
     */
    #graph = null;

    /**
     *
     * @type {FramePassNode}
     * @private
     */
    #node = null;

    /**
     *
     * @param {FrameGraph} graph
     * @param {FramePassNode} node
     */
    init(graph, node) {
        assert.defined(graph, 'graph');
        assert.defined(node, 'node');

        this.#graph = graph;
        this.#node = node;
    }

    /**
     * Create a new resource.
     * Creation implies writing as well.
     * @param {string} name
     * @param {ResourceDescriptor} descriptor
     * @returns {number} resource ID
     */
    create(name, descriptor) {
        const node = this.#graph.create_resource(name, descriptor);

        // remember resource
        this.#node.resource_creates.push(node);

        // creation implies writing as well
        this.#node.resource_writes.push(node);

        return node;
    }

    /**
     * Read an existing resource
     * @param {number} resource
     * @returns {number} resource ID same as the input
     */
    read(resource) {
        assert.isNonNegativeInteger(resource, 'resource');
        assert.defined(this.#graph.getResourceEntry(resource), 'resource node');

        const node = this.#node;

        return node.read(resource);
    }

    /**
     * Write a resource
     * @param {number} resource
     * @returns {number} resource ID
     */
    write(resource) {
        assert.isNonNegativeInteger(resource, 'resource');

        const graph = this.#graph;
        const node = this.#node;

        if (graph.getResourceEntry(resource).isImported()) {
            // comes from outside the graph, and we write to it. That's a side effect
            node.has_side_effects = true;
        }

        if (node.creates(resource)) {
            return node.write(resource);
        } else {
            // writing to a resource produces a renamed handle
            node.read(resource);

            return node.write(graph.clone_resource(resource));
        }

    }

    /**
     * Indicate that this pass has side effects.
     * Will force the node to always be executed, regardless if it contributes to the final outputs of the graph or not.
     * Useful for debug purposes or cases where some of the resources written are not part of the graph.
     */
    make_side_effect() {
        this.#node.has_side_effects = true;
    }
}
