interface MediaDevice {
    device: InputDeviceInfo;
    capabilities: MediaTrackCapabilities;
}

interface VideoTrackCapabilities {
    [x: string]: {
        isSupported: boolean;
        min: number;
        max: number;
        step: number;
        value: number;
    }
}

interface CameraStatusChanged extends CustomEvent {
    detail: {
        isOpen: boolean;
        capabilities: VideoTrackCapabilities;
    }
}

interface SelectedCameraChanged extends CustomEvent {
    detail: {
        deviceId: string;
    }
}

interface DevicePixelRatioChanged extends CustomEvent {
    detail: {
        devicePixelRatio: number;
    }
}

declare class CameraController extends EventTarget {
    #private;
    mediaDevices: MediaDevice[];
    selectedDeviceId: string;
    cameraStream: MediaStream;
    cameraWidth: number;
    cameraHeight: number;
    cameraFrameRate: number;
    settingsInputIds: string[];
    standardSettingsIds: string[];
    videoTrackCapabilities: VideoTrackCapabilities;
    constructor();
    static init(): CameraController;
    updatePixelRatio(): void;
    /** Enumerates the list of video input devices
     *
     * The label field for each MediaDevice will be empty if the permission
     * is not equal to `granted`
     */
    enumerate(): Promise<MediaDevice[]>;
    /** Request access to the camera
     *
     * If the camera permission is set to `prompt` then it will ask for permission.
     *
     * If permission is granted it returns `true` otherwise it returns `false`
     */
    requestPermission(): Promise<{
        isPermissionGranted: boolean;
        cameras: MediaDevice[];
    }>;
    /** Populates the list of `mediaDevices` and sets `selectedDeviceId`
     *
     * If the camera permission is set to `prompt` then it will prompt for permission.
     *
     * If camera permission granted it will populate `mediaDevices` with the list of available cameras and
     * sets `selectedDeviceId` to that of the first available camera.
     *
     * If camera permission is blocked, it will set `mediaDevices` to [] and `selectedDeviceId` to an empty string
     *
     */
    list(): Promise<void>;
    /** Change selected media device Id
     *
     */
    setDeviceId(deviceId: string): void;
    /** Start camera
     *
     */
    start(frameWidth: number, frameHeight: number): Promise<boolean>;
    /** Return VideoTrackCapabilities
     *
     */
    getVideoTrackCapabilities(): VideoTrackCapabilities;
    /** Stop camera
     *
     */
    stop(): void;
    getCameraStatusEvent(isOpen: boolean): CameraStatusChanged;
    getSelectedDeviceChangedEvent(): SelectedCameraChanged;
    getDeviceAspectRatioChangedEvent(): DevicePixelRatioChanged;
}

interface VideoControllerInterface {
    getBytesCallback(bytes: number): void;
    videoLoadedCallback(isLoaded: boolean): Promise<void>;
    videoEndedCallback(): void;
    mimeCodec: string;
    videoSource: HTMLSourceElement;
    canvas: HTMLCanvasElement;
    bytesDownloaded: number;
    isVideoLoaded: boolean;
    videoFrameCallback: VideoFrameRequestCallback;
    ctx: CanvasRenderingContext2D | null;
    mediaStream: MediaStream | null;
}

interface VideoControllerSettings {
    mimeCodec: string,
    getBytesCallback: (bytes: number) => void,
    videoLoadedCallback: (isLoaded: boolean) => Promise<void>
    videoEndedCallback: () => void;
}

declare class VideoController implements VideoControllerInterface {
    mimeCodec: string;
    videoElement: HTMLVideoElement;
    videoSource: HTMLSourceElement;
    canvas: HTMLCanvasElement;
    mediaStream: MediaStream | null;
    bytesDownloaded: number;
    isVideoLoaded: boolean;
    ctx: CanvasRenderingContext2D | null;
    static init(settings: VideoControllerSettings): VideoController;
    constructor(settings: VideoControllerSettings);
    getBytesCallback(bytes: number): void;
    videoLoadedCallback(isLoaded: boolean): Promise<void>;
    videoEndedCallback(): void;
    videoFrameCallback(now: DOMHighResTimeStamp, metadata: VideoFrameCallbackMetadata): void;
    captureFromCanvas(): void;
    captureFromVideoElement(): void;
    setMediaStream(): void;
    getBuffer(url: string): Promise<ArrayBuffer | undefined>;
    init(url: string): Promise<void>;
}

