import InterfaceTypes from '../../interface-types/interface-types.js';
import bufferToTypedArray from '../../buffer-to-typed-array.js';
import FloatTypes from '../../interface-types/float-types.js';
import IntTypes from '../../interface-types/int-types.js';
const haveSharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function';
const encoder = new TextEncoder();
const decoder = new TextDecoder('utf-8');
function readFileSharedArray(emscriptenModule, path) {
    const opts = { flags: 'r', encoding: 'binary' };
    const stream = emscriptenModule.fs_open(path, opts.flags);
    const stat = emscriptenModule.fs_stat(path);
    const length = stat.size;
    let arrayBufferData = null;
    if (haveSharedArrayBuffer) {
        arrayBufferData = new SharedArrayBuffer(length); // eslint-disable-line
    }
    else {
        arrayBufferData = new ArrayBuffer(length);
    }
    const array = new Uint8Array(arrayBufferData);
    emscriptenModule.fs_read(stream, array, 0, length, 0);
    emscriptenModule.fs_close(stream);
    return array;
}
function memoryUint8SharedArray(emscriptenModule, byteOffset, length) {
    let arrayBufferData = null;
    if (haveSharedArrayBuffer) {
        arrayBufferData = new SharedArrayBuffer(length); // eslint-disable-line
    }
    else {
        arrayBufferData = new ArrayBuffer(length);
    }
    const array = new Uint8Array(arrayBufferData);
    const dataArrayView = new Uint8Array(emscriptenModule.HEAPU8.buffer, byteOffset, length);
    array.set(dataArrayView);
    return array;
}
function setPipelineModuleInputArray(emscriptenModule, dataArray, inputIndex, subIndex) {
    let dataPtr = 0;
    if (dataArray !== null) {
        dataPtr = emscriptenModule.ccall('itk_wasm_input_array_alloc', 'number', ['number', 'number', 'number', 'number'], [0, inputIndex, subIndex, dataArray.buffer.byteLength]);
        emscriptenModule.HEAPU8.set(new Uint8Array(dataArray.buffer), dataPtr);
    }
    return dataPtr;
}
function setPipelineModuleInputJSON(emscriptenModule, dataObject, inputIndex) {
    const dataJSON = JSON.stringify(dataObject);
    const length = emscriptenModule.lengthBytesUTF8(dataJSON) + 1;
    const jsonPtr = emscriptenModule.ccall('itk_wasm_input_json_alloc', 'number', ['number', 'number', 'number'], [0, inputIndex, length]);
    emscriptenModule.stringToUTF8(dataJSON, jsonPtr, length);
}
function getPipelineModuleOutputArray(emscriptenModule, outputIndex, subIndex, componentType) {
    const dataPtr = emscriptenModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, outputIndex, subIndex]);
    const dataSize = emscriptenModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, outputIndex, subIndex]);
    const dataUint8 = memoryUint8SharedArray(emscriptenModule, dataPtr, dataSize);
    const data = bufferToTypedArray(componentType, dataUint8.buffer);
    return data;
}
function getPipelineModuleOutputJSON(emscriptenModule, outputIndex) {
    const jsonPtr = emscriptenModule.ccall('itk_wasm_output_json_address', 'number', ['number', 'number'], [0, outputIndex]);
    const dataJSON = emscriptenModule.UTF8ToString(jsonPtr);
    const dataObject = JSON.parse(dataJSON);
    return dataObject;
}
function runPipelineEmscripten(pipelineModule, args, outputs, inputs) {
    if (!(inputs == null) && inputs.length > 0) {
        inputs.forEach(function (input, index) {
            switch (input.type) {
                case InterfaceTypes.TextStream:
                    {
                        const dataArray = encoder.encode(input.data.data);
                        const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
                        const dataJSON = { size: dataArray.buffer.byteLength, data: `data:application/vnd.itk.address,0:${arrayPtr}` };
                        setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
                        break;
                    }
                case InterfaceTypes.JsonCompatible:
                    {
                        const dataArray = encoder.encode(JSON.stringify(input.data));
                        const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
                        const dataJSON = { size: dataArray.buffer.byteLength, data: `data:application/vnd.itk.address,0:${arrayPtr}` };
                        setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
                        break;
                    }
                case InterfaceTypes.BinaryStream:
                    {
                        const dataArray = input.data.data;
                        const arrayPtr = setPipelineModuleInputArray(pipelineModule, dataArray, index, 0);
                        const dataJSON = { size: dataArray.buffer.byteLength, data: `data:application/vnd.itk.address,0:${arrayPtr}` };
                        setPipelineModuleInputJSON(pipelineModule, dataJSON, index);
                        break;
                    }
                case InterfaceTypes.TextFile:
                    {
                        pipelineModule.fs_writeFile(input.data.path, input.data.data);
                        break;
                    }
                case InterfaceTypes.BinaryFile:
                    {
                        pipelineModule.fs_writeFile(input.data.path, input.data.data);
                        break;
                    }
                case InterfaceTypes.Image:
                    {
                        const image = input.data;
                        const dataPtr = setPipelineModuleInputArray(pipelineModule, image.data, index, 0);
                        const directionPtr = setPipelineModuleInputArray(pipelineModule, image.direction, index, 1);
                        const metadata = typeof image.metadata?.entries !== 'undefined' ? JSON.stringify(Array.from(image.metadata.entries())) : '[]';
                        const imageJSON = {
                            imageType: image.imageType,
                            name: image.name,
                            origin: image.origin,
                            spacing: image.spacing,
                            direction: `data:application/vnd.itk.address,0:${directionPtr}`,
                            size: image.size,
                            data: `data:application/vnd.itk.address,0:${dataPtr}`,
                            metadata
                        };
                        setPipelineModuleInputJSON(pipelineModule, imageJSON, index);
                        break;
                    }
                case InterfaceTypes.Mesh:
                    {
                        const mesh = input.data;
                        const pointsPtr = setPipelineModuleInputArray(pipelineModule, mesh.points, index, 0);
                        const cellsPtr = setPipelineModuleInputArray(pipelineModule, mesh.cells, index, 1);
                        const pointDataPtr = setPipelineModuleInputArray(pipelineModule, mesh.pointData, index, 2);
                        const cellDataPtr = setPipelineModuleInputArray(pipelineModule, mesh.cellData, index, 3);
                        const meshJSON = {
                            meshType: mesh.meshType,
                            name: mesh.name,
                            numberOfPoints: mesh.numberOfPoints,
                            points: `data:application/vnd.itk.address,0:${pointsPtr}`,
                            numberOfCells: mesh.numberOfCells,
                            cells: `data:application/vnd.itk.address,0:${cellsPtr}`,
                            cellBufferSize: mesh.cellBufferSize,
                            numberOfPointPixels: mesh.numberOfPointPixels,
                            pointData: `data:application/vnd.itk.address,0:${pointDataPtr}`,
                            numberOfCellPixels: mesh.numberOfCellPixels,
                            cellData: `data:application/vnd.itk.address,0:${cellDataPtr}`
                        };
                        setPipelineModuleInputJSON(pipelineModule, meshJSON, index);
                        break;
                    }
                case InterfaceTypes.PolyData:
                    {
                        const polyData = input.data;
                        const pointsPtr = setPipelineModuleInputArray(pipelineModule, polyData.points, index, 0);
                        const verticesPtr = setPipelineModuleInputArray(pipelineModule, polyData.vertices, index, 1);
                        const linesPtr = setPipelineModuleInputArray(pipelineModule, polyData.lines, index, 2);
                        const polygonsPtr = setPipelineModuleInputArray(pipelineModule, polyData.polygons, index, 3);
                        const triangleStripsPtr = setPipelineModuleInputArray(pipelineModule, polyData.triangleStrips, index, 4);
                        const pointDataPtr = setPipelineModuleInputArray(pipelineModule, polyData.pointData, index, 5);
                        const cellDataPtr = setPipelineModuleInputArray(pipelineModule, polyData.pointData, index, 6);
                        const polyDataJSON = {
                            polyDataType: polyData.polyDataType,
                            name: polyData.name,
                            numberOfPoints: polyData.numberOfPoints,
                            points: `data:application/vnd.itk.address,0:${pointsPtr}`,
                            verticesBufferSize: polyData.verticesBufferSize,
                            vertices: `data:application/vnd.itk.address,0:${verticesPtr}`,
                            linesBufferSize: polyData.linesBufferSize,
                            lines: `data:application/vnd.itk.address,0:${linesPtr}`,
                            polygonsBufferSize: polyData.polygonsBufferSize,
                            polygons: `data:application/vnd.itk.address,0:${polygonsPtr}`,
                            triangleStripsBufferSize: polyData.triangleStripsBufferSize,
                            triangleStrips: `data:application/vnd.itk.address,0:${triangleStripsPtr}`,
                            numberOfPointPixels: polyData.numberOfPointPixels,
                            pointData: `data:application/vnd.itk.address,0:${pointDataPtr}`,
                            numberOfCellPixels: polyData.numberOfCellPixels,
                            cellData: `data:application/vnd.itk.address,0:${cellDataPtr}`
                        };
                        setPipelineModuleInputJSON(pipelineModule, polyDataJSON, index);
                        break;
                    }
                default:
                    throw Error('Unsupported input InterfaceType');
            }
        });
    }
    pipelineModule.resetModuleStdout();
    pipelineModule.resetModuleStderr();
    const stackPtr = pipelineModule.stackSave();
    let returnValue = 0;
    try {
        returnValue = pipelineModule.callMain(args.slice());
    }
    catch (exception) {
        // Note: Module must be built with CMAKE_BUILD_TYPE set to Debug.
        // e.g.: itk-wasm build my/project -- -DCMAKE_BUILD_TYPE:STRING=Debug
        if (typeof exception === 'number') {
            console.log('Exception while running pipeline:');
            console.log('stdout:', pipelineModule.getModuleStdout());
            console.error('stderr:', pipelineModule.getModuleStderr());
            if (typeof pipelineModule.getExceptionMessage !== 'undefined') {
                console.error('exception:', pipelineModule.getExceptionMessage(exception));
            }
            else {
                console.error('Build module in Debug mode for exception message information.');
            }
        }
        throw exception;
    }
    finally {
        pipelineModule.stackRestore(stackPtr);
    }
    const stdout = pipelineModule.getModuleStdout();
    const stderr = pipelineModule.getModuleStderr();
    const populatedOutputs = [];
    if (!(outputs == null) && outputs.length > 0 && returnValue === 0) {
        outputs.forEach(function (output, index) {
            let outputData = null;
            switch (output.type) {
                case InterfaceTypes.TextStream:
                    {
                        const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        const dataArrayView = new Uint8Array(pipelineModule.HEAPU8.buffer, dataPtr, dataSize);
                        outputData = { data: decoder.decode(dataArrayView) };
                        break;
                    }
                case InterfaceTypes.JsonCompatible:
                    {
                        const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        const dataArrayView = new Uint8Array(pipelineModule.HEAPU8.buffer, dataPtr, dataSize);
                        outputData = JSON.parse(decoder.decode(dataArrayView));
                        break;
                    }
                case InterfaceTypes.BinaryStream:
                    {
                        const dataPtr = pipelineModule.ccall('itk_wasm_output_array_address', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        const dataSize = pipelineModule.ccall('itk_wasm_output_array_size', 'number', ['number', 'number', 'number'], [0, index, 0]);
                        outputData = { data: memoryUint8SharedArray(pipelineModule, dataPtr, dataSize) };
                        break;
                    }
                case InterfaceTypes.TextFile:
                    {
                        outputData = { path: output.data.path, data: pipelineModule.fs_readFile(output.data.path, { encoding: 'utf8' }) };
                        break;
                    }
                case InterfaceTypes.BinaryFile:
                    {
                        outputData = { path: output.data.path, data: readFileSharedArray(pipelineModule, output.data.path) };
                        break;
                    }
                case InterfaceTypes.Image:
                    {
                        const image = getPipelineModuleOutputJSON(pipelineModule, index);
                        image.data = getPipelineModuleOutputArray(pipelineModule, index, 0, image.imageType.componentType);
                        image.direction = getPipelineModuleOutputArray(pipelineModule, index, 1, FloatTypes.Float64);
                        image.metadata = new Map(image.metadata);
                        outputData = image;
                        break;
                    }
                case InterfaceTypes.Mesh:
                    {
                        const mesh = getPipelineModuleOutputJSON(pipelineModule, index);
                        if (mesh.numberOfPoints > 0) {
                            mesh.points = getPipelineModuleOutputArray(pipelineModule, index, 0, mesh.meshType.pointComponentType);
                        }
                        else {
                            mesh.points = bufferToTypedArray(mesh.meshType.pointComponentType, new ArrayBuffer(0));
                        }
                        if (mesh.numberOfCells > 0) {
                            mesh.cells = getPipelineModuleOutputArray(pipelineModule, index, 1, mesh.meshType.cellComponentType);
                        }
                        else {
                            mesh.cells = bufferToTypedArray(mesh.meshType.cellComponentType, new ArrayBuffer(0));
                        }
                        if (mesh.numberOfPointPixels > 0) {
                            mesh.pointData = getPipelineModuleOutputArray(pipelineModule, index, 2, mesh.meshType.pointPixelComponentType);
                        }
                        else {
                            mesh.pointData = bufferToTypedArray(mesh.meshType.pointPixelComponentType, new ArrayBuffer(0));
                        }
                        if (mesh.numberOfCellPixels > 0) {
                            mesh.cellData = getPipelineModuleOutputArray(pipelineModule, index, 3, mesh.meshType.cellPixelComponentType);
                        }
                        else {
                            mesh.cellData = bufferToTypedArray(mesh.meshType.cellPixelComponentType, new ArrayBuffer(0));
                        }
                        outputData = mesh;
                        break;
                    }
                case InterfaceTypes.PolyData:
                    {
                        const polyData = getPipelineModuleOutputJSON(pipelineModule, index);
                        if (polyData.numberOfPoints > 0) {
                            polyData.points = getPipelineModuleOutputArray(pipelineModule, index, 0, FloatTypes.Float32);
                        }
                        else {
                            polyData.points = new Float32Array();
                        }
                        if (polyData.verticesBufferSize > 0) {
                            polyData.vertices = getPipelineModuleOutputArray(pipelineModule, index, 1, IntTypes.UInt32);
                        }
                        else {
                            polyData.vertices = new Uint32Array();
                        }
                        if (polyData.linesBufferSize > 0) {
                            polyData.lines = getPipelineModuleOutputArray(pipelineModule, index, 2, IntTypes.UInt32);
                        }
                        else {
                            polyData.lines = new Uint32Array();
                        }
                        if (polyData.polygonsBufferSize > 0) {
                            polyData.polygons = getPipelineModuleOutputArray(pipelineModule, index, 3, IntTypes.UInt32);
                        }
                        else {
                            polyData.polygons = new Uint32Array();
                        }
                        if (polyData.triangleStripsBufferSize > 0) {
                            polyData.triangleStrips = getPipelineModuleOutputArray(pipelineModule, index, 4, IntTypes.UInt32);
                        }
                        else {
                            polyData.triangleStrips = new Uint32Array();
                        }
                        if (polyData.numberOfPointPixels > 0) {
                            polyData.pointData = getPipelineModuleOutputArray(pipelineModule, index, 5, polyData.polyDataType.pointPixelComponentType);
                        }
                        else {
                            polyData.pointData = bufferToTypedArray(polyData.polyDataType.pointPixelComponentType, new ArrayBuffer(0));
                        }
                        if (polyData.numberOfCellPixels > 0) {
                            polyData.cellData = getPipelineModuleOutputArray(pipelineModule, index, 6, polyData.polyDataType.cellPixelComponentType);
                        }
                        else {
                            polyData.cellData = bufferToTypedArray(polyData.polyDataType.cellPixelComponentType, new ArrayBuffer(0));
                        }
                        outputData = polyData;
                        break;
                    }
                default:
                    throw Error('Unsupported output InterfaceType');
            }
            const populatedOutput = {
                type: output.type,
                data: outputData
            };
            populatedOutputs.push(populatedOutput);
        });
    }
    return { returnValue, stdout, stderr, outputs: populatedOutputs };
}
export default runPipelineEmscripten;
//# sourceMappingURL=run-pipeline-emscripten.js.map