import photoAccessHelper from "@ohos:file.photoAccessHelper";
import camera from "@ohos:multimedia.camera";
import media from "@ohos:multimedia.media";
import type { BusinessError as BusinessError } from "@ohos:base";
import JSON from "@ohos:util.json";
import fileIo from "@ohos:file.fs";
import fs from "@ohos:file.fs";
import image from "@ohos:multimedia.image";
import CLLog from "@normalized:N&&&@wanshu/realauthsdk/src/main/ets/utils/CLLogUtils&1.0.0";
const TAG: string = 'CameraService';
const uiContext: UIContext | undefined = AppStorage.get('uiContext');
export class Constants {
    // default zoom radio max.
    static readonly ZOOM_RADIO_MAX: number = 6;
    // default zoom radio step.
    static readonly ZOOM_RADIO_MAX_STEP: number = 0.1;
    // default zoom radio step.
    static readonly ZOOM_RADIO_MIN_STEP: number = 0.01;
    // AUDIO_BITRATE.
    static readonly AUDIO_BITRATE: number = 48000;
    // AUDIO_CHANNELS.
    static readonly AUDIO_CHANNELS: number = 2;
    // AUDIO_SAMPLE_RATE.
    static readonly AUDIO_SAMPLE_RATE: number = 48000;
    // VIDEO_BITRATE.
    static readonly VIDEO_BITRATE: number = 512000;
    // VIDEO_FRAME.
    static readonly MAX_VIDEO_FRAME: number = 60;
}
;
export class SliderValue {
    min: number = 1;
    max: number = 6;
    step: number = 0.1;
}
class CLCamreManager {
    private context: Context | undefined = undefined;
    private cameraDeviceIndex: number = 0;
    private surfaceId: string | undefined = undefined;
    private cameraManager: camera.CameraManager | undefined = undefined;
    private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = [];
    private cameraInput: camera.CameraInput | undefined = undefined;
    private previewOutput: camera.PreviewOutput | undefined = undefined;
    private photoOutput: camera.PhotoOutput | undefined = undefined;
    private videoOutput: camera.VideoOutput | undefined = undefined;
    private avRecorder: media.AVRecorder | undefined = undefined;
    private session: camera.PhotoSession | camera.VideoSession | undefined = undefined;
    private handlePhotoAssetCb: (photoAsset: photoAccessHelper.PhotoAsset) => void = () => {
    };
    private curCameraDevice: camera.CameraDevice | undefined = undefined;
    private isRecording: boolean = false;
    // One of the recommended camera resolutions.
    private photoProfileObj: camera.Profile = {
        format: 2000,
        size: {
            width: 1080,
            height: 1080
        }
    };
    // One of the recommended preview resolutions.
    private previewProfileObj: camera.Profile = {
        format: 1003,
        size: {
            width: 1080,
            height: 1080
        }
    };
    // One of the recommended video resolutions.
    private videoProfileObj: camera.VideoProfile = {
        format: 1003,
        size: {
            width: 1080,
            height: 1080
        },
        frameRateRange: {
            min: 30,
            max: 60
        }
    };
    private curSceneMode: camera.SceneMode = camera.SceneMode.NORMAL_PHOTO;
    private imageReceiver: image.ImageReceiver | undefined = undefined;
    constructor() {
    }
    setSavePictureCallback(callback: (photoAsset: photoAccessHelper.PhotoAsset) => void): void {
        this.handlePhotoAssetCb = callback;
    }
    setSceneMode(sceneMode: camera.SceneMode): void {
        this.curSceneMode = sceneMode;
    }
    getSceneMode(): camera.SceneMode {
        return this.curSceneMode;
    }
    getPreviewProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined {
        let previewProfiles = cameraOutputCapability.previewProfiles;
        if (previewProfiles.length < 1) {
            return undefined;
        }
        let minDiff = Number.MAX_VALUE;
        let bestProfile = previewProfiles[0];
        for (const profile of previewProfiles) {
            const ratio = profile.size.width / profile.size.height;
            const diff = Math.abs(ratio - 1.6);
            if (diff < minDiff) {
                minDiff = diff;
                bestProfile = profile;
            }
        }
        let index = previewProfiles.findIndex((previewProfile: camera.Profile) => {
            return previewProfile.size.width === this.previewProfileObj.size.width &&
                previewProfile.size.height === this.previewProfileObj.size.height &&
                previewProfile.format === this.previewProfileObj.format;
        });
        if (index === -1) {
            return undefined;
        }
        return previewProfiles[index];
    }
    getPhotoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined {
        let photoProfiles = cameraOutputCapability.photoProfiles;
        if (photoProfiles.length < 1) {
            return undefined;
        }
        let minDiff = Number.MAX_VALUE;
        let bestProfile = photoProfiles[0];
        for (const profile of photoProfiles) {
            const ratio = profile.size.width / profile.size.height;
            const diff = Math.abs(ratio - 1.6);
            if (diff < minDiff) {
                minDiff = diff;
                bestProfile = profile;
            }
        }
        let index = photoProfiles.findIndex((photoProfile: camera.Profile) => {
            return photoProfile.size.width === this.photoProfileObj.size.width &&
                photoProfile.size.height === this.photoProfileObj.size.height &&
                photoProfile.format === this.photoProfileObj.format;
        });
        if (index === -1) {
            return undefined;
        }
        return photoProfiles[index];
    }
    getVideoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.VideoProfile | undefined {
        let videoProfiles = cameraOutputCapability.videoProfiles;
        console.log(`当前支持的video${JSON.stringify(videoProfiles)}`);
        if (videoProfiles.length < 1) {
            return undefined;
        }
        // for (let i = 0; i < videoProfiles.length; i++) {
        //   CLLog.info(`getVideoProfile: ${JSON.stringify(videoProfiles[i])}`);
        // }
        let index = videoProfiles.findIndex((videoProfile: camera.VideoProfile) => {
            return videoProfile.size.width === this.videoProfileObj.size.width &&
                videoProfile.size.height === this.videoProfileObj.size.height &&
                videoProfile.format === this.videoProfileObj.format &&
                videoProfile.frameRateRange.min <= Constants.MAX_VIDEO_FRAME &&
                videoProfile.frameRateRange.max <= Constants.MAX_VIDEO_FRAME;
        });
        if (index === -1) {
            return undefined;
        }
        return videoProfiles[index];
    }
    isSupportedSceneMode(cameraManager: camera.CameraManager, cameraDevice: camera.CameraDevice): boolean {
        let sceneModes = cameraManager.getSupportedSceneModes(cameraDevice);
        if (sceneModes === undefined) {
            return false;
        }
        let index = sceneModes.findIndex((sceneMode: camera.SceneMode) => {
            return sceneMode === this.curSceneMode;
        });
        if (index === -1) {
            return false;
        }
        return true;
    }
    // async capturePhotoThenRecord(surfaceId: string, cameraDeviceIndex: number = 0): Promise<void> {
    //   // Step 1: 打开相机（拍照模式）
    //   this.setSceneMode(camera.SceneMode.NORMAL_PHOTO);
    //   await this.initCamera(this.context!, surfaceId, cameraDeviceIndex);
    //
    //   // Step 2: 拍一张照
    //   await this.takePicture();
    //
    //   // ⚠️ 这里可以等待回调 `photoAssetAvailable` 拿到保存的图片，再进入下一步
    //
    //   // Step 3: 释放拍照会话
    //   await this.releaseCamera();
    //
    //   // Step 4: 切换到录像模式重新初始化
    //   this.setSceneMode(camera.SceneMode.NORMAL_VIDEO);
    //   await this.initCamera(this.context!, surfaceId, cameraDeviceIndex);
    //
    //   // Step 5: 开始录像
    //   await this.startVideo();
    // }
    async captureToRecordVideo(): Promise<void> {
        console.log('开始录制视频了---');
        const lastFocusPoint = this.session?.getFocusPoint();
        const lastZoom = this.session?.getZoomRatio() ?? 1.0;
        const meteringPoint = this.session?.getMeteringPoint();
        this.setSceneMode(camera.SceneMode.NORMAL_VIDEO);
        await this.initCamera(this.context!, this.surfaceId!, this.cameraDeviceIndex);
        if (lastFocusPoint) {
            this.session?.setFocusPoint(lastFocusPoint);
        }
        if (meteringPoint) {
            this.session?.setMeteringPoint(meteringPoint);
        }
        this.session?.setZoomRatio(lastZoom);
        setTimeout(async () => {
            await this.startVideo();
        }, 200);
    }
    /**
     * Initialize camera function
     * @param surfaceId - Surface ID
     * @param cameraDeviceIndex - Camera device index
     * @returns No return value
     */
    async initCamera(context: Context, surfaceId: string, cameraDeviceIndex: number = 0): Promise<void> {
        CLLog.debug(`initCamera cameraDeviceIndex: ${cameraDeviceIndex}`);
        if (!context) {
            CLLog.error('初始化相机context不存在');
            return;
        }
        if (!surfaceId) {
            CLLog.error('初始化相机surfaceId不存在');
            return;
        }
        this.cameraDeviceIndex = cameraDeviceIndex;
        this.context = context;
        this.surfaceId = surfaceId;
        try {
            await this.releaseCamera();
            // Get Camera Manager instance.
            this.cameraManager = this.getCameraManagerFn();
            if (this.cameraManager === undefined) {
                CLLog.error('cameraManager is undefined');
                return;
            }
            // Get support for specified camera device objects.
            this.cameras = this.getSupportedCamerasFn(this.cameraManager);
            if (this.cameras.length < 1 || this.cameras.length < cameraDeviceIndex + 1) {
                return;
            }
            this.curCameraDevice = this.cameras[cameraDeviceIndex];
            let isSupported = this.isSupportedSceneMode(this.cameraManager, this.curCameraDevice);
            if (!isSupported) {
                CLLog.error('The current scene mode is not supported.');
                return;
            }
            let cameraOutputCapability = this.cameraManager.getSupportedOutputCapability(this.curCameraDevice, this.curSceneMode);
            let previewProfile = this.getPreviewProfile(cameraOutputCapability);
            if (previewProfile === undefined) {
                CLLog.error('The resolution of the current preview stream is not supported.');
                return;
            }
            this.previewProfileObj = previewProfile;
            // Create previewOutput output object.
            this.previewOutput = this.createPreviewOutputFn(this.cameraManager, this.previewProfileObj, surfaceId);
            if (this.previewOutput === undefined) {
                CLLog.error('Failed to create the preview stream.');
                return;
            }
            // Monitor preview events.
            this.previewOutputCallBack(this.previewOutput);
            if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) {
                let photoProfile = this.getPhotoProfile(cameraOutputCapability);
                if (photoProfile === undefined) {
                    CLLog.error('The resolution of the current photo stream is not supported.');
                    return;
                }
                this.photoProfileObj = photoProfile;
                // Create photoOutput output object.
                this.photoOutput = this.createPhotoOutputFn(this.cameraManager, this.photoProfileObj);
                if (this.photoOutput === undefined) {
                    CLLog.error('Failed to create the photo stream.');
                    return;
                }
            }
            else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) {
                let videoProfile = this.getVideoProfile(cameraOutputCapability);
                if (videoProfile === undefined) {
                    CLLog.error('The resolution of the current video stream is not supported.');
                    return;
                }
                this.videoProfileObj = videoProfile;
                this.avRecorder = await this.createAVRecorder();
                if (this.avRecorder === undefined) {
                    CLLog.error('Failed to create the avRecorder.');
                    return;
                }
                await this.prepareAVRecorder();
                let videoSurfaceId = await this.avRecorder.getInputSurface();
                // Create videoOutput output object.
                this.videoOutput = this.createVideoOutputFn(this.cameraManager, this.videoProfileObj, videoSurfaceId);
                if (this.videoOutput === undefined) {
                    CLLog.error('Failed to create the video stream.');
                    return;
                }
            }
            // Create cameraInput output object.
            this.cameraInput = this.createCameraInputFn(this.cameraManager, this.curCameraDevice);
            if (this.cameraInput === undefined) {
                CLLog.error('Failed to create the camera input.');
                return;
            }
            // Open the camera.
            let isOpenSuccess = await this.cameraInputOpenFn(this.cameraInput);
            if (!isOpenSuccess) {
                CLLog.error('Failed to open the camera.');
                return;
            }
            // Camera status callback.
            this.onCameraStatusChange(this.cameraManager);
            // Monitor error events from CameraInput.
            this.onCameraInputChange(this.cameraInput, this.curCameraDevice);
            // Conversation process.
            await this.sessionFlowFn(this.cameraManager, this.cameraInput, this.previewOutput, this.photoOutput, this.videoOutput);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`initCamera fail: ${JSON.stringify(err)}`);
        }
    }
    /**
     * Obtain variable focal length range.
     */
    getZoomRatioRange(): Array<number> {
        let zoomRatioRange: Array<number> = [];
        if (this.session !== undefined) {
            zoomRatioRange = this.session.getZoomRatioRange();
        }
        return zoomRatioRange;
    }
    /**
     * Zoom
     */
    setZoomRatioFn(zoomRatio: number): void {
        CLLog.info(`setZoomRatioFn value ${zoomRatio}`);
        // Obtain supported zoom range.
        try {
            let zoomRatioRange = this.getZoomRatioRange();
            CLLog.logg(`getZoomRatioRange success: ${JSON.stringify(zoomRatioRange)}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`getZoomRatioRange fail: ${JSON.stringify(err)}`);
        }
        try {
            this.session?.setZoomRatio(zoomRatio);
            CLLog.logg('setZoomRatioFn success');
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`setZoomRatioFn fail: ${JSON.stringify(err)}`);
        }
    }
    /**
     * Trigger a photo with specified parameters.
     */
    async takePicture(): Promise<void> {
        let cameraDeviceIndex = this.cameraDeviceIndex;
        CLLog.logg('takePicture start');
        let photoSettings: camera.PhotoCaptureSetting = {
            quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
            mirror: cameraDeviceIndex ? true : false
        };
        await this.photoOutput?.capture(photoSettings);
        CLLog.logg('takePicture end');
    }
    async saveCameraPhoto(asset: photoAccessHelper.PhotoAsset) {
        CLLog.logg('saveCameraPhoto');
        try {
            let context = uiContext?.getHostContext();
            let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
            let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = new photoAccessHelper.MediaAssetChangeRequest(asset);
            assetChangeRequest.saveCameraPhoto();
            await phAccessHelper.applyChanges(assetChangeRequest);
            CLLog.logg('apply saveCameraPhoto successfully');
        }
        catch (err) {
            CLLog.error(`apply saveCameraPhoto failed with error: ${err.code}, ${err.message}`);
        }
    }
    /**
     * Release the session and its related parameters.
     */
    async releaseCamera(): Promise<void> {
        CLLog.logg('releaseCamera is called');
        try {
            await this.previewOutput?.release();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`previewOutput release fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.previewOutput = undefined;
        }
        try {
            await this.photoOutput?.release();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`photoOutput release fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.photoOutput = undefined;
        }
        try {
            await this.avRecorder?.release();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`avRecorder release fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.avRecorder = undefined;
        }
        try {
            await this.videoOutput?.release();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`videoOutput release fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.videoOutput = undefined;
        }
        try {
            this.session?.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE);
            await this.session?.release();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`captureSession release fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.session = undefined;
        }
        try {
            await this.cameraInput?.close();
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`cameraInput close fail: error: ${JSON.stringify(err)}`);
        }
        finally {
            this.cameraInput = undefined;
        }
        this.offCameraStatusChange();
        CLLog.logg('releaseCamera success');
    }
    /**
     * Get Camera Manager instance.
     */
    getCameraManagerFn(): camera.CameraManager | undefined {
        if (this.cameraManager) {
            return this.cameraManager;
        }
        let cameraManager: camera.CameraManager | undefined = undefined;
        try {
            cameraManager = camera.getCameraManager(this.context);
            CLLog.logg(`getCameraManager success: ${cameraManager}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`getCameraManager failed: ${JSON.stringify(err)}`);
        }
        return cameraManager;
    }
    /**
     * Get support for specified camera device objects.
     */
    getSupportedCamerasFn(cameraManager: camera.CameraManager): Array<camera.CameraDevice> {
        let supportedCameras: Array<camera.CameraDevice> = [];
        try {
            supportedCameras = cameraManager.getSupportedCameras();
            CLLog.logg(`getSupportedCameras success: ${this.cameras}, length: ${this.cameras?.length}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`getSupportedCameras failed: ${JSON.stringify(err)}`);
        }
        return supportedCameras;
    }
    /**
     * Create previewOutput output object.
     */
    createPreviewOutputFn(cameraManager: camera.CameraManager, previewProfileObj: camera.Profile, surfaceId: string): camera.PreviewOutput | undefined {
        let previewOutput: camera.PreviewOutput | undefined = undefined;
        try {
            previewOutput = cameraManager.createPreviewOutput(previewProfileObj, surfaceId);
            CLLog.logg(`createPreviewOutput success: ${previewOutput}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`createPreviewOutput failed: ${JSON.stringify(err)}`);
        }
        return previewOutput;
    }
    /**
     * Create photoOutPut output object.
     */
    createPhotoOutputFn(cameraManager: camera.CameraManager, photoProfileObj: camera.Profile): camera.PhotoOutput | undefined {
        let photoOutput: camera.PhotoOutput | undefined = undefined;
        try {
            photoOutput = cameraManager.createPhotoOutput(photoProfileObj);
            CLLog.logg(`createPhotoOutputFn success: ${photoOutput}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`createPhotoOutputFn failed: ${JSON.stringify(err)}`);
        }
        return photoOutput;
    }
    /**
     * Create videoOutPut output object.
     */
    createVideoOutputFn(cameraManager: camera.CameraManager, videoProfileObj: camera.VideoProfile, surfaceId: string): camera.VideoOutput | undefined {
        let videoOutput: camera.VideoOutput | undefined = undefined;
        try {
            videoOutput = cameraManager.createVideoOutput(videoProfileObj, surfaceId);
            CLLog.logg(`createVideoOutputFn success: ${videoOutput}`);
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`createVideoOutputFn failed: ${JSON.stringify(err)}`);
        }
        return videoOutput;
    }
    /**
     * Create cameraInput output object.
     */
    createCameraInputFn(cameraManager: camera.CameraManager, cameraDevice: camera.CameraDevice): camera.CameraInput | undefined {
        CLLog.logg('createCameraInputFn is called.');
        let cameraInput: camera.CameraInput | undefined = undefined;
        try {
            cameraInput = cameraManager.createCameraInput(cameraDevice);
            CLLog.logg('createCameraInputFn success');
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`createCameraInputFn failed: ${JSON.stringify(err)}`);
        }
        return cameraInput;
    }
    /**
     * Open the camera.
     */
    async cameraInputOpenFn(cameraInput: camera.CameraInput): Promise<boolean> {
        let isOpenSuccess = false;
        try {
            await cameraInput.open();
            isOpenSuccess = true;
            CLLog.logg('cameraInput open success');
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`createCameraInput failed : ${JSON.stringify(err)}`);
        }
        return isOpenSuccess;
    }
    /**
     * Conversation process.
     */
    async sessionFlowFn(cameraManager: camera.CameraManager, cameraInput: camera.CameraInput, previewOutput: camera.PreviewOutput, photoOutput: camera.PhotoOutput | undefined, videoOutput: camera.VideoOutput | undefined): Promise<void> {
        try {
            if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) {
                this.session = cameraManager.createSession(this.curSceneMode) as camera.PhotoSession;
            }
            else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) {
                this.session = cameraManager.createSession(this.curSceneMode) as camera.VideoSession;
            }
            if (this.session === undefined) {
                return;
            }
            this.onSessionErrorChange(this.session);
            this.session.beginConfig();
            this.session.addInput(cameraInput);
            this.session.addOutput(previewOutput);
            if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) {
                if (photoOutput === undefined) {
                    return;
                }
                this.photoOutputCallBack(photoOutput);
                this.session.addOutput(photoOutput);
            }
            else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) {
                if (videoOutput === undefined) {
                    return;
                }
                this.videoOutCallBack(videoOutput);
                this.session.addOutput(videoOutput);
            }
            await this.session.commitConfig();
            if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) {
                this.setVideoStabilizationFn(this.session as camera.VideoSession, camera.VideoStabilizationMode.MIDDLE);
            }
            // this.updateSliderValue();
            // this.setFocusMode(camera.FocusMode.FOCUS_MODE_AUTO);
            // await this.session.setExposureMode(camera.ExposureMode.EXPOSURE_MODE_LOCKED);
            await this.session.start();
            CLLog.logg('sessionFlowFn success');
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`sessionFlowFn fail : ${JSON.stringify(err)}`);
        }
    }
    setVideoStabilizationFn(session: camera.VideoSession, videoStabilizationMode: camera.VideoStabilizationMode): void {
        // Check if the specified video stabilization mode is supported.
        let isVideoStabilizationModeSupported: boolean = session.isVideoStabilizationModeSupported(videoStabilizationMode);
        if (isVideoStabilizationModeSupported) {
            session.setVideoStabilizationMode(videoStabilizationMode);
        }
        CLLog.logg('setVideoStabilizationFn success');
    }
    /**
     * Update slider data.
     */
    updateSliderValue(): void {
        let zoomRatioRange = this.getZoomRatioRange();
        if (zoomRatioRange.length !== 0) {
            let zoomRatioMin = zoomRatioRange[0];
            let zoomRatioMax = zoomRatioRange[1] > Constants.ZOOM_RADIO_MAX ? Constants.ZOOM_RADIO_MAX : zoomRatioRange[1];
            let sliderStep = zoomRatioRange[1] > Constants.ZOOM_RADIO_MAX ? Constants.ZOOM_RADIO_MAX_STEP : Constants.ZOOM_RADIO_MIN_STEP;
            AppStorage.set<SliderValue>('sliderValue', {
                min: zoomRatioMin,
                max: zoomRatioMax,
                step: sliderStep
            });
        }
    }
    /**
     * Monitoring and photography events.
     */
    photoOutputCallBack(photoOutput: camera.PhotoOutput): void {
        try {
            photoOutput.on('captureStartWithInfo', (err: BusinessError, captureStartInfo: camera.CaptureStartInfo): void => {
                CLLog.logg(`photoOutputCallBack captureStartWithInfo success: ${JSON.stringify(captureStartInfo)}`);
            });
            photoOutput.on('frameShutter', (err: BusinessError, frameShutterInfo: camera.FrameShutterInfo): void => {
                CLLog.logg(`photoOutputCallBack frameShutter captureId:
          ${frameShutterInfo.captureId}, timestamp: ${frameShutterInfo.timestamp}`);
            });
            photoOutput.on('captureEnd', (err: BusinessError, captureEndInfo: camera.CaptureEndInfo): void => {
                CLLog.logg(`photoOutputCallBack captureEnd captureId:
          ${captureEndInfo.captureId}, frameCount: ${captureEndInfo.frameCount}`);
            });
            photoOutput.on('error', (data: BusinessError): void => {
                CLLog.logg(`photoOutPut data: ${JSON.stringify(data)}`);
            });
            photoOutput.on('photoAssetAvailable', (err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset) => {
                CLLog.logg('photoAssetAvailable begin');
                if (photoAsset === undefined) {
                    CLLog.error('photoAsset is undefined');
                    return;
                }
                this.handlePhotoAssetCb(photoAsset);
            });
        }
        catch (err) {
            CLLog.error('photoOutputCallBack error');
        }
    }
    videoOutCallBack(videoOut: camera.VideoOutput): void {
        try {
            videoOut.on("frameStart", () => {
                CLLog.logg(`videoOut data: frameStart`);
            });
            videoOut.on("frameEnd", () => {
                CLLog.logg(`videoOut data: frameEnd`);
            });
            videoOut.on('error', (data: BusinessError): void => {
                CLLog.logg(`videoOut data: ${JSON.stringify(data)}`);
            });
        }
        catch (e) {
            CLLog.error('videoOutCallBack error');
        }
    }
    /**
     * Monitor preview events.
     */
    previewOutputCallBack(previewOutput: camera.PreviewOutput): void {
        CLLog.logg('previewOutputCallBack is called');
        try {
            previewOutput.on('frameStart', (): void => {
                CLLog.logg('Preview frame started');
            });
            previewOutput.on('frameEnd', (): void => {
                CLLog.logg('Preview frame ended');
            });
            previewOutput.on('error', (previewOutputError: BusinessError): void => {
                CLLog.logg(`Preview output previewOutputError: ${JSON.stringify(previewOutputError)}`);
            });
        }
        catch (err) {
            CLLog.error('previewOutputCallBack error');
        }
    }
    /**
     * Callback function for registering camera status changes.
     * @param err
     * @param cameraStatusInfo
     * @returns
     */
    registerCameraStatusChange(err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo): void {
        CLLog.logg(`cameraId: ${cameraStatusInfo.camera.cameraId},status: ${cameraStatusInfo.status}`);
    }
    /**
     * Monitor camera status changes.
     * @param cameraManager
     * @returns
     */
    onCameraStatusChange(cameraManager: camera.CameraManager): void {
        CLLog.logg('onCameraStatusChange is called');
        try {
            cameraManager.on('cameraStatus', this.registerCameraStatusChange);
        }
        catch (error) {
            CLLog.error('onCameraStatusChange error');
        }
    }
    /**
     * Stop monitoring camera status changes.
     * @returns
     */
    offCameraStatusChange(): void {
        CLLog.logg('offCameraStatusChange is called');
        this.cameraManager?.off('cameraStatus', this.registerCameraStatusChange);
    }
    /**
     * Monitor camera input changes.
     * @param cameraInput
     * @param cameraDevice
     * @returns
     */
    onCameraInputChange(cameraInput: camera.CameraInput, cameraDevice: camera.CameraDevice): void {
        CLLog.logg(`onCameraInputChange is called`);
        try {
            cameraInput.on('error', cameraDevice, (cameraInputError: BusinessError): void => {
                CLLog.logg(`onCameraInputChange cameraInput error code: ${cameraInputError.code}`);
            });
        }
        catch (error) {
            CLLog.error('onCameraInputChange error');
        }
    }
    /**
     * @param session
     * @returns
     */
    onSessionErrorChange(session: camera.PhotoSession | camera.VideoSession): void {
        try {
            session.on('error', (captureSessionError: BusinessError): void => {
                CLLog.logg('onCaptureSessionErrorChange captureSession fail: ' + JSON.stringify(captureSessionError.code));
            });
        }
        catch (error) {
            CLLog.error('onCaptureSessionErrorChange error');
        }
    }
    async createAVRecorder(): Promise<media.AVRecorder | undefined> {
        let avRecorder: media.AVRecorder | undefined = undefined;
        try {
            avRecorder = await media.createAVRecorder();
        }
        catch (error) {
            CLLog.error(`createAVRecorder error: ${error}`);
        }
        return avRecorder;
    }
    initFd(): number {
        CLLog.logg('initFd is called');
        let filesDir = this.context?.filesDir;
        let filePath = filesDir + `/${Date.now()}.mp4`;
        AppStorage.setOrCreate<string>('cl_real_filePath', filePath);
        let file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
        return file.fd;
    }
    async prepareAVRecorder(): Promise<void> {
        CLLog.logg('prepareAVRecorder is called');
        let fd = this.initFd();
        let videoConfig: media.AVRecorderConfig = {
            audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
            videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
            profile: {
                audioBitrate: Constants.AUDIO_BITRATE,
                audioChannels: Constants.AUDIO_CHANNELS,
                audioCodec: media.CodecMimeType.AUDIO_AAC,
                audioSampleRate: Constants.AUDIO_SAMPLE_RATE,
                fileFormat: media.ContainerFormatType.CFT_MPEG_4,
                videoBitrate: Constants.VIDEO_BITRATE,
                videoCodec: media.CodecMimeType.VIDEO_AVC,
                videoFrameWidth: this.videoProfileObj.size.width,
                videoFrameHeight: this.videoProfileObj.size.height,
                videoFrameRate: this.videoProfileObj.frameRateRange.max
            },
            url: `fd://${fd.toString()}`,
            rotation: this.curCameraDevice?.cameraOrientation
        };
        CLLog.logg(`prepareAVRecorder videoConfig: ${JSON.stringify(videoConfig)}`);
        await this.avRecorder?.prepare(videoConfig).catch((err: BusinessError): void => {
            CLLog.error(`prepareAVRecorder prepare err: ${JSON.stringify(err)}`);
        });
    }
    async startVideo(): Promise<void> {
        CLLog.logg('startVideo is called');
        try {
            await this.videoOutput?.start();
            await this.avRecorder?.start();
            this.isRecording = true;
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`startVideo err: ${JSON.stringify(err)}`);
        }
        CLLog.logg('startVideo End of call');
    }
    async stopVideo(): Promise<string> {
        CLLog.logg('stopVideo is called');
        if (!this.isRecording) {
            CLLog.logg('not in recording');
        }
        try {
            if (this.previewOutput) {
                await this.previewOutput;
            }
            if (this.avRecorder) {
                await this.avRecorder.stop();
            }
            if (this.videoOutput) {
                await this.videoOutput.stop();
            }
            this.isRecording = false;
            return new Promise((race) => {
                const path = AppStorage.get<string>('cl_real_filePath');
                if (path && path.length > 0) {
                    const url = path;
                    race(url);
                }
                else {
                    race('');
                }
            });
        }
        catch (error) {
            let err = error as BusinessError;
            CLLog.error(`stopVideo err: ${JSON.stringify(err)}`);
        }
        CLLog.logg('stopVideo End of call');
        return '';
    }
    /**
     * Flashing lights
     */
    hasFlashFn(flashMode: camera.FlashMode): void {
        let hasFlash = this.session?.hasFlash();
        CLLog.debug(`hasFlash success, hasFlash: ${hasFlash}`);
        let isFlashModeSupported = this.session?.isFlashModeSupported(flashMode);
        CLLog.debug(`isFlashModeSupported success, isFlashModeSupported: ${isFlashModeSupported}`);
        this.session?.setFlashMode(flashMode);
    }
    getFlashMode(): camera.FlashMode | undefined {
        return this.session?.getFlashMode();
    }
    setFlashMode() {
        if (this.session?.getFlashMode() === camera.FlashMode.FLASH_MODE_OPEN || this.session?.getFlashMode() === camera.FlashMode.FLASH_MODE_ALWAYS_OPEN) {
            this.hasFlashFn(camera.FlashMode.FLASH_MODE_CLOSE);
        }
        else {
            this.hasFlashFn(camera.FlashMode.FLASH_MODE_ALWAYS_OPEN);
        }
    }
    /**
     * Set the current focus
     */
    setFocusPoint(point: camera.Point): void {
        this.session?.setFocusPoint(point);
        CLLog.info(`setFocusPoint success point: ${JSON.stringify(point)}`);
        // Get the current focus
        let nowPoint: camera.Point | undefined = undefined;
        nowPoint = this.session?.getFocusPoint();
        CLLog.info(`getFocusPoint success, nowPoint: ${JSON.stringify(nowPoint)}`);
    }
    /**
     * Exposure area
     */
    isMeteringPoint(point: camera.Point): void {
        let exposureMode: camera.ExposureMode | undefined = undefined;
        exposureMode = this.session?.getExposureMode();
        CLLog.info(`getExposureMode success, exposureMode: ${exposureMode}`);
        this.session?.setMeteringPoint(point);
        let exposurePoint: camera.Point | undefined = undefined;
        exposurePoint = this.session?.getMeteringPoint();
        CLLog.info(`getMeteringPoint exposurePoint: ${JSON.stringify(exposurePoint)}`);
    }
    /**
     * Exposure compensation
     */
    isExposureBiasRange(exposureBias: number): void {
        CLLog.debug(`setExposureBias value ${exposureBias}`);
        let biasRangeArray: Array<number> | undefined = [];
        biasRangeArray = this.session?.getExposureBiasRange();
        CLLog.debug(`getExposureBiasRange success, biasRangeArray: ${JSON.stringify(biasRangeArray)}`);
        this.session?.setExposureBias(exposureBias);
    }
    setFocusMode(focusMode: camera.FocusMode): void {
        CLLog.info(`setFocusMode is called`);
        let isSupported = this.session?.isFocusModeSupported(focusMode);
        CLLog.info(`setFocusMode isSupported: ${isSupported}`);
        if (!isSupported) {
            return;
        }
        this.session?.setFocusMode(focusMode);
    }
    async getImageUint8Array(uri: string): Promise<Uint8Array> {
        const file = await fs.open(uri, fs.OpenMode.READ_ONLY);
        const imageSource: image.ImageSource = image.createImageSource(file.fd);
        const imagePackerApi = image.createImagePacker();
        // 设置打包参数
        // format：当前仅支持打包为JPEG、WebP 和 png 格式
        // quality：JPEG 编码输出图片质量
        // bufferSize：图片大小，默认 10M
        const packOpts: image.PackingOption = { format: "image/jpeg", quality: 100 };
        let imageBuffer: ArrayBuffer = new ArrayBuffer(1);
        try {
            // 图片压缩或重新打包
            imageBuffer = await imagePackerApi.packing(imageSource, packOpts);
            // let base64Str = buffer.from(imageBuffer).toString('base64')
            // resultBase64Str="data:image/jpeg;base64,"+base64Str
        }
        catch (err) {
            console.error(`Invoke getImageArrayBufferWithUri failed, err: ${JSON.stringify(err)}`);
        }
        return new Uint8Array(imageBuffer);
    }
    openPhotoAlbum(call: (imageURL: string, error?: Error) => void) {
        try {
            let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
            photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
            photoSelectOptions.maxSelectNumber = 1;
            let photoPicker = new photoAccessHelper.PhotoViewPicker();
            photoPicker.select(photoSelectOptions)
                .then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
                let imageURI = PhotoSelectResult.photoUris[0];
                call(imageURI);
            });
        }
        catch (error) {
            call('', error);
        }
    }
}
export default new CLCamreManager();
