import {ImageSourceAdapter, type DSRect} from "../core";
import {NativeModules, Platform} from "react-native";
import {CameraView} from "./CameraView"
import {EnumCameraPosition} from "./EnumCameraPosition";
import type {EnumFocusMode} from "./EnumFocusMode";
import type {EnumEnhancedFeatures} from "./EnumEnhancedFeatures";
import type {EnumResolution} from "./EnumResolution";
import type {Rect} from "../core/Rect";
import type {Point} from "../core/Point";

// @ts-ignore Check whether __turboModuleProxy exists, it may not
const isTurboModuleEnabled = global.__turboModuleProxy != null;

const DynamsoftCameraEnhancerModule = isTurboModuleEnabled ?
  require("./NativeDynamsoftCameraViewModule").default :
  NativeModules.DynamsoftCameraView;

/**
 * The singleton instance of CameraEnhancer.
 * */
let dce: CameraEnhancer | null;

/**
 * The CameraEnhancer class is the primary class of Dynamsoft Camera Enhancer that defines the camera controlling APIs.
 * It is a subclass of {@link ImageSourceAdapter}.
 * <p>
 * In js end of react-native, CameraEnhancer uses singleton mode. You can only get CaptureVisionRouter instance by {@link CameraEnhancer.getInstance}.
 * @hideconstructor
 * */
export class CameraEnhancer extends ImageSourceAdapter {
  private constructor() {
    super();
  }

  protected init() {
    this.isaId = DynamsoftCameraEnhancerModule.createInstance()
  }

  /**Trigger to request the camera permission.*/
  static async requestCameraPermission() {
    if (Platform.OS === "android") {
      DynamsoftCameraEnhancerModule.requestCameraPermission()
    }
  }

  /**
   * Get the singleton instance of CameraEnhancer.
   * <p>
   * This method ensures that only one instance of CameraEnhancer is created
   * and reused throughout the application. If an instance already exists, it will
   * return the existing one. Otherwise, it will create a new instance and return it.
   *
   * @returns {CameraEnhancer} The singleton instance of CameraEnhancer.
   * */
  static getInstance(): CameraEnhancer {
    if (dce) {
      return dce;
    }
    return dce = new CameraEnhancer();
  }

  /**
   * Destroys the CameraEnhancer instance and releases all associated resources.
   * After calling this method, the instance will be set to null and cannot be used anymore.
   * Only call this method when you want to completely dispose the CameraEnhancer instance, otherwise just call {@link CameraEnhancer.close} to stop the camera and release the video stream.
   */
  dispose() {
    DynamsoftCameraEnhancerModule.destroyInstance(this.isaId);
    dce = null;
  }

  /**Opens the currently selected camera and starts the video stream.*/
  open() {
    DynamsoftCameraEnhancerModule.open();
  }

  /**Closes the currently active camera and stops the video stream.*/
  close() {
    DynamsoftCameraEnhancerModule.close();
  }

  /**
   * Bind a CameraView instance with this CameraEnhancer instance.
   *
   * <p>Code Snippet:</p>
   *
   *```
   * function App(): React.JSX.Element {
   *      const cameraView = useRef<CameraView>(null);
   *      const camera = CameraEnhancer.getInstance();
   *      useEffect(() => {
   *          camera.setCameraView(cameraView.current!!);
   *          camera.open();
   *      }, [camera, cameraView])
   *      return (<CameraView style={{flex:1}} ref={cameraView}/>);
   * }
   * ```
   *
   * @param view - the CameraView element
   * */
  setCameraView(view: CameraView | null) {
    DynamsoftCameraEnhancerModule.setCameraView(view ? view.nativeCameraViewHandle : 0);
  }

  /**
   * Select a camera with a camera position.
   * @param position - One of the {@link EnumCameraPosition}
   * @note {@link EnumCameraPosition.CP_BACK_DUAL_WIDE_AUTO} and {@link EnumCameraPosition.CP_BACK_ULTRA_WIDE} are only supported on iOS.
   * */
  selectCamera(position: EnumCameraPosition | number) {
    if(Platform.OS !== "ios") {
      if (position === EnumCameraPosition.CP_BACK_DUAL_WIDE_AUTO || position === EnumCameraPosition.CP_BACK_ULTRA_WIDE) {
        position = EnumCameraPosition.CP_BACK;
      }
    }
    DynamsoftCameraEnhancerModule.selectCamera(position);
  }

  /**
   * Get the camera position.
   * @return {Promise<EnumCameraPosition|number>} - A promise that resolves {@link EnumCameraPosition.CP_BACK} or {@link EnumCameraPosition.CP_FRONT}.
   * */
  getCameraPosition(): Promise<EnumCameraPosition | number> {
    return DynamsoftCameraEnhancerModule.getCameraPosition();
  }

