// serial.js

const HEADER1 = 0xAA;
const HEADER2 = 0x55;
const BAUD_RATE = 1000000;

const CMD_ID_REQUEST = 0x01;
const CMD_FILE_LIST = 0x02;
const CMD_LOAD_FILE = 0x03;
const CMD_SAVE_FILE = 0x05;
const CMD_DELETE_FILE = 0x04;
const CMD_SET_POSITION = 0x07;
const CMD_PLAY_FILE = 0x08;
const CMD_SCAN_CHANNEL = 0x09;
const CMD_WRITE_DATA = 0x10;
const CMD_READ_DATA = 0x11;
const CMD_WRITE_CONFIG_UPDATE = 0x12;
const CMD_START_POSITION_STREAM = 0x14;
const POSITION_STREAM = 0x15;

export class SerialManager {
    constructor() {
        this.port = null;
        this.writer = null;
        this.reader = null;
    }

    async connect() {
        this.port = await navigator.serial.requestPort();
        await this.port.open({ baudRate: BAUD_RATE });
        this.writer = this.port.writable.getWriter();
        this.reader = this.port.readable.getReader();
    }

    async send(commandCode, payload = []) {
        const length = payload.length;
        const lengthHigh = (length >> 8) & 0xFF;
        const lengthLow = length & 0xFF;

        let checksum = commandCode ^ lengthHigh ^ lengthLow;
        for (let byte of payload) {
            checksum ^= byte;
        }

        const message = [HEADER1, HEADER2, commandCode, lengthHigh, lengthLow, ...payload, checksum];
        //console.log(new Uint8Array(message));
        await this.writer.write(new Uint8Array(message));
    }


    async requestIDPacket() {
        console.log("Requesting ID packet");
        await this.send(CMD_ID_REQUEST);
    }

    async requestFileList() {
        console.log("Requesting File List");
        await this.send(CMD_FILE_LIST);
    }

    async requestFile(filename) {
        console.log("Requesting File: " + filename);
        const encoder = new TextEncoder();
        const payload = Array.from(encoder.encode(filename));
        await this.send(CMD_LOAD_FILE, payload); // CMD_LOAD_FILE
    }

    async requestPlayFile(payload) {
        console.log("Playing File: " + "filename");
        await this.send(CMD_PLAY_FILE, payload);

    }

    async saveFile(payload) {
        console.log("Saving File: " + "filename");
        await this.send(CMD_SAVE_FILE, payload);
    }

    async deleteFile(payload) {
        console.log("Deleting File: " + "filename");
        await this.send(CMD_DELETE_FILE, payload);
    }

    async sendSetPositions(payload) {
        //console.log("Setting positions");
        await this.send(CMD_SET_POSITION, payload);
    }

    async requestScan(channel) {
        console.log("Scanning Channel " + (channel + 1));
        let payload = new Uint8Array([channel]);
        await this.send(CMD_SCAN_CHANNEL, payload);
    }

    async requestWriteData(payload) {
        console.log(payload);
        await this.send(CMD_WRITE_DATA, payload);
    }

    async sendConfigUpdate(payload) {
        console.log(payload);
        await this.send(CMD_WRITE_CONFIG_UPDATE, payload);
    }

    async requestPositionStreaming(stream) { //stream true/false
        await this.send(CMD_START_POSITION_STREAM, new Uint8Array([stream]));
    }

    startReading(onPacket) {
        const decoder = new TextDecoder();
        let buffer = [];

        const processBuffer = () => {
            while (buffer.length >= 5) {
                if (buffer[0] !== HEADER1 || buffer[1] !== HEADER2) {
                    buffer.shift(); // discard until headers align
                    continue;
                }

                const command = buffer[2];
                const length = (buffer[3] << 8) | buffer[4];

                if (buffer.length < 5 + length) return; // wait for full payload

                const payload = buffer.slice(5, 5 + length);
                onPacket(command, payload);

                buffer = buffer.slice(5 + length); // remove processed packet
            }
        };

        const loop = async () => {
            while (this.port.readable) {
                try {
                    const { value, done } = await this.reader.read();
                    if (done) break;
                    if (value) {
                        buffer.push(...value);
                        processBuffer();
                    }
                } catch (err) {
                    console.error("Read error:", err);
                    break;
                }
            }
        };

        loop();
    }



    async receive(timeoutMs = 1000) {
        const timeoutPromise = new Promise((_, reject) =>
            setTimeout(() => reject(new Error("Timeout waiting for response")), timeoutMs)
        );

        const readPromise = this.reader.read();

        try {
            const { value } = await Promise.race([readPromise, timeoutPromise]);
            return value;
        } catch (err) {
            console.error("Receive error:", err.message);
            return null;
        }
    }


    disconnect() {
        this.reader.releaseLock();
        this.writer.releaseLock();
        this.port.close();
    }
}

