"use strict";
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * WASM Resource Provider Support
 *
 * Handles resource provider registration and handler invocation for WASM plugins
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.wasmResourceProviders = void 0;
exports.callWasmResourceHandler = callWasmResourceHandler;
exports.updateResourceProviderInstance = updateResourceProviderInstance;
exports.cleanupResourceProviders = cleanupResourceProviders;
exports.createResourceProviderBinding = createResourceProviderBinding;
const debug_1 = __importDefault(require("debug"));
const debug = (0, debug_1.default)('signalk:wasm:resource-provider');
/**
 * Registered resource providers from WASM plugins
 * Key: pluginId:resourceType
 */
exports.wasmResourceProviders = new Map();
/**
 * Call a WASM resource handler function
 * Handles both AssemblyScript and Rust plugins
 */
function callWasmResourceHandler(pluginInstance, handlerName, requestJson) {
    try {
        const asLoader = pluginInstance.asLoader;
        const rawExports = pluginInstance.instance?.exports;
        if (asLoader && typeof asLoader.exports[handlerName] === 'function') {
            // AssemblyScript: allocate string in WASM memory, pass pointer, get string pointer back
            const requestPtr = asLoader.exports.__newString(requestJson);
            const resultPtr = asLoader.exports[handlerName](requestPtr);
            return asLoader.exports.__getString(resultPtr);
        }
        else if (rawExports && typeof rawExports[handlerName] === 'function') {
            // Rust: buffer-based string passing
            if (typeof rawExports.allocate !== 'function') {
                debug(`Plugin ${pluginInstance.pluginId} missing allocate export`);
                return null;
            }
            const requestBytes = Buffer.from(requestJson, 'utf8');
            const requestPtr = rawExports.allocate(requestBytes.length);
            const responseMaxLen = 65536; // 64KB response buffer
            const responsePtr = rawExports.allocate(responseMaxLen);
            // Write request to WASM memory
            const memory = rawExports.memory;
            const memView = new Uint8Array(memory.buffer);
            memView.set(requestBytes, requestPtr);
            // Call handler: (request_ptr, request_len, response_ptr, response_max_len) -> written_len
            const writtenLen = rawExports[handlerName](requestPtr, requestBytes.length, responsePtr, responseMaxLen);
            // Read response from WASM memory
            const responseBytes = new Uint8Array(memory.buffer, responsePtr, writtenLen);
            const responseJson = new TextDecoder('utf-8').decode(responseBytes);
            // Deallocate buffers
            if (typeof rawExports.deallocate === 'function') {
                rawExports.deallocate(requestPtr, requestBytes.length);
                rawExports.deallocate(responsePtr, responseMaxLen);
            }
            return responseJson;
        }
        debug(`Handler ${handlerName} not found in plugin ${pluginInstance.pluginId}`);
        return null;
    }
    catch (error) {
        debug(`Error calling resource handler ${handlerName}: ${error}`);
        return null;
    }
}
/**
 * Update resource provider references with a newly loaded plugin instance
 */
function updateResourceProviderInstance(pluginId, pluginInstance) {
    if (exports.wasmResourceProviders && exports.wasmResourceProviders.size > 0) {
        exports.wasmResourceProviders.forEach((provider, key) => {
            if (provider.pluginId === pluginId) {
                provider.pluginInstance = pluginInstance;
                debug(`Updated resource provider ${key} with plugin instance`);
            }
        });
    }
}
/**
 * Clean up resource provider registrations for a plugin
 * @param pluginId The plugin ID
 * @param app The Signal K app (optional, if provided will also unregister from ResourcesApi)
 */
