import { GcFiles } from '../../gc-core-assets/lib/GcFiles';
import { ServicesRegistry } from '../../gc-core-services/lib/ServicesRegistry';
import { Events } from '../../gc-core-assets/lib/Events';
import { connectionManager } from '../../gc-target-connection-manager/lib/ConnectionManager';
import { xmodemStatusEventType, xmodemProgressEventType, DEFAULT_TIMEOUT_MS, DEFAULT_BAUD, xmodemServiceType } from '../../gc-service-xmodem/lib/XmodemService';

/**
 *  Copyright (c) 2025 Texas Instruments Incorporated
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  *   Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  *   Neither the name of Texas Instruments Incorporated nor the names of
 *  its contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/**
 * A target program loader for loading programs via xmodem protocol over serial uart.
 * Target program loaders are registered with the connection manager, and are loaded
 * during connection to the target.
 */
class TargetXmodemProgramLoader extends Events {
    /**
     * Constructor for a target program loader.
     *
     * @param params parameters for configuring the target program loader.
     */
    constructor(params) {
        super();
        this.params = params;
        connectionManager.registerProgramLoader(this);
    }
    dispose() {
        connectionManager.unregisterProgramLoader(this);
    }
    /**
     * Method to load the program.
     *
     * @param logger a callback for logging error, warning, progress, and debug messages while loading a program.
     */
    async loadProgram(logger) {
        if (this.params.binary1) {
            await this.load(this.params.binary1, 'binary 1', false, logger);
        }
        else {
            throw Error('Binary 1 path not specified.');
        }
        if (this.params.binary2) {
            await this.load(this.params.binary2, 'binary 2', false, logger);
        }
        if (this.params.binary3) {
            await this.load(this.params.binary3, 'binary 3', this.params.expectResponse || false, logger);
        }
    }
    /**
     * Loads a program from a file located at the specified path over UART using Xmodem protocol.
     *
     * @param path The path to the file containing the program to load.
     * @param response Should a response from the target be expected and returned
     * @throws An error if the loading process fails.
     * @returns Promise that resolves with a response as an array of bytes.
     */
    async load(path, description, response = false, logger) {
        if (!path) {
            throw Error(`${description} path not specified.`);
        }
        const xmodemPort = await this.findXmodemPort();
        const statusHandler = (details) => {
            switch (details.type) {
                case 'info':
                    logger.addProgressMessage(details.message);
                    break;
                case 'warning':
                    logger.addWarningMessage(details.message);
                    break;
                case 'error':
                    logger.addErrorMessage(details.message);
                    break;
            }
        };
        const progressHandler = (details) => {
            logger.addProgressMessage(`Load ${details.name}`, undefined, details.percent);
        };
        xmodemPort.addEventListener(xmodemStatusEventType, statusHandler);
        xmodemPort.addEventListener(xmodemProgressEventType, progressHandler);
        this.fireEvent(xmodemStatusEventType, { type: 'info', message: `Loading ${path}` });
        try {
            logger.addProgressMessage(`Load ${description} ...`);
            const file = await this.readBinaryFile(path);
            await xmodemPort.open({ timeoutMs: this.params.timeoutMs || DEFAULT_TIMEOUT_MS, baudRate: this.params.baudRate || DEFAULT_BAUD });
            await xmodemPort.send(file, path);
            if (response) {
                const responseContent = await xmodemPort.receive();
                logger.addProgressMessage(`Load ${description} Complete. Response: ${response}`);
                return responseContent;
            }
            else {
                logger.addProgressMessage(`Load ${description} Complete.`);
                return [];
            }
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            this.fireEvent(xmodemStatusEventType, { type: 'error', message });
            throw Error(`Load ${description} failed: ${message}`);
        }
        finally {
            await xmodemPort.close();
            xmodemPort.removeEventListener(xmodemStatusEventType, statusHandler);
            xmodemPort.removeEventListener(xmodemProgressEventType, progressHandler);
        }
    }
    /**
     * Reads a binary file from disk and returns its contents as an array of bytes.
     *
     * @param fname The path to the file to read.
     * @returns Promise that resolves with the file's contents as an array of bytes.
     */
    async readBinaryFile(fname) {
        const data = await GcFiles.readBinaryFile(fname);
        const arrayBuffer = (data instanceof Blob) ? await data.arrayBuffer() : data;
        return [...new Uint8Array(arrayBuffer)];
    }
    async findXmodemPort() {
        const loaderService = ServicesRegistry.getService(xmodemServiceType);
        const xmodemPorts = await loaderService.listPorts(this.params.timeoutMs || DEFAULT_TIMEOUT_MS, this.params.baudRate || DEFAULT_BAUD);
        if (!xmodemPorts || xmodemPorts.length === 0) {
            const noCRC = 'No port receiving xmodem commands. Ensure the target device is set to boot from UART, and is reset.';
            throw new Error(noCRC);
        }
        return xmodemPorts[0];
    }
}

export { TargetXmodemProgramLoader };
//# sourceMappingURL=TargetXmodemProgramLoader.js.map
