import { InputEventMessageBase } from "src/Models/Input/InputEventMessageBase";

export interface PreviewerFrame {
    data: ImageData;
    dpiX: number;
    dpiY: number;
}

export class PreviewerServerConnection {
    private nextFrame = {
        width: 0,
        height: 0,
        stride: 0,
        dpiX: 0,
        dpiY: 0,
        sequenceId: "0"
    };

    public currentFrame: PreviewerFrame | null;
    private handlers = new Set<(frame: PreviewerFrame | null) => void>();
    private conn: WebSocket;

    public addFrameListener(listener: (frame: PreviewerFrame | null) => void) {
        this.handlers.add(listener);
        if (this.currentFrame)
            listener(this.currentFrame);
    }

    public removeFrameListener(listener: (frame: PreviewerFrame | null) => void) {
        this.handlers.delete(listener);
    }

    public sendMouseEvent(message: InputEventMessageBase) {
        this.conn.send(message.toString());
    }

    constructor(uri: string) {
        this.currentFrame = null;
        var conn = this.conn = new WebSocket(uri);
        conn.binaryType = 'arraybuffer';

        const onMessage = this.onMessage;
        conn.onmessage = msg => onMessage(msg);

        const onClose = () => this.setFrame(null);
        conn.onclose = () => onClose();
        conn.onerror = (err: Event) => {
            onClose();
            console.log("Connection error: " + err);
        }
    }

    private onMessage = (msg: MessageEvent) => {
        if (typeof msg.data == 'string' || msg.data instanceof String) {
            const parts = msg.data.split(':');
            if (parts[0] == 'frame') {
                this.nextFrame = {
                    sequenceId: parts[1],
                    width: parseInt(parts[2]),
                    height: parseInt(parts[3]),
                    stride: parseInt(parts[4]),
                    dpiX: parseInt(parts[5]),
                    dpiY: parseInt(parts[6])
                }
            }
        } else if (msg.data instanceof ArrayBuffer) {
            const arr = new Uint8ClampedArray(msg.data, 0);
            const imageData = new ImageData(arr, this.nextFrame.width, this.nextFrame.height);
            this.conn.send('frame-received:' + this.nextFrame.sequenceId);
            this.setFrame({
                data: imageData,
                dpiX: this.nextFrame.dpiX,
                dpiY: this.nextFrame.dpiY
            });


        }
    };

    private setFrame(frame: PreviewerFrame | null) {
        this.currentFrame = frame;
        this.handlers.forEach(h => h(this.currentFrame));
    }
}
