import _ from 'lodash';
import '../../../assert/js/assert.js';
import '../../../globals.js';
// Copyright 2024, University of Colorado Boulder
/**
 * Responsible for recording GPU commands globally, so we can play them back later.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import { alpenglow } from '../imports.js';
import arrayRemove from '../../../phet-core/js/arrayRemove.js';
export default class WebGPURecorder {
    constructor() {
        this.commandLists = [];
    }
    start() {
        const commandList = new WebGPUCommandList();
        this.commandLists.push(commandList);
        return commandList;
    }
    stop(commandList) {
        assert && assert(this.commandLists.includes(commandList));
        arrayRemove(this.commandLists, commandList);
    }
    recordCommand(command) {
        for (let i = 0; i < this.commandLists.length; i++) {
            this.commandLists[i].push(command);
        }
    }
    recordGetAdapter(result, options) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandGetAdapter(result, options));
        }
    }
    recordAdapterRequestDevice(result, adapter, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandAdapterRequestDevice(result, adapter, descriptor));
        }
    }
    recordDeviceCreateBuffer(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateBuffer(result, device, descriptor));
        }
    }
    recordDeviceCreateQuerySet(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateQuerySet(result, device, descriptor));
        }
    }
    recordDeviceCreateBindGroupLayout(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateBindGroupLayout(result, device, descriptor));
        }
    }
    recordDeviceCreatePipelineLayout(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreatePipelineLayout(result, device, descriptor));
        }
    }
    recordDeviceCreateShaderModule(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateShaderModule(result, device, descriptor));
        }
    }
    recordDeviceCreateComputePipeline(result, device, descriptor, async) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateComputePipeline(result, device, descriptor, async));
        }
    }
    recordDeviceCreateBindGroup(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateBindGroup(result, device, descriptor));
        }
    }
    recordDeviceCreateCommandEncoder(result, device, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceCreateCommandEncoder(result, device, descriptor));
        }
    }
    recordDeviceWriteBuffer(device, buffer, bufferOffset, data, dataOffset, size) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceWriteBuffer(device, buffer, bufferOffset, data, dataOffset, size));
        }
    }
    recordDeviceQueueSubmit(device, commandBuffers) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceQueueSubmit(device, commandBuffers));
        }
    }
    recordDeviceDestroy(device) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandDeviceDestroy(device));
        }
    }
    recordBufferMapAsync(buffer, mode, offset, size) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandBufferMapAsync(buffer, mode, offset, size));
        }
    }
    recordBufferUnmap(buffer) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandBufferUnmap(buffer));
        }
    }
    recordBufferDestroy(buffer) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandBufferDestroy(buffer));
        }
    }
    recordEncoderBeginRenderPass(result, commandEncoder, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderBeginRenderPass(result, commandEncoder, descriptor));
        }
    }
    recordEncoderBeginComputePass(result, commandEncoder, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderBeginComputePass(result, commandEncoder, descriptor));
        }
    }
    recordEncoderCopyBufferToBuffer(encoder, source, sourceOffset, destination, destinationOffset, size) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderCopyBufferToBuffer(encoder, source, sourceOffset, destination, destinationOffset, size));
        }
    }
    recordEncoderCopyBufferToTexture(encoder, source, destination, copySize) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderCopyBufferToTexture(encoder, source, destination, copySize));
        }
    }
    recordEncoderCopyTextureToBuffer(encoder, source, destination, copySize) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderCopyTextureToBuffer(encoder, source, destination, copySize));
        }
    }
    recordEncoderCopyTextureToTexture(encoder, source, destination, copySize) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderCopyTextureToTexture(encoder, source, destination, copySize));
        }
    }
    recordEncoderClearBuffer(encoder, buffer, offset, size) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderClearBuffer(encoder, buffer, offset, size));
        }
    }
    recordEncoderResolveQuerySet(encoder, querySet, firstQuery, queryCount, destination, destinationOffset) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderResolveQuerySet(encoder, querySet, firstQuery, queryCount, destination, destinationOffset));
        }
    }
    recordEncoderFinish(result, encoder, descriptor) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandEncoderFinish(result, encoder, descriptor));
        }
    }
    recordComputePassEncoderSetPipeline(encoder, pipeline) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandComputePassEncoderSetPipeline(encoder, pipeline));
        }
    }
    recordComputePassEncoderDispatchWorkgroups(encoder, x, y, z) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandComputePassEncoderDispatchWorkgroups(encoder, x, y, z));
        }
    }
    recordComputePassEncoderDispatchWorkgroupsIndirect(encoder, indirectBuffer, indirectOffset) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandComputePassEncoderDispatchWorkgroupsIndirect(encoder, indirectBuffer, indirectOffset));
        }
    }
    recordComputePassEncoderEnd(encoder) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandComputePassEncoderEnd(encoder));
        }
    }
    recordPassEncoderSetBindGroup(passEncoder, index, bindGroup, dynamicOffsets) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandPassEncoderSetBindGroup(passEncoder, index, bindGroup, dynamicOffsets));
        }
    }
    recordQuerySetDestroy(querySet) {
        if (this.commandLists.length) {
            this.recordCommand(new WebGPUCommandQuerySetDestroy(querySet));
        }
    }
    static getNamePrefix(obj) {
        if (obj instanceof GPUDevice) {
            return 'device';
        }
        else if (obj instanceof GPUAdapter) {
            return 'adapter';
        }
        else if (obj instanceof GPUBuffer) {
            return 'buffer';
        }
        else if (obj instanceof GPUQuerySet) {
            return 'querySet';
        }
        else if (obj instanceof GPUShaderModule) {
            return 'shaderModule';
        }
        else if (obj instanceof GPUComputePipeline) {
            return 'computePipeline';
        }
        else if (obj instanceof GPUBindGroup) {
            return 'bindGroup';
        }
        else if (obj instanceof GPUBindGroupLayout) {
            return 'bindGroupLayout';
        }
        else if (obj instanceof GPUPipelineLayout) {
            return 'pipelineLayout';
        }
        else if (obj instanceof GPUCommandEncoder) {
            return 'commandEncoder';
        }
        else if (obj instanceof GPURenderPassEncoder) {
            return 'renderPassEncoder';
        }
        else if (obj instanceof GPUComputePassEncoder) {
            return 'computePassEncoder';
        }
        else if (obj instanceof GPUTextureView) {
            return 'textureView';
        }
        else if (obj instanceof GPUSampler) {
            return 'sampler';
        }
        else if (obj instanceof GPUExternalTexture) {
            return 'externalTexture';
        }
        else if (obj instanceof GPUCommandBuffer) {
            return 'commandBuffer';
        }
        else {
            throw new Error('add the name');
        }
    }
    static createNameMap(objects) {
        objects = _.uniq(objects);
        const prefixTotalCountMap = new Map();
        objects.forEach(obj => {
            const prefix = WebGPURecorder.getNamePrefix(obj);
            const count = prefixTotalCountMap.get(prefix) || 0;
            prefixTotalCountMap.set(prefix, count + 1);
        });
        const map = new Map();
        const prefixCountMap = new Map();
        objects.forEach(obj => {
            const prefix = WebGPURecorder.getNamePrefix(obj);
            const hasMultiples = prefixTotalCountMap.get(prefix) > 1;
            if (hasMultiples) {
                const number = prefixCountMap.get(prefix) || 0;
                prefixCountMap.set(prefix, number + 1);
                map.set(obj, `${prefix}${number}`);
            }
            else {
                map.set(obj, prefix);
            }
        });
        return map;
    }
    // TODO: consider how we're handling this
    static arrayBufferLikeString(data) {
        return `new Uint8Array( [ ${new Uint8Array(data).join(', ')} ] ).buffer`;
    }
    static objectToString(level, map) {
        const definedKeys = Object.keys(map).filter(key => map[key] !== undefined);
        if (definedKeys.length === 0) {
            return '{}';
        }
        else if (definedKeys.length === 1) {
            return `{ ${definedKeys[0]}: ${map[definedKeys[0]]} }`;
        }
        else {
            return `{\n${definedKeys.map(key => `${'  '.repeat(level + 1)}${key}: ${map[key]}`).join(',\n')}\n${'  '.repeat(level)}}`;
        }
    }
    static rawValue(level, value, nameMap, objectOverrides) {
        if (nameMap && nameMap.has(value)) {
            return nameMap.get(value);
        }
        else if (typeof value === 'number') {
            return `${value}`;
        }
        else if (typeof value === 'string') {
            if (value.includes('\n')) {
                return `\`${value.replace(/`/g, '\\`')}\``;
            }
            else {
                return `'${value.replace(/'/g, '\\\'')}'`;
            }
        }
        else if (value === undefined) {
            return undefined;
        }
        else if (value === null) {
            return 'null';
        }
        else if (Array.isArray(value)) {
            if (value.length === 0) {
                return '[]';
            }
            else if (value.length === 1) {
                return `[ ${WebGPURecorder.rawValue(level, value[0], nameMap)} ]`;
            }
            else {
                const getValue = (item) => {
                    if (objectOverrides && objectOverrides.arrayElement) {
                        return objectOverrides.arrayElement(item);
                    }
                    else {
                        return WebGPURecorder.rawValue(level + 1, item, nameMap);
                    }
                };
                return `[\n${value.map(item => `${'  '.repeat(level + 1)}${getValue(item)}`).join(',\n')}\n${'  '.repeat(level)}]`;
            }
        }
        else {
            const map = {};
            Object.keys(value).forEach(key => {
                if (objectOverrides && objectOverrides[key]) {
                    map[key] = objectOverrides[key](value[key]);
                }
                else {
                    map[key] = WebGPURecorder.rawValue(level + 1, value[key], nameMap);
                }
            });
            return WebGPURecorder.objectToString(level, map);
        }
    }
    static bitfieldToString(bitfield, nameMap) {
        const usedNames = [];
        nameMap.forEach((name, value) => {
            if (bitfield & value) {
                usedNames.push(name);
                bitfield ^= value;
            }
        });
        assert && assert(bitfield === 0, 'bitfield should be empty');
        if (usedNames.length === 0) {
            return '0';
        }
        else {
            return usedNames.join(' | ');
        }
    }
}
alpenglow.register('WebGPURecorder', WebGPURecorder);
export class WebGPUCommandList {
    constructor(commands = []) {
        this.commands = commands;
    }
    push(command) {
        this.commands.push(command);
    }
    getDeclaredObjects() {
        const objects = [];
        this.commands.forEach(command => {
            if (command.result) {
                objects.push(command.result);
            }
        });
        return objects;
    }
    getUnboundObjects(declaredObjects = this.getDeclaredObjects()) {
        const objects = [];
        this.commands.forEach(command => {
            command.dependencies.forEach(dependency => {
                if (!declaredObjects.includes(dependency)) {
                    objects.push(dependency);
                }
            });
        });
        return _.uniq(objects);
    }
    getObjects() {
        const declaredObjects = this.getDeclaredObjects();
        const unboundObjects = this.getUnboundObjects(declaredObjects);
        // uniq for sanity check
        return _.uniq([...unboundObjects, ...declaredObjects]);
    }
    getNameMap() {
        return WebGPURecorder.createNameMap(this.getObjects());
    }
    toJS(nameMap = this.getNameMap(), level = 0) {
        return this.commands.map(command => `${'  '.repeat(level)}${command.toJS(nameMap, level)}`).join('\n');
    }
    toJSClosure(nameMap = this.getNameMap(), level = 0) {
        const unboundObjects = this.getUnboundObjects();
        return `async (${unboundObjects.length ? ` ${unboundObjects.map(unboundObject => {
            return `${nameMap.get(unboundObject)}: ${unboundObject.constructor.name}`;
        }).join(', ')} ` : ''}) => {\n${this.toJS(nameMap, level + 1)}\n}`;
    }
}
alpenglow.register('WebGPUCommandList', WebGPUCommandList);
const getName = (nameMap, obj) => {
    const name = nameMap.get(obj);
    assert && assert(name);
    return name;
};
export class WebGPUCommand {
    constructor(result, dependencies) {
        this.result = result;
        this.dependencies = dependencies;
    }
    getDeclaration(nameMap) {
        if (this.result !== null) {
            return `const ${getName(nameMap, this.result)} = `;
        }
        else {
            return '';
        }
    }
}
alpenglow.register('WebGPUCommand', WebGPUCommand);
class WebGPUCommandGetAdapter extends WebGPUCommand {
    constructor(result, options) {
        super(result, []);
        this.options = options;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}await navigator.gpu?.requestAdapter(${this.options ? ` ${WebGPURecorder.rawValue(level, this.options, nameMap)} ` : ''});`;
    }
}
class WebGPUCommandAdapterRequestDevice extends WebGPUCommand {
    constructor(result, adapter, descriptor) {
        super(result, []);
        this.adapter = adapter;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.adapter)}.requestDevice(${this.descriptor ? ` ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} ` : ''});`;
    }
}
class WebGPUCommandDeviceCreateBuffer extends WebGPUCommand {
    constructor(result, device, descriptor) {
        super(result, [device]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createBuffer( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap, {
            usage: (value) => {
                const numberValue = value;
                // eslint-disable-next-line phet/no-simple-type-checking-assertions
                assert && assert(typeof value === 'number');
                return WebGPURecorder.bitfieldToString(numberValue, new Map([
                    [GPUBufferUsage.MAP_READ, 'GPUBufferUsage.MAP_READ'],
                    [GPUBufferUsage.MAP_WRITE, 'GPUBufferUsage.MAP_WRITE'],
                    [GPUBufferUsage.COPY_SRC, 'GPUBufferUsage.COPY_SRC'],
                    [GPUBufferUsage.COPY_DST, 'GPUBufferUsage.COPY_DST'],
                    [GPUBufferUsage.INDEX, 'GPUBufferUsage.INDEX'],
                    [GPUBufferUsage.VERTEX, 'GPUBufferUsage.VERTEX'],
                    [GPUBufferUsage.UNIFORM, 'GPUBufferUsage.UNIFORM'],
                    [GPUBufferUsage.STORAGE, 'GPUBufferUsage.STORAGE'],
                    [GPUBufferUsage.INDIRECT, 'GPUBufferUsage.INDIRECT'],
                    [GPUBufferUsage.QUERY_RESOLVE, 'GPUBufferUsage.QUERY_RESOLVE']
                ]));
            }
        })} );`;
    }
}
class WebGPUCommandDeviceCreateQuerySet extends WebGPUCommand {
    constructor(result, device, descriptor) {
        super(result, [device]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createQuerySet( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} );`;
    }
}
class WebGPUCommandDeviceCreateBindGroupLayout extends WebGPUCommand {
    constructor(result, device, descriptor) {
        super(result, [device]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createBindGroupLayout( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap, {
            entries: (value) => {
                return WebGPURecorder.rawValue(level + 1, value, nameMap, {
                    arrayElement: (value) => {
                        return WebGPURecorder.rawValue(level + 2, value, nameMap, {
                            visibility: (value) => WebGPURecorder.bitfieldToString(value, new Map([
                                [GPUShaderStage.VERTEX, 'GPUShaderStage.VERTEX'],
                                [GPUShaderStage.FRAGMENT, 'GPUShaderStage.FRAGMENT'],
                                [GPUShaderStage.COMPUTE, 'GPUShaderStage.COMPUTE']
                            ]))
                        });
                    }
                });
            }
        })} );`;
    }
}
class WebGPUCommandDeviceCreatePipelineLayout extends WebGPUCommand {
    constructor(result, device, descriptor) {
        super(result, [device, ...descriptor.bindGroupLayouts]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createPipelineLayout( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} );`;
    }
}
class WebGPUCommandDeviceCreateShaderModule extends WebGPUCommand {
    constructor(result, device, descriptor) {
        const compilationHintLayouts = descriptor.compilationHints ? descriptor.compilationHints.map(hint => hint.layout).filter(hint => hint && hint !== 'auto') : [];
        super(result, [device, ...compilationHintLayouts]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createShaderModule( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} );`;
    }
}
class WebGPUCommandDeviceCreateComputePipeline extends WebGPUCommand {
    constructor(result, device, descriptor, async) {
        const module = descriptor.compute.module;
        const layout = descriptor.layout;
        super(result, [device, module, ...(layout === 'auto' ? [] : [layout])]);
        this.device = device;
        this.descriptor = descriptor;
        this.async = async;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${this.async ? 'await ' : ''}${getName(nameMap, this.device)}.createComputePipeline${this.async ? 'Async' : ''}( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} );`;
    }
}
class WebGPUCommandDeviceCreateBindGroup extends WebGPUCommand {
    constructor(result, device, descriptor) {
        const resources = [...descriptor.entries].map(entry => {
            const resource = entry.resource;
            if (resource instanceof GPUSampler || resource instanceof GPUTextureView || resource instanceof GPUExternalTexture) {
                return resource;
            }
            else {
                return resource.buffer;
            }
        });
        super(result, [device, descriptor.layout, ...resources]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createBindGroup( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} );`;
    }
}
class WebGPUCommandDeviceCreateCommandEncoder extends WebGPUCommand {
    constructor(result, device, descriptor) {
        super(result, [device]);
        this.device = device;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.device)}.createCommandEncoder(${this.descriptor ? ` ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} ` : ''});`;
    }
}
class WebGPUCommandDeviceWriteBuffer extends WebGPUCommand {
    constructor(device, buffer, bufferOffset, data, dataOffset, size) {
        super(null, [device, buffer]);
        this.device = device;
        this.buffer = buffer;
        this.bufferOffset = bufferOffset;
        this.data = data;
        this.dataOffset = dataOffset;
        this.size = size;
    }
    toJS(nameMap, level = 0) {
        let dataString;
        if (this.data instanceof ArrayBuffer) {
            dataString = WebGPURecorder.arrayBufferLikeString(this.data);
        }
        else if (this.data instanceof SharedArrayBuffer) {
            dataString = WebGPURecorder.arrayBufferLikeString(this.data);
        }
        else {
            dataString = WebGPURecorder.arrayBufferLikeString(this.data.buffer);
        }
        return `${getName(nameMap, this.device)}.queue.writeBuffer( ${getName(nameMap, this.buffer)}, ${this.bufferOffset}, ${dataString}${this.dataOffset !== undefined ? `, ${this.dataOffset}` : ''}${this.size !== undefined ? `, ${this.size}` : ''} );`;
    }
}
class WebGPUCommandDeviceQueueSubmit extends WebGPUCommand {
    constructor(device, commandBuffers) {
        super(null, [device, ...commandBuffers]);
        this.device = device;
        this.commandBuffers = commandBuffers;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.device)}.queue.submit( ${WebGPURecorder.rawValue(0, [
            ...this.commandBuffers
        ], nameMap)} );`;
    }
}
class WebGPUCommandDeviceDestroy extends WebGPUCommand {
    constructor(device) {
        super(null, [device]);
        this.device = device;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.device)}.destroy();`;
    }
}
class WebGPUCommandBufferMapAsync extends WebGPUCommand {
    constructor(buffer, mode, offset, size) {
        super(null, [buffer]);
        this.buffer = buffer;
        this.mode = mode;
        this.offset = offset;
        this.size = size;
    }
    toJS(nameMap, level = 0) {
        const modeString = WebGPURecorder.bitfieldToString(this.mode, new Map([
            [GPUMapMode.READ, 'GPUMapMode.READ'],
            [GPUMapMode.WRITE, 'GPUMapMode.WRITE']
        ]));
        return `${getName(nameMap, this.buffer)}.mapAsync( ${modeString}${this.offset !== undefined ? `, ${this.offset}` : ''}${this.size !== undefined ? `, ${this.size}` : ''} ).then( () => console.log( new Uint8Array( ${getName(nameMap, this.buffer)}.getMappedRange() ) ) );`;
    }
}
class WebGPUCommandBufferUnmap extends WebGPUCommand {
    constructor(buffer) {
        super(null, [buffer]);
        this.buffer = buffer;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.buffer)}.unmap();`;
    }
}
class WebGPUCommandBufferDestroy extends WebGPUCommand {
    constructor(buffer) {
        super(null, [buffer]);
        this.buffer = buffer;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.buffer)}.destroy();`;
    }
}
class WebGPUCommandEncoderBeginRenderPass extends WebGPUCommand {
    constructor(result, commandEncoder, descriptor) {
        super(result, [
            commandEncoder,
            [...descriptor.colorAttachments].flatMap(attachment => attachment === null ? [] : [
                attachment.view,
                ...(attachment.resolveTarget ? [attachment.resolveTarget] : [])
            ]),
            ...(descriptor.depthStencilAttachment ? [descriptor.depthStencilAttachment.view] : []),
            ...(descriptor.occlusionQuerySet ? [descriptor.occlusionQuerySet] : []),
            ...(descriptor.timestampWrites ? [descriptor.timestampWrites.querySet] : [])
        ]);
        this.commandEncoder = commandEncoder;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.commandEncoder)}.beginRenderPass( ${WebGPURecorder.rawValue(level, this.descriptor, nameMap, {})} );`;
    }
}
class WebGPUCommandEncoderBeginComputePass extends WebGPUCommand {
    constructor(result, commandEncoder, descriptor) {
        super(result, [
            commandEncoder,
            ...(descriptor && descriptor.timestampWrites ? [descriptor.timestampWrites.querySet] : [])
        ]);
        this.commandEncoder = commandEncoder;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.commandEncoder)}.beginComputePass(${this.descriptor ? ` ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} ` : ''});`;
    }
}
class WebGPUCommandEncoderCopyBufferToBuffer extends WebGPUCommand {
    constructor(encoder, source, sourceOffset, destination, destinationOffset, size) {
        super(null, [encoder, source, destination]);
        this.encoder = encoder;
        this.source = source;
        this.sourceOffset = sourceOffset;
        this.destination = destination;
        this.destinationOffset = destinationOffset;
        this.size = size;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.copyBufferToBuffer( ${getName(nameMap, this.source)}, ${this.sourceOffset}, ${getName(nameMap, this.destination)}, ${this.destinationOffset}, ${this.size} );`;
    }
}
class WebGPUCommandEncoderCopyBufferToTexture extends WebGPUCommand {
    constructor(encoder, source, destination, copySize) {
        super(null, [encoder, source, destination]);
        this.encoder = encoder;
        this.source = source;
        this.destination = destination;
        this.copySize = copySize;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.copyBufferToTexture( ${getName(nameMap, this.source)}, ${getName(nameMap, this.destination)}, ${WebGPURecorder.rawValue(level, this.copySize, nameMap)} );`;
    }
}
class WebGPUCommandEncoderCopyTextureToBuffer extends WebGPUCommand {
    constructor(encoder, source, destination, copySize) {
        super(null, [encoder, source, destination]);
        this.encoder = encoder;
        this.source = source;
        this.destination = destination;
        this.copySize = copySize;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.copyTextureToBuffer( ${getName(nameMap, this.source)}, ${getName(nameMap, this.destination)}, ${WebGPURecorder.rawValue(level, this.copySize, nameMap)} );`;
    }
}
class WebGPUCommandEncoderCopyTextureToTexture extends WebGPUCommand {
    constructor(encoder, source, destination, copySize) {
        super(null, [encoder, source, destination]);
        this.encoder = encoder;
        this.source = source;
        this.destination = destination;
        this.copySize = copySize;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.copyTextureToTexture( ${getName(nameMap, this.source)}, ${getName(nameMap, this.destination)}, ${WebGPURecorder.rawValue(level, this.copySize, nameMap)} );`;
    }
}
class WebGPUCommandEncoderClearBuffer extends WebGPUCommand {
    constructor(encoder, buffer, offset, size) {
        super(null, [encoder, buffer]);
        this.encoder = encoder;
        this.buffer = buffer;
        this.offset = offset;
        this.size = size;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.clearBuffer( ${getName(nameMap, this.buffer)}${this.offset !== undefined ? `, ${this.offset}` : ''}${this.size !== undefined ? `, ${this.size}` : ''} );`;
    }
}
class WebGPUCommandEncoderResolveQuerySet extends WebGPUCommand {
    constructor(encoder, querySet, firstQuery, queryCount, destination, destinationOffset) {
        super(null, [encoder, querySet, destination]);
        this.encoder = encoder;
        this.querySet = querySet;
        this.firstQuery = firstQuery;
        this.queryCount = queryCount;
        this.destination = destination;
        this.destinationOffset = destinationOffset;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.encoder)}.resolveQuerySet( ${getName(nameMap, this.querySet)}, ${this.firstQuery}, ${this.queryCount}, ${getName(nameMap, this.destination)}, ${this.destinationOffset} );`;
    }
}
class WebGPUCommandEncoderFinish extends WebGPUCommand {
    constructor(result, encoder, descriptor) {
        super(result, [encoder]);
        this.encoder = encoder;
        this.descriptor = descriptor;
    }
    toJS(nameMap, level = 0) {
        return `${this.getDeclaration(nameMap)}${getName(nameMap, this.encoder)}.finish(${this.descriptor ? ` ${WebGPURecorder.rawValue(level, this.descriptor, nameMap)} ` : ''});`;
    }
}
class WebGPUCommandComputePassEncoderSetPipeline extends WebGPUCommand {
    constructor(computePassEncoder, pipeline) {
        super(null, [computePassEncoder, pipeline]);
        this.computePassEncoder = computePassEncoder;
        this.pipeline = pipeline;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.computePassEncoder)}.setPipeline( ${getName(nameMap, this.pipeline)} );`;
    }
}
class WebGPUCommandComputePassEncoderDispatchWorkgroups extends WebGPUCommand {
    constructor(computePassEncoder, x, y, z) {
        super(null, [computePassEncoder]);
        this.computePassEncoder = computePassEncoder;
        this.x = x;
        this.y = y;
        this.z = z;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.computePassEncoder)}.dispatchWorkgroups( ${this.x}${this.y !== undefined ? `, ${this.y}` : ''}${this.z !== undefined ? `, ${this.z}` : ''} );`;
    }
}
class WebGPUCommandComputePassEncoderDispatchWorkgroupsIndirect extends WebGPUCommand {
    constructor(computePassEncoder, indirectBuffer, indirectOffset) {
        super(null, [computePassEncoder, indirectBuffer]);
        this.computePassEncoder = computePassEncoder;
        this.indirectBuffer = indirectBuffer;
        this.indirectOffset = indirectOffset;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.computePassEncoder)}.dispatchWorkgroupsIndirect( ${getName(nameMap, this.indirectBuffer)}, ${this.indirectOffset} );`;
    }
}
class WebGPUCommandComputePassEncoderEnd extends WebGPUCommand {
    constructor(computePassEncoder) {
        super(null, [computePassEncoder]);
        this.computePassEncoder = computePassEncoder;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.computePassEncoder)}.end();`;
    }
}
class WebGPUCommandPassEncoderSetBindGroup extends WebGPUCommand {
    constructor(passEncoder, index, bindGroup, dynamicOffsets) {
        super(null, [passEncoder]);
        this.passEncoder = passEncoder;
        this.index = index;
        this.bindGroup = bindGroup;
        this.dynamicOffsets = dynamicOffsets;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.passEncoder)}.setBindGroup( ${this.index}, ${this.bindGroup === null ? 'null' : getName(nameMap, this.bindGroup)}${this.dynamicOffsets ? `, ${WebGPURecorder.rawValue(level, this.dynamicOffsets, nameMap)}` : ''} );`;
    }
}
class WebGPUCommandQuerySetDestroy extends WebGPUCommand {
    constructor(querySet) {
        super(null, [querySet]);
        this.querySet = querySet;
    }
    toJS(nameMap, level = 0) {
        return `${getName(nameMap, this.querySet)}.destroy();`;
    }
}
// TODO: add more render commands (we're missing a lot).
//# sourceMappingURL=WebGPURecorder.js.map