  /**
   * Set the focus point of interest and trigger an one-off auto-focus.
   * After the focus, you can either lock the focalngth or keep the continuous auto focus enabled by configuring the subsequent focus mode.
   * @param floatX - The x of focus point of interest. The coordinate base of the point is "image".
   * @param floatY - The y of focus point of interest. The coordinate base of the point is "image".
   * @param focusMode The subsequent focus mode.
   * @see EnumFocusMode
   * */
  setFocus(floatX: number, floatY: number, focusMode: EnumFocusMode | number) {
    DynamsoftCameraEnhancerModule.setFocus(floatX, floatY, focusMode);
  }

  /**
   * Get the currently actived focus mode.
   *  @return {Promise<EnumFocusMode|number>} - A promise that resolves one of {@link EnumFocusMode}.
   * */
  getFocusMode(): Promise<EnumFocusMode | number> {
    return DynamsoftCameraEnhancerModule.getFocusMode();
  }

  /**
   * Set the zoom factor of the camera.
   * @param factor - The zoom factor.
   * */
  setZoomFactor(factor: number) {
    DynamsoftCameraEnhancerModule.setZoomFactor(factor);
  }

  /**
   * Get the zoom factor of the camera.
   * @return {Promise<number>} - A promise that resolves current zoom factor.
   * */
  getZoomFactor(): Promise<number> {
    return DynamsoftCameraEnhancerModule.getZoomFactor();
  }

  /**
   * Enable the specified enhanced features. View {@link EnumEnhancedFeatures} to learn about these enhanced features.
   * By default, these enhanced features are all disabled.
   * <p>Code Snippet:</p>
   *
   *```
   * cameraEnhancer.enableEnhancedFeatures(EnumEnhancedFeatures.EF_AUTO_ZOOM | EnumEnhancedFeatures.EF_FRAME_FILTER)
   * ```
   * @param features - An enum value or a bitwise combination of EnumEnhancedFeatures which indicates a series of enhanced features.
   * @see {@link EnumEnhancedFeatures}
   * */
  enableEnhancedFeatures(features: EnumEnhancedFeatures | number) {
    DynamsoftCameraEnhancerModule.enableEnhancedFeatures(features);
  }

  /**
   * Disables one or more previously enabled enhanced features.
   * <p>Code Snippet:</p>
   *
   *```
   * cameraEnhancer.disableEnhancedFeatures(EnumEnhancedFeatures.EF_AUTO_ZOOM | EnumEnhancedFeatures.EF_FRAME_FILTER)
   * ```
   * @param features - An enum value or a bitwise combination of EnumEnhancedFeatures indicating the features to be disabled.
   * @see {@link EnumEnhancedFeatures}
   * */
  disableEnhancedFeatures(features: EnumEnhancedFeatures | number) {
    DynamsoftCameraEnhancerModule.disableEnhancedFeatures(features);
  }

  /**
   * Sets the scan region within the camera's view which limits the frame acquisition to a specific area of the video feed.
   * <p>Code Snippet:</p>
   *
   *```
   * let scanRegion = {
   *    top: 0.25,
   *    bottom: 0.75,
   *    left: 0.25,
   *    right: 0.75,
   *    measuredInPercentage: true,
   * }
   * cameraEnhancer.setScanRegion(scanRegion);
   * //...
   * cameraEnhancer.setScanRegion(null); //Cancel the scan region
   * ```
   *
   * @param region - specifies the scan region. If null or undefined, cancel the scan region.
   * @remarks The region is always specified relative to the original video size, regardless of any transformations or zoom applied to the video display.
   * @see {@link DSRect}
   * */
  setScanRegion(region: DSRect | null | undefined) {
    DynamsoftCameraEnhancerModule.setScanRegion(region);
  }

  /**
   * Retrieves the current scan region.
   * @return {Promise<DSRect|null|undefined>} - A promise that resolves current scan region.
   * @see {@link DSRect}
   * */
  getScanRegion(): Promise<DSRect | null | undefined> {
    return DynamsoftCameraEnhancerModule.getScanRegion()
  }

  /**
   * Turn on the torch.
   * */
  turnOnTorch() {
    DynamsoftCameraEnhancerModule.turnOnTorch()
  }

  /**
   * Turn off the torch.
   * */
  turnOffTorch() {
    DynamsoftCameraEnhancerModule.turnOffTorch()
  }

  /**
   * Set the resolution of the camera.
   * @param resolution - One of the {@link EnumResolution}.
   * */
  setResolution(resolution: EnumResolution) {
    DynamsoftCameraEnhancerModule.setResolution(resolution);
  }

  /**
   * Converts a DSRect to view coordinates.
   * @param rect - The DSRect to be converted.
   * @return {Rect} - A Rect in view coordinates.
   * */
  convertRectToViewCoordinates(rect: DSRect): Rect {
    return DynamsoftCameraEnhancerModule.convertRectToViewCoordinates(rect);
  }

  /**
   * Converts a point to view coordinates.
   * @param point - The point to be converted.
   * @return {Point} - A point in view coordinates.
   * */
  convertPointToViewCoordinates(point: Point): Point {
    return DynamsoftCameraEnhancerModule.convertPointToViewCoordinates(point);
  }
}