function cleanupResourceProviders(pluginId, app) {
    const keysToDelete = [];
    exports.wasmResourceProviders.forEach((provider, key) => {
        if (provider.pluginId === pluginId) {
            keysToDelete.push(key);
        }
    });
    keysToDelete.forEach((key) => {
        debug(`Removing resource provider registration: ${key}`);
        exports.wasmResourceProviders.delete(key);
    });
    // Also unregister from Signal K ResourcesApi
    if (app &&
        app.resourcesApi &&
        typeof app.resourcesApi.unRegister === 'function') {
        try {
            app.resourcesApi.unRegister(pluginId);
            debug(`Unregistered ${pluginId} from ResourcesApi`);
        }
        catch (error) {
            debug(`Error unregistering from ResourcesApi: ${error}`);
        }
    }
}
/**
 * Create the sk_register_resource_provider host binding
 */
function createResourceProviderBinding(pluginId, capabilities, app, readUtf8String) {
    return (typePtr, typeLen) => {
        try {
            const resourceType = readUtf8String(typePtr, typeLen);
            debug(`[${pluginId}] Registering as resource provider for: ${resourceType}`);
            // Check if plugin has resourceProvider capability
            if (!capabilities.resourceProvider) {
                debug(`[${pluginId}] resourceProvider capability not granted`);
                return 0; // Failure
            }
            // Check if app and resourcesApi are available
            if (!app || !app.resourcesApi) {
                debug(`[${pluginId}] app.resourcesApi not available`);
                return 0;
            }
            // Store the registration (we'll update the pluginInstance reference after instance creation)
            const key = `${pluginId}:${resourceType}`;
            exports.wasmResourceProviders.set(key, {
                pluginId,
                resourceType,
                pluginInstance: null // Will be set after full instance creation
            });
            // Create wrapper methods that call into WASM
            // Note: resourceType is captured from closure scope
            const providerMethods = {
                listResources: async (query) => {
                    const provider = exports.wasmResourceProviders.get(key);
                    if (!provider || !provider.pluginInstance) {
                        debug(`[${pluginId}] Resource provider instance not ready`);
                        return {};
                    }
                    // Include resourceType so WASM knows which type to list
                    const queryJson = JSON.stringify({ ...query, resourceType });
                    const result = callWasmResourceHandler(provider.pluginInstance, 'resources_list_resources', queryJson);
                    return result ? JSON.parse(result) : {};
                },
                getResource: async (id, property) => {
                    const provider = exports.wasmResourceProviders.get(key);
                    if (!provider || !provider.pluginInstance) {
                        debug(`[${pluginId}] Resource provider instance not ready`);
                        return {};
                    }
                    // Include resourceType so WASM knows which storage to search
                    const requestJson = JSON.stringify({ id, property, resourceType });
                    const result = callWasmResourceHandler(provider.pluginInstance, 'resources_get_resource', requestJson);
                    return result ? JSON.parse(result) : {};
                },
                setResource: async (id, value) => {
                    const provider = exports.wasmResourceProviders.get(key);
                    if (!provider || !provider.pluginInstance) {
                        debug(`[${pluginId}] Resource provider instance not ready`);
                        return;
                    }
                    // Include resourceType so WASM knows which storage to update
                    const requestJson = JSON.stringify({ id, value, resourceType });
                    callWasmResourceHandler(provider.pluginInstance, 'resources_set_resource', requestJson);
                },
                deleteResource: async (id) => {
                    const provider = exports.wasmResourceProviders.get(key);
                    if (!provider || !provider.pluginInstance) {
                        debug(`[${pluginId}] Resource provider instance not ready`);
                        return;
                    }
                    // Include resourceType so WASM knows which storage to delete from
                    const requestJson = JSON.stringify({ id, resourceType });
                    callWasmResourceHandler(provider.pluginInstance, 'resources_delete_resource', requestJson);
                }
            };
            // Register with Signal K ResourcesApi
            app.resourcesApi.register(pluginId, {
                type: resourceType,
                methods: providerMethods
            });
            debug(`[${pluginId}] Successfully registered as ${resourceType} resource provider`);
            return 1; // Success
        }
        catch (error) {
            debug(`Plugin register resource provider error: ${error}`);
            return 0;
        }
    };
}
//# sourceMappingURL=resource-provider.js.map