import '../../../../assert/js/assert.js';
import '../../../../globals.js';
// Copyright 2023-2024, University of Colorado Boulder
/**
 * TODO: doc
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import arrayRemove from '../../../../phet-core/js/arrayRemove.js';
import { optionize3 } from '../../../../phet-core/js/optionize.js';
import { alpenglow, BufferSlot, ComputePipeline, getArrayType, getCastedType, ResourceUsage, U32Type, wgsl, WGSLModule, wgslWith } from '../../imports.js';
export const PIPELINE_BLUEPRINT_DEFAULTS = {
    log: false
};
class PipelineBlueprint {
    constructor(providedOptions) {
        this.usages = [];
        this.declarations = [];
        this.usageMap = new Map();
        const options = optionize3()({}, PIPELINE_BLUEPRINT_DEFAULTS, providedOptions);
        this.name = options.name;
        this.log = options.log;
    }
    async toComputePipeline(deviceContext, pipelineLayout) {
        return ComputePipeline.withContextAsync(deviceContext, this.name, this.toString(pipelineLayout), pipelineLayout);
    }
    add(name, declarations) {
        if (!this.declarations.some(declaration => declaration.name === name)) {
            this.declarations.push(new WGSLInternalDeclaration(name, declarations.withBlueprint(this)));
        }
    }
    addSlot(name, slot, bindingType) {
        let usage;
        // If it already exists, we'll do some checks and "combine" types (might switch read-only to read-write, etc.)
        if (this.usageMap.has(name)) {
            const oldUsage = this.usageMap.get(name);
            assert && assert(oldUsage.resourceSlot === slot);
            const combinedType = bindingType.combined(oldUsage.bindingType);
            assert && assert(combinedType);
            arrayRemove(this.usages, oldUsage);
            usage = new ResourceUsage(slot, combinedType);
        }
        else {
            usage = new ResourceUsage(slot, bindingType);
        }
        this.usageMap.set(name, usage);
        this.usages.push(usage);
    }
    toString(pipelineLayout) {
        return [
            ...Array.from(this.usageMap.keys()).map(name => {
                const usage = this.usageMap.get(name);
                const binding = pipelineLayout.getBindingFromSlot(usage.resourceSlot);
                // NOTE: type declaration should NOT create another usage. We will already have created bind group layouts
                // based on the usages at this point, so referencing ANOTHER slot would cause major issues.
                return binding.getWGSLDeclaration(name).withBlueprint(this);
            }),
            ...this.declarations.map(declaration => declaration.declarations)
        ].join('\n');
    }
    toDebugString() {
        return `PipelineBlueprint["${this.name}" log:${this.log}]\n${Array.from(this.usageMap.keys()).map(name => {
            const usage = this.usageMap.get(name);
            return `  ${name}: ${usage.toDebugString()}`;
        }).join('\n')}`;
    }
}
// TODO: oh no, we need to put the atomic in here(!)
// TODO: Or actually, just an ability to put structs of arbitrary types in ConcreteTypes
PipelineBlueprint.LOG_BUFFER_SLOT = new BufferSlot(getCastedType(getArrayType(U32Type, 2 << 24, 0), wgslWith(wgsl `_Log`, new WGSLModule('_Log', wgsl `
      struct _Log {
        next_space: atomic<u32>,
        data: array<u32>
      };
    `))));
export default PipelineBlueprint;
alpenglow.register('PipelineBlueprint', PipelineBlueprint);
class WGSLInternalDeclaration {
    constructor(name, declarations) {
        this.name = name;
        this.declarations = declarations;
    }
}
//# sourceMappingURL=PipelineBlueprint.js.map