interface ImageSequenceSettings {
    url: string,
    imageName: {
        prefix: string;
        numberOfDigits: number;
        extension: string;
    }
}

interface ImageSequenceMediaStreamReadyEvent extends CustomEvent {
    detail: {
        isReady: boolean;
    }
}

declare class ImageSequenceController extends EventTarget {
    canvas: HTMLCanvasElement;
    ctx: CanvasRenderingContext2D | null;
    imageBitmaps: ImageBitmap[];
    canDrawImage: boolean;
    settings: ImageSequenceSettings;
    static init(settings: ImageSequenceSettings, videoEndedCallback: () => void): ImageSequenceController;
    constructor(settings: ImageSequenceSettings, videoEndedCallback: () => void);
    videoEndedCallback(): void;
    getImageName(sequence: number): string;
    getImageBitmapFromUrl(url: string): Promise<ImageBitmap>;
    getImageBitmapFromArrayOfUrls(urls: string[]): Promise<void>;
    setImageBitmaps(imageBitmaps: ImageBitmap[]): void;
    drawImageBitmapOnCanvas(imageBitmap: ImageBitmap): void;
    getMediaStreamReadyEvent(isReady: boolean): ImageSequenceMediaStreamReadyEvent;
    drawImagesOnCanvas(): void;
}

interface BytesDownloaded extends CustomEvent {
    detail: {
        bytes: number;
        url: string;
        done: boolean;
    }
}

interface BytesDownloadError extends CustomEvent {
    detail: {
        url: string;
        error: unknown;
    }
}

declare class AssetDownloader extends EventTarget {
    #private;
    static init(): AssetDownloader;
    isSimdSupported(): Promise<boolean>;
    dispatch(eventType: string, payload: unknown): void;
    getBytesDownloadedEvent(bytes: number, url: string, done: boolean): BytesDownloaded;
    getBytesDownloadErrorEvent(url: string, error: unknown): BytesDownloadError;
    /** Decompresses a Brotli compressed/based 64 encoded string and returns an ArrayBuffer */
    decompressBrotli(compressedBuffer: string): ArrayBufferLike;
    /** Returns either an ArrayBuffer or undefined */
    fetchAsset(url: string, decompress: boolean): Promise<ArrayBuffer | undefined>;
}

declare const utils: {
    /**
     * Example usage:
     *
     * ```js
     * const camera = CameraController.init();
     * ```
     *
     */
    CameraController: typeof CameraController;
    /**
     * Example usage:
     *
     * ```js
     * const video = VideoController.init({
     *       mimeCodec: 'video/mp4',
     *       getBytesCallback: () => {},
     *       videoLoadedCallback: async () => {}
     *});
     * ```
     *
     */
    VideoController: typeof VideoController;
    /**
     * Example usage:
     *
     * ```js
     * const imageSequence = ImageSequenceController.init({
            url: 'http://localhost/frames/',
            imageName: {
                prefix: 'img',
                numberOfDigits: 4,
                extension: 'jpg',
            }
    });
     * ```
     *
     */
    ImageSequenceController: typeof ImageSequenceController;
    /**
     * Example usage:
     *
     * ```js
     * const assetDownloader = AssetDownloader.init();

     * const onBytesDownloaded = (e: CustomEvent) => {
           const { bytes, url, done } = e.detail;
           console.log(bytes, url, done);
       };
       assetDownloader.addEventListener('bytesDownloaded', onBytesDownloaded as EventListener);

       const onDownloadedError = (e: CustomEvent) => {
           const { error, url } = e.detail;
           console.log(error, url);
       };
       assetDownloader.addEventListener('downloadedError', onDownloadedError as EventListener);
     *
     * const isSimdSupported = await assetDownloader.isSimdSupported();
     * const arrayBuffer = await assetDownloader.fetchAsset(url);
     * ```
     *
     */
    AssetDownloader: typeof AssetDownloader;
};

export { type BytesDownloadError, type BytesDownloaded, type CameraStatusChanged, type DevicePixelRatioChanged, type ImageSequenceMediaStreamReadyEvent, type SelectedCameraChanged, utils as default };
