// 假设在前端项目中，可创建一个文件 CapDevice.ts
// 定义 FilterInfo 类
class FilterInfo {
    constructor(public MonikerString: string, public Name: string) {}
}

// 定义 CapDevice 类
class CapDevice {
    private _stopSignal: boolean = false;
    private _worker: Promise<void> | null = null;
    private _stream: MediaStream | null = null;
    private _videoElement: HTMLVideoElement | null = null;
    private _timer: number = 0;
    private _frames: number = 0;
    private _monikerString: string = "";
    private _name: string = "";
    private _framerate: number = 0;
    private _bitmapSource: HTMLCanvasElement | null = null;

    public NewBitmapReady: (() => void) | null = null;

    constructor(moniker: string = "") {
        this._monikerString = moniker;
    }

    public static async getDeviceMonikers(): Promise<FilterInfo[]> {
        const devices = await navigator.mediaDevices.enumerateDevices();
        return devices
          .filter(device => device.kind === "videoinput")
          .map(device => new FilterInfo(device.deviceId, device.label));
    }

    public async getDevices(): Promise<CapDevice[]> {
        const monikers = await CapDevice.getDeviceMonikers();
        return monikers.map(moniker => new CapDevice(moniker.MonikerString));
    }

    public get BitmapSource(): HTMLCanvasElement | null {
        return this._bitmapSource;
    }

    public get Name(): string {
        return this._name;
    }

    public get MonikerString(): string {
        return this._monikerString;
    }

    public set MonikerString(value: string) {
        this._monikerString = value;
        this.stop();
        if (value) {
            this.initializeDeviceForMoniker(value);
            this.start();
        }
    }

    public get Framerate(): number {
        return this._framerate;
    }

    public set Framerate(value: number) {
        this._framerate = value;
    }

    public get IsRunning(): boolean {
        return this._worker !== null &&!this._stopSignal;
    }

    private async initializeDeviceForMoniker(moniker: string) {
        const devices = await CapDevice.getDeviceMonikers();
        const device = devices.find(d => d.MonikerString === moniker);
        if (device) {
            this._name = device.Name;
        }
    }

    public async start() {
        if (!this._monikerString) return;
        if (this.IsRunning) {
            this.stop();
        }
        this._stopSignal = false;
        this._worker = this.runWorker();
    }

    public async stop() {
        this._stopSignal = true;
        if (this._worker) {
            await this._worker;
        }
        this.release();
    }

    private release() {
        this._worker = null;
        if (this._stream) {
            this._stream.getTracks().forEach(track => track.stop());
            this._stream = null;
        }
        this._videoElement = null;
        this._bitmapSource = null;
    }

    private async runWorker() {
        try {
            const constraints = { video: { deviceId: { exact: this._monikerString } } };
            this._stream = await navigator.mediaDevices.getUserMedia(constraints);
            this._videoElement = document.createElement("video");
            this._videoElement.srcObject = this._stream;
            this._videoElement.autoplay = true;
            this._bitmapSource = document.createElement("canvas");
            const ctx = this._bitmapSource.getContext("2d");
            if (ctx) {
                this._timer = performance.now();
                this._frames = 0;
                const drawFrame = () => {
                    if (this._stopSignal) return;
                    ctx.drawImage(this._videoElement!, 0, 0, this._bitmapSource!.width, this._bitmapSource!.height);
                    this._frames++;
                    const now = performance.now();
                    if (now - this._timer >= 1000) {
                        this._framerate = Math.round(this._frames * 1000 / (now - this._timer));
                        this._timer = now;
                        this._frames = 0;
                    }
                    if (this.NewBitmapReady) {
                        this.NewBitmapReady();
                    }
                    requestAnimationFrame(drawFrame);
                };
                requestAnimationFrame(drawFrame);
            }
        } catch (error) {
            console.error("Error starting video capture:", error);
        }
    }
}

export default CapDevice;