"use strict";
/* eslint-disable @typescript-eslint/no-explicit-any */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createUtf8Reader = createUtf8Reader;
exports.createEnvImports = createEnvImports;
/**
 * WASM Environment Imports (Host Bindings)
 *
 * Provides the Signal K API functions that WASM plugins can import
 */
const debug_1 = __importDefault(require("debug"));
const server_api_1 = require("@signalk/server-api");
const resource_provider_1 = require("./resource-provider");
const weather_provider_1 = require("./weather-provider");
const radar_provider_1 = require("./radar-provider");
const binary_stream_1 = require("./binary-stream");
const socket_manager_1 = require("./socket-manager");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const atomicWrite_1 = require("../../atomicWrite");
const debug = (0, debug_1.default)('signalk:wasm:bindings');
/**
 * Helper to read UTF-8 strings from WASM memory
 */
function createUtf8Reader(memoryRef) {
    return (ptr, len) => {
        if (!memoryRef.current) {
            throw new Error('AssemblyScript module memory not initialized');
        }
        const bytes = new Uint8Array(memoryRef.current.buffer, ptr, len);
        const decoder = new TextDecoder('utf-8');
        return decoder.decode(bytes);
    };
}
/**
 * Create environment imports for a WASM plugin
 */
function createEnvImports(options) {
    const { pluginId, capabilities, app, memoryRef, rawExports, asLoaderInstance, configPath, packageName: _packageName } = options;
    const readUtf8String = createUtf8Reader(memoryRef);
    const readBinaryData = (0, binary_stream_1.createBinaryDataReader)(memoryRef);
    const envImports = {
        // AssemblyScript runtime requirements
        abort: (msg, file, line, column) => {
            debug(`WASM abort called: ${msg} at ${file}:${line}:${column}`);
        },
        seed: () => {
            return Date.now() * Math.random();
        },
        'console.log': (ptr, len) => {
            try {
                const message = readUtf8String(ptr, len);
                debug(`[${pluginId}] ${message}`);
            }
            catch (error) {
                debug(`WASM console.log error: ${error}`);
            }
        },
        // Signal K API functions
        sk_debug: (ptr, len) => {
            try {
                const message = readUtf8String(ptr, len);
                debug(`[${pluginId}] ${message}`);
            }
            catch (error) {
                debug(`Plugin debug error: ${error}`);
            }
        },
        sk_set_status: (ptr, len) => {
            try {
                const message = readUtf8String(ptr, len);
                debug(`[${pluginId}] Status: ${message}`);
                if (app && app.setPluginStatus) {
                    app.setPluginStatus(pluginId, message);
                }
            }
            catch (error) {
                debug(`Plugin set status error: ${error}`);
            }
        },
        sk_set_error: (ptr, len) => {
            try {
                const message = readUtf8String(ptr, len);
                debug(`[${pluginId}] Error: ${message}`);
                if (app && app.setPluginError) {
                    app.setPluginError(pluginId, message);
                }
            }
            catch (error) {
                debug(`Plugin set error error: ${error}`);
            }
        },
        // Get value from vessels.self path
        sk_get_self_path: (pathPtr, pathLen, bufPtr, bufMaxLen) => {
            try {
                const path = readUtf8String(pathPtr, pathLen);
                debug(`[${pluginId}] getSelfPath: ${path}`);
                if (!app || !app.getSelfPath) {
                    debug(`[${pluginId}] app.getSelfPath not available`);
                    return 0;
                }
                const value = app.getSelfPath(path);
                if (value === undefined || value === null) {
                    return 0;
                }
                // Serialize value to JSON
                const jsonStr = JSON.stringify(value);
                const jsonBytes = Buffer.from(jsonStr, 'utf8');
                if (jsonBytes.length > bufMaxLen) {
                    debug(`[${pluginId}] getSelfPath buffer too small: need ${jsonBytes.length}, have ${bufMaxLen}`);
                    return 0;
                }
                // Write to WASM memory
                if (memoryRef.current) {
                    const memView = new Uint8Array(memoryRef.current.buffer);
                    memView.set(jsonBytes, bufPtr);
                    return jsonBytes.length;
                }
                return 0;
            }
            catch (error) {
                debug(`[${pluginId}] getSelfPath error: ${error}`);
                return 0;
            }
        },
        /**
         * Emit a delta message to the Signal K server
         *
         * @param ptr - Pointer to delta JSON string in WASM memory
         * @param len - Length of delta JSON string
         * @param version - Signal K version: 1 = v1 (default), 2 = v2
         *
         * Plugins should use v1 for regular navigation data (the default).
         * Use v2 for Course API paths and other v2-specific data to prevent
         * v2 data from being mixed into the v1 full data model.
         *
         * This mirrors the TypeScript plugin API where handleMessage accepts
         * an optional skVersion parameter.
         */
        sk_handle_message: (ptr, len, version = 1) => {
            try {
                const deltaJson = readUtf8String(ptr, len);
                debug(`[${pluginId}] Emitting delta (v${version === 2 ? '2' : '1'}): ${deltaJson.substring(0, 200)}...`);
                if (app && app.handleMessage) {
                    try {
                        const delta = JSON.parse(deltaJson);
                        const skVersion = version === 2 ? server_api_1.SKVersion.v2 : server_api_1.SKVersion.v1;
                        app.handleMessage(pluginId, delta, skVersion);
                        debug(`[${pluginId}] Delta processed by server (${skVersion})`);
                    }
                    catch (parseError) {
                        debug(`[${pluginId}] Failed to parse/process delta: ${parseError}`);
                    }
                }
                else {
                    debug(`[${pluginId}] Warning: app.handleMessage not available, delta not processed`);
                }
            }
            catch (error) {
                debug(`Plugin handle message error: ${error}`);
            }
        },
        /**
         * Publish a SignalK notification (v6)
         *
         * @param pathPtr - Pointer to notification path string (e.g., "notifications.navigation.closestApproach.radar:1:target:5")
         * @param pathLen - Length of path string
         * @param valuePtr - Pointer to notification value JSON
         * @param valueLen - Length of value JSON
         * @returns 0 on success, -1 on error
         */
        sk_publish_notification: (pathPtr, pathLen, valuePtr, valueLen) => {
            try {
                const path = readUtf8String(pathPtr, pathLen);
                const valueJson = readUtf8String(valuePtr, valueLen);
                debug(`[${pluginId}] Publishing notification: ${path}`);
                if (!app || !app.handleMessage) {
                    debug(`[${pluginId}] app.handleMessage not available`);
                    return -1;
                }
                // Parse and validate the notification value
                let notificationValue;
                try {
                    notificationValue = JSON.parse(valueJson);
                }
                catch (e) {
                    debug(`[${pluginId}] Invalid notification JSON: ${e}`);
                    return -1;
                }
                // Validate required notification fields per SignalK spec
                // Notifications must have: state, method, message
                if (!notificationValue.state) {
                    debug(`[${pluginId}] Notification missing required 'state' field`);
                    return -1;
                }
                const validStates = ['normal', 'alert', 'warn', 'alarm', 'emergency'];
                if (!validStates.includes(notificationValue.state)) {
                    debug(`[${pluginId}] Invalid notification state: ${notificationValue.state}`);
                    return -1;
                }
                // Build the delta message for the notification
                const delta = {
                    updates: [
                        {
                            values: [
                                {
                                    path: path,
                                    value: notificationValue
                                }
                            ]
                        }
                    ]
                };
                // Notifications should be processed normally (version 1)
                app.handleMessage(pluginId, delta);
                debug(`[${pluginId}] Notification published: ${path} state=${notificationValue.state}`);
                return 0; // Success
            }
            catch (error) {
                debug(`[${pluginId}] sk_publish_notification error: ${error}`);
                return -1;
            }
        },
        // ==========================================================================
        // Plugin Configuration API
        // ==========================================================================
        /**
         * Read plugin configuration from plugin-config-data
         * Uses: ~/.signalk/plugin-config-data/{pluginId}.json
         *
         * This matches the storage location used by JS plugins.
         *
         * @param bufPtr - Buffer to write config JSON into
         * @param bufMaxLen - Maximum buffer size
         * @returns Number of bytes written, or 0 if no config / error
         */
        sk_read_config: (bufPtr, bufMaxLen) => {
            try {
                const cfgPath = configPath || app?.config?.configPath;
                if (!cfgPath) {
                    debug(`[${pluginId}] sk_read_config: configPath not available`);
                    return 0;
                }
                // Plugin config path: plugin-config-data/{pluginId}.json (same as JS plugins)
                const configFile = path.join(cfgPath, 'plugin-config-data', `${pluginId}.json`);
                let configJson = '{}';
                if (fs.existsSync(configFile)) {
                    try {
                        const rawConfig = fs.readFileSync(configFile, 'utf8');
                        const parsed = JSON.parse(rawConfig);
                        // Return just the configuration object (not enabled/enableLogging flags)
                        configJson = JSON.stringify(parsed.configuration || {});
                    }
                    catch (e) {
                        debug(`[${pluginId}] Could not read config: ${e}`);
                    }
                }
                debug(`[${pluginId}] Reading config from ${configFile}: ${configJson.substring(0, 100)}...`);
                const encoder = new TextEncoder();
                const configBytes = encoder.encode(configJson);
                if (configBytes.length > bufMaxLen) {
                    debug(`[${pluginId}] Config buffer too small: need ${configBytes.length}, have ${bufMaxLen}`);
                    return 0;
                }
                if (!memoryRef.current)
                    return 0;
                const memView = new Uint8Array(memoryRef.current.buffer);
                memView.set(configBytes, bufPtr);
                return configBytes.length;
            }
            catch (error) {
                debug(`[${pluginId}] sk_read_config error: ${error}`);
                return 0;
            }
        },
        /**
         * Save plugin configuration to plugin-config-data
         * Uses: ~/.signalk/plugin-config-data/{pluginId}.json
         *
         * This matches the storage location used by JS plugins.
         *
         * @param configPtr - Pointer to config JSON string
         * @param configLen - Length of config JSON
         * @returns 0 on success, negative on error
         */
        sk_save_config: (configPtr, configLen) => {
            try {
                const cfgPath = configPath || app?.config?.configPath;
                if (!cfgPath) {
                    debug(`[${pluginId}] sk_save_config: configPath not available`);
                    return -1;
                }
                const configJson = readUtf8String(configPtr, configLen);
                debug(`[${pluginId}] Saving config: ${configJson.substring(0, 100)}...`);
                // Validate JSON
                const configuration = JSON.parse(configJson);
                // Plugin config path: plugin-config-data/{pluginId}.json (same as JS plugins)
                const configDataDir = path.join(cfgPath, 'plugin-config-data');
                const configFile = path.join(configDataDir, `${pluginId}.json`);
                // Create directory if needed
                if (!fs.existsSync(configDataDir)) {
                    fs.mkdirSync(configDataDir, { recursive: true });
                }
                // Read existing config to preserve enabled/enableLogging flags
                let existingConfig = { enabled: true };
                if (fs.existsSync(configFile)) {
                    try {
                        existingConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
                    }
                    catch (e) {
                        debug(`[${pluginId}] Could not read existing config: ${e}`);
                    }
                }
                // Update configuration while preserving other fields
                existingConfig.configuration = configuration;
                (0, atomicWrite_1.atomicWriteFileSync)(configFile, JSON.stringify(existingConfig, null, 2));
                debug(`[${pluginId}] Config saved to ${configFile}`);
                return 0;
            }
            catch (error) {
                debug(`[${pluginId}] sk_save_config error: ${error}`);
                return -1;
            }
        },
        // Capability checking
        sk_has_capability: (capPtr, capLen) => {
            try {
                const capability = readUtf8String(capPtr, capLen);
                debug(`[${pluginId}] Checking capability: ${capability}`);
                if (capability === 'network') {
                    return capabilities.network ? 1 : 0;
                }
                if (capability === 'rawSockets') {
                    return capabilities.rawSockets ? 1 : 0;
                }
                return 0;
            }
            catch (error) {
                debug(`Plugin capability check error: ${error}`);
                return 0;
            }
        },
        // PUT Handler Registration
        sk_register_put_handler: (contextPtr, contextLen, pathPtr, pathLen) => {
            try {
                const context = readUtf8String(contextPtr, contextLen);
                const path = readUtf8String(pathPtr, pathLen);
                debug(`[${pluginId}] Registering PUT handler: context=${context}, path=${path}`);
                if (!capabilities.putHandlers) {
                    debug(`[${pluginId}] PUT handlers capability not granted`);
                    return 0;
                }
                debug(`[${pluginId}] app available: ${!!app}, app.registerActionHandler available: ${!!(app && app.registerActionHandler)}`);
                if (app && app.registerActionHandler) {
                    // Send meta message to indicate this path supports PUT
                    if (app.handleMessage) {
                        app.handleMessage(pluginId, {
                            updates: [
                                {
                                    meta: [
                                        {
                                            path: path,
                                            value: { supportsPut: true }
                                        }
                                    ]
                                }
                            ]
                        });
                        debug(`[${pluginId}] Sent supportsPut meta for ${path}`);
                    }
                    const callback = (cbContext, cbPath, value, cb) => {
                        debug.enabled &&
                            debug(`[${pluginId}] PUT request received: ${cbContext}.${cbPath} = ${JSON.stringify(value)}`);
                        const handlerName = `handle_put_${cbContext.replace(/\./g, '_')}_${cbPath.replace(/\./g, '_')}`;
                        const exports = asLoaderInstance.current?.exports || rawExports.current;
                        const handlerFunc = exports?.[handlerName];
                        if (handlerFunc) {
                            debug(`[${pluginId}] Calling WASM handler: ${handlerName}`);
                            const valueJson = JSON.stringify(value);
                            try {
                                let responseJson;
                                if (asLoaderInstance.current) {
                                    responseJson = handlerFunc(valueJson);
                                }
                                else if (rawExports.current?.allocate) {
                                    // Rust library plugin: buffer-based string passing
                                    const valueBytes = Buffer.from(valueJson, 'utf8');
                                    const valuePtr = rawExports.current.allocate(valueBytes.length);
                                    const responseMaxLen = 8192;
                                    const responsePtr = rawExports.current.allocate(responseMaxLen);
                                    const memory = rawExports.current.memory;
                                    const memView = new Uint8Array(memory.buffer);
                                    memView.set(valueBytes, valuePtr);
                                    const writtenLen = handlerFunc(valuePtr, valueBytes.length, responsePtr, responseMaxLen);
                                    const responseBytes = new Uint8Array(memory.buffer, responsePtr, writtenLen);
                                    responseJson = new TextDecoder('utf-8').decode(responseBytes);
                                    if (rawExports.current.deallocate) {
                                        rawExports.current.deallocate(valuePtr, valueBytes.length);
                                        rawExports.current.deallocate(responsePtr, responseMaxLen);
                                    }
                                }
                                else {
                                    throw new Error('Unknown plugin type for PUT handler');
                                }
                                const response = JSON.parse(responseJson);
                                debug.enabled &&
                                    debug(`[${pluginId}] PUT handler response: ${JSON.stringify(response)}`);
                                cb(response);
                            }
                            catch (error) {
                                debug(`[${pluginId}] PUT handler error: ${error}`);
                                cb({
                                    state: 'COMPLETED',
                                    statusCode: 500,
                                    message: `Handler error: ${error}`
                                });
                            }
                        }
                        else {
                            debug(`[${pluginId}] Warning: Handler function not found: ${handlerName}`);
                            cb({
                                state: 'COMPLETED',
                                statusCode: 501,
                                message: 'Handler not implemented'
                            });
                        }
                    };
                    app.registerActionHandler(context, path, pluginId, callback);
                    debug(`[${pluginId}] PUT handler registered successfully via registerActionHandler`);
                    return 1;
                }
                else {
                    debug(`[${pluginId}] app.registerActionHandler not available`);
                    return 0;
                }
            }
            catch (error) {
                debug(`Plugin register PUT handler error: ${error}`);
                return 0;
            }
        },
        // Resource Provider Registration
        sk_register_resource_provider: (0, resource_provider_1.createResourceProviderBinding)(pluginId, capabilities, app, readUtf8String),
        // Weather Provider Registration
        sk_register_weather_provider: (0, weather_provider_1.createWeatherProviderBinding)(pluginId, capabilities, app, readUtf8String),
        // Radar Provider Registration
        sk_register_radar_provider: (0, radar_provider_1.createRadarProviderBinding)(pluginId, capabilities, app, readUtf8String),
        // ==========================================================================
        // Binary Stream API (for high-frequency data streaming)
        // ==========================================================================
        /**
         * Emit binary data to a stream
         * General-purpose binary streaming for any plugin
         * @param streamIdPtr - Pointer to stream ID string
         * @param streamIdLen - Length of stream ID
         * @param dataPtr - Pointer to binary data
         * @param dataLen - Length of binary data
         * @returns 1 on success, 0 on failure
         */
        sk_emit_binary_stream: (0, binary_stream_1.createBinaryStreamBinding)(pluginId, app, readUtf8String, readBinaryData),
        /**
         * Emit radar spoke data
         * Convenience wrapper for radar providers
         * @param radarIdPtr - Pointer to radar ID string
         * @param radarIdLen - Length of radar ID
         * @param spokeDataPtr - Pointer to binary spoke data (protobuf)
         * @param spokeDataLen - Length of spoke data
         * @returns 1 on success, 0 on failure
         */
        sk_radar_emit_spokes: (0, radar_provider_1.createRadarEmitSpokesBinding)(pluginId, capabilities, app, readUtf8String, readBinaryData),
        // ==========================================================================
        // Raw Socket API (for radar, NMEA, etc.)
        // Requires rawSockets capability
        // ==========================================================================
        /**
         * Create a UDP socket
         * @param type - 0 for udp4, 1 for udp6
         * @returns Socket ID (>0), or -1 on error
         */
        sk_udp_create: (type) => {
            if (!capabilities.rawSockets) {
                debug(`[${pluginId}] rawSockets capability not granted`);
                return -1;
            }
            const socketType = type === 1 ? 'udp6' : 'udp4';
            return socket_manager_1.socketManager.createSocket(pluginId, socketType);
        },
        /**
         * Bind socket to a port
         * @param socketId - Socket ID from sk_udp_create
         * @param port - Port number (0 for any available)
         * @returns 0 on success, -1 on error
         */
        sk_udp_bind: (socketId, port) => {
            if (!capabilities.rawSockets)
                return -1;
            // Note: bind is async but we return immediately and let it complete
            // The socket will be ready by the time we try to receive
            socket_manager_1.socketManager.bind(socketId, port).catch((err) => {
                debug(`[${pluginId}] Async bind error: ${err}`);
            });
            return 0;
        },
        /**
         * Join a multicast group
         * @param socketId - Socket ID
         * @param addrPtr - Pointer to multicast address string
         * @param addrLen - Length of address string
         * @param ifacePtr - Pointer to interface address (0 for default)
         * @param ifaceLen - Length of interface string
         * @returns 0 on success, -1 on error
         */
        sk_udp_join_multicast: (socketId, addrPtr, addrLen, ifacePtr, ifaceLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const multicastAddr = readUtf8String(addrPtr, addrLen);
                const interfaceAddr = ifaceLen > 0 ? readUtf8String(ifacePtr, ifaceLen) : undefined;
                debug(`[${pluginId}] Joining multicast ${multicastAddr} on interface ${interfaceAddr || 'default'}`);
                return socket_manager_1.socketManager.joinMulticast(socketId, multicastAddr, interfaceAddr);
            }
            catch (error) {
                debug(`[${pluginId}] Join multicast error: ${error}`);
                return -1;
            }
        },
        /**
         * Leave a multicast group
         */
        sk_udp_leave_multicast: (socketId, addrPtr, addrLen, ifacePtr, ifaceLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const multicastAddr = readUtf8String(addrPtr, addrLen);
                const interfaceAddr = ifaceLen > 0 ? readUtf8String(ifacePtr, ifaceLen) : undefined;
                return socket_manager_1.socketManager.leaveMulticast(socketId, multicastAddr, interfaceAddr);
            }
            catch (error) {
                debug(`[${pluginId}] Leave multicast error: ${error}`);
                return -1;
            }
        },
        /**
         * Set multicast TTL
         */
        sk_udp_set_multicast_ttl: (socketId, ttl) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.socketManager.setMulticastTTL(socketId, ttl);
        },
        /**
         * Enable/disable multicast loopback
         */
        sk_udp_set_multicast_loopback: (socketId, enabled) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.socketManager.setMulticastLoopback(socketId, enabled !== 0);
        },
        /**
         * Enable/disable broadcast
         */
        sk_udp_set_broadcast: (socketId, enabled) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.socketManager.setBroadcast(socketId, enabled !== 0);
        },
        /**
         * Send data via UDP
         * @param socketId - Socket ID
         * @param addrPtr - Destination address pointer
         * @param addrLen - Destination address length
         * @param port - Destination port
         * @param dataPtr - Data pointer
         * @param dataLen - Data length
         * @returns Bytes sent, or -1 on error
         */
        sk_udp_send: (socketId, addrPtr, addrLen, port, dataPtr, dataLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const address = readUtf8String(addrPtr, addrLen);
                if (!memoryRef.current)
                    return -1;
                const data = Buffer.from(new Uint8Array(memoryRef.current.buffer, dataPtr, dataLen));
                // Send is async, but we return 0 immediately and let it complete
                socket_manager_1.socketManager.send(socketId, data, address, port).catch((err) => {
                    debug(`[${pluginId}] Async send error: ${err}`);
                });
                return dataLen; // Optimistically return bytes "sent"
            }
            catch (error) {
                debug(`[${pluginId}] Send error: ${error}`);
                return -1;
            }
        },
        /**
         * Receive data from UDP socket (non-blocking)
         * @param socketId - Socket ID
         * @param bufPtr - Buffer to write data into
         * @param bufMaxLen - Maximum buffer size
         * @param addrOutPtr - Buffer to write source address (at least 46 bytes for IPv6)
         * @param portOutPtr - Pointer to write source port (u16)
         * @returns Bytes received, 0 if no data, -1 on error
         */
        sk_udp_recv: (socketId, bufPtr, bufMaxLen, addrOutPtr, portOutPtr) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const datagram = socket_manager_1.socketManager.receive(socketId);
                if (!datagram) {
                    return 0; // No data available
                }
                if (!memoryRef.current)
                    return -1;
                const memory = memoryRef.current;
                const memView = new Uint8Array(memory.buffer);
                // Copy data to buffer
                const bytesToCopy = Math.min(datagram.data.length, bufMaxLen);
                memView.set(datagram.data.slice(0, bytesToCopy), bufPtr);
                // Write source address (null-terminated string)
                const addrBytes = Buffer.from(datagram.address + '\0', 'utf8');
                memView.set(addrBytes, addrOutPtr);
                // Write source port (u16, little-endian)
                const portView = new DataView(memory.buffer);
                portView.setUint16(portOutPtr, datagram.port, true);
                return bytesToCopy;
            }
            catch (error) {
                debug(`[${pluginId}] Recv error: ${error}`);
                return -1;
            }
        },
        /**
         * Get number of buffered datagrams waiting to be received
         */
        sk_udp_pending: (socketId) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.socketManager.getBufferedCount(socketId);
        },
        /**
         * Close a socket
         */
        sk_udp_close: (socketId) => {
            if (!capabilities.rawSockets)
                return;
            socket_manager_1.socketManager.close(socketId);
        },
        // ==========================================================================
        // TCP Socket API (for protocols requiring persistent connections)
        // Requires rawSockets capability
        // ==========================================================================
        /**
         * Create a TCP socket
         * @returns Socket ID (>0), or -1 on error
         */
        sk_tcp_create: () => {
            if (!capabilities.rawSockets) {
                debug(`[${pluginId}] rawSockets capability not granted`);
                return -1;
            }
            return socket_manager_1.tcpSocketManager.createSocket(pluginId);
        },
        /**
         * Connect TCP socket to remote host
         * @param socketId - Socket ID from sk_tcp_create
         * @param addrPtr - Pointer to host address string
         * @param addrLen - Length of address string
         * @param port - Remote port number
         * @returns 0 if connection initiated, -1 on error
         */
        sk_tcp_connect: (socketId, addrPtr, addrLen, port) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const address = readUtf8String(addrPtr, addrLen);
                debug(`[${pluginId}] TCP connecting to ${address}:${port}`);
                return socket_manager_1.tcpSocketManager.connect(socketId, address, port);
            }
            catch (error) {
                debug(`[${pluginId}] TCP connect error: ${error}`);
                return -1;
            }
        },
        /**
         * Check if TCP socket is connected
         * @param socketId - Socket ID
         * @returns 1 if connected, 0 if not, -1 if socket not found
         */
        sk_tcp_connected: (socketId) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.tcpSocketManager.isConnected(socketId);
        },
        /**
         * Set TCP socket buffering mode
         * @param socketId - Socket ID
         * @param lineBuffering - 1 for line-buffered (text), 0 for raw (binary)
         * @returns 0 on success, -1 on error
         */
        sk_tcp_set_line_buffering: (socketId, lineBuffering) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.tcpSocketManager.setLineBuffering(socketId, lineBuffering !== 0);
        },
        /**
         * Send data via TCP
         * @param socketId - Socket ID
         * @param dataPtr - Data pointer
         * @param dataLen - Data length
         * @returns Bytes sent, or -1 on error
         */
        sk_tcp_send: (socketId, dataPtr, dataLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                if (!memoryRef.current)
                    return -1;
                const data = Buffer.from(new Uint8Array(memoryRef.current.buffer, dataPtr, dataLen));
                // Send is async, but we return immediately
                socket_manager_1.tcpSocketManager.send(socketId, data).catch((err) => {
                    debug(`[${pluginId}] Async TCP send error: ${err}`);
                });
                return dataLen;
            }
            catch (error) {
                debug(`[${pluginId}] TCP send error: ${error}`);
                return -1;
            }
        },
        /**
         * Receive a complete line from TCP socket (non-blocking)
         * Only works in line-buffered mode
         * @param socketId - Socket ID
         * @param bufPtr - Buffer to write line into (without line ending)
         * @param bufMaxLen - Maximum buffer size
         * @returns Bytes received, 0 if no complete line, -1 on error
         */
        sk_tcp_recv_line: (socketId, bufPtr, bufMaxLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const line = socket_manager_1.tcpSocketManager.receiveLine(socketId);
                if (!line) {
                    return 0; // No complete line available
                }
                if (!memoryRef.current)
                    return -1;
                const memory = memoryRef.current;
                const memView = new Uint8Array(memory.buffer);
                // Convert line to bytes and copy to buffer
                const lineBytes = Buffer.from(line, 'utf8');
                const bytesToCopy = Math.min(lineBytes.length, bufMaxLen);
                memView.set(lineBytes.slice(0, bytesToCopy), bufPtr);
                return bytesToCopy;
            }
            catch (error) {
                debug(`[${pluginId}] TCP recv line error: ${error}`);
                return -1;
            }
        },
        /**
         * Receive raw data from TCP socket (non-blocking)
         * Only works in raw mode
         * @param socketId - Socket ID
         * @param bufPtr - Buffer to write data into
         * @param bufMaxLen - Maximum buffer size
         * @returns Bytes received, 0 if no data, -1 on error
         */
        sk_tcp_recv_raw: (socketId, bufPtr, bufMaxLen) => {
            if (!capabilities.rawSockets)
                return -1;
            try {
                const data = socket_manager_1.tcpSocketManager.receiveRaw(socketId);
                if (!data) {
                    return 0; // No data available
                }
                if (!memoryRef.current)
                    return -1;
                const memory = memoryRef.current;
                const memView = new Uint8Array(memory.buffer);
                const bytesToCopy = Math.min(data.length, bufMaxLen);
                memView.set(data.slice(0, bytesToCopy), bufPtr);
                return bytesToCopy;
            }
            catch (error) {
                debug(`[${pluginId}] TCP recv raw error: ${error}`);
                return -1;
            }
        },
        /**
         * Get number of buffered items waiting to be received
         */
        sk_tcp_pending: (socketId) => {
            if (!capabilities.rawSockets)
                return -1;
            return socket_manager_1.tcpSocketManager.getBufferedCount(socketId);
        },
        /**
         * Close a TCP socket
         */
        sk_tcp_close: (socketId) => {
            if (!capabilities.rawSockets)
                return;
            socket_manager_1.tcpSocketManager.close(socketId);
        }
    };
    return envImports;
}
//# sourceMappingURL=env-imports.js.map