import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
import NativeBarcodeCameraView, {
  Commands,
} from '../spec/ScanbotBarcodeScannerViewNativeComponent';
import {
  ScanbotBarcodeCameraViewHandle,
  ScanbotBarcodeCameraViewProperties,
} from './ScanbotBarcodeCameraViewProperties';
import { NativeSyntheticEvent, Platform, StyleSheet, View } from 'react-native';
import { BarcodeScannerResult } from '../../results';
import { BarcodeResultField } from '../../types';

export const ScanbotBarcodeCameraView = forwardRef<
  ScanbotBarcodeCameraViewHandle,
  ScanbotBarcodeCameraViewProperties
>(({ onBarcodeScannerResult, ...props }, forwardedRef) => {
  const viewRef = useRef(null);
  const boundBarcodeItemOverlayViews = useRef<Map<string, number>>(new Map());

  useImperativeHandle(forwardedRef, () => {
    return {
      freezeCamera() {
        if (viewRef.current) {
          //@ts-ignore
          Commands.freezeCamera(viewRef.current);
        }
      },

      unfreezeCamera() {
        if (viewRef.current) {
          //@ts-ignore
          Commands.unfreezeCamera(viewRef.current);
        }
      },
    };
  }, []);

  const _onBarcodeScannerResult = useCallback(
    async (event: NativeSyntheticEvent<any>) => {
      const result = event.nativeEvent.result;
      const selectionOverlayAutoSelectDisabled =
        props.selectionOverlayConfig?.overlayEnabled &&
        !props.selectionOverlayConfig?.automaticSelectionEnabled;

      if (result) {
        const barcodeScannerResult = (
          Platform.OS === 'ios' ? JSON.parse(result) : result
        ) as BarcodeScannerResult;
        if (!selectionOverlayAutoSelectDisabled) {
          onBarcodeScannerResult(barcodeScannerResult);
        }

        if (props.selectionOverlayConfig?.barcodeItemOverlayViewBinder) {
          for (let barcode of barcodeScannerResult.barcodes!!) {
            const barcodeItemUuid = barcode.textWithExtension + '_' + barcode.type;

            const barcodeItemNextBind = boundBarcodeItemOverlayViews.current.get(barcodeItemUuid);

            if (
              barcodeItemNextBind === undefined ||
              (barcodeItemNextBind !== -1 && Date.now() >= barcodeItemNextBind)
            ) {
              boundBarcodeItemOverlayViews.current.set(barcodeItemUuid, -1);

              const bindingConfig =
                await props.selectionOverlayConfig.barcodeItemOverlayViewBinder(barcode);

              if (bindingConfig.refreshRate !== undefined && bindingConfig.refreshRate > 0) {
                boundBarcodeItemOverlayViews.current.set(
                  barcodeItemUuid,
                  Date.now() + bindingConfig.refreshRate
                );
              }

              if (viewRef.current != null) {
                Commands.bindBarcodeItemOverlayView(
                  //@ts-ignore
                  viewRef.current,
                  barcodeItemUuid,
                  JSON.stringify(bindingConfig)
                );
              }
            }
          }
        }
      }
    },
    [props.selectionOverlayConfig, boundBarcodeItemOverlayViews, onBarcodeScannerResult]
  );

  const _onBarcodeSelectionResult = useCallback(
    (event: NativeSyntheticEvent<any>) => {
      const result = event.nativeEvent.result;
      const selectionOverlayAutoSelectDisabled =
        props.selectionOverlayConfig?.overlayEnabled &&
        !props.selectionOverlayConfig?.automaticSelectionEnabled;
      if (selectionOverlayAutoSelectDisabled && result && Platform.OS === 'android') {
        const barcodeItemResult = result as BarcodeResultField;
        onBarcodeScannerResult({ barcodes: [barcodeItemResult] });
      } else if (selectionOverlayAutoSelectDisabled && result && Platform.OS === 'ios') {
        const barcodeResult = JSON.parse(result) as BarcodeScannerResult;
        onBarcodeScannerResult(barcodeResult);
      }
    },
    [
      onBarcodeScannerResult,
      props.selectionOverlayConfig?.automaticSelectionEnabled,
      props.selectionOverlayConfig?.overlayEnabled,
    ]
  );

  return (
    <View style={[styles.container, props.style]}>
      <View
        pointerEvents={'box-none'}
        style={[styles.contentContainerStyle, props.contentContainerStyle]}
      >
        {props.children}
      </View>
      <NativeBarcodeCameraView
        //FinderConfigs
        finderEnabled={props.finderConfig?.viewFinderEnabled}
        finderStrokeWidth={props.finderConfig?.finderLineWidth}
        finderMinPadding={props.finderConfig?.minPadding}
        finderStrokeColor={props.finderConfig?.finderLineColor}
        finderOverlayColor={props.finderConfig?.overlayColor}
        finderInset={props.finderConfig?.finderInset}
        finderRequiredAspectRatios={props.finderConfig?.requiredAspectRatio}
        //CameraConfigs
        cameraZoomFactor={Platform.select({
          ios: props.cameraConfig?.cameraZoomFactor
            ? parseFloat(props.cameraConfig?.cameraZoomFactor.toFixed(2))
            : undefined,
          android:
            props.cameraConfig?.cameraZoomFactor !== undefined
              ? (props.cameraConfig?.cameraZoomRange?.minZoom || 1) +
                ((props.cameraConfig?.cameraZoomRange?.maxZoom || 12) -
                  (props.cameraConfig?.cameraZoomRange?.minZoom || 1)) *
                  props.cameraConfig?.cameraZoomFactor
              : undefined,
        })}
        cameraZoomRange={props.cameraConfig?.cameraZoomRange}
        minFocusDistanceLock={props.cameraConfig?.minFocusDistanceLock}
        cameraModule={props.cameraConfig?.cameraModule}
        //DetectorConfigs
        detectorConfig={props.detectorConfig}
        //SelectionOverlayConfig
        overlayEnabled={props.selectionOverlayConfig?.overlayEnabled}
        overlayPolygonColor={props.selectionOverlayConfig?.polygonColor}
        overlayStrokeColor={props.selectionOverlayConfig?.strokeColor}
        overlayTextFormat={props.selectionOverlayConfig?.textFormat}
        overlayTextColor={props.selectionOverlayConfig?.textColor}
        overlayTextContainerColor={props.selectionOverlayConfig?.textContainerColor}
        overlayLoadingTextValue={props.selectionOverlayConfig?.loadingTextValue}
        overlayBarcodeItemOverlayViewBinder={
          props.selectionOverlayConfig?.barcodeItemOverlayViewBinder !== undefined
        }
        //Other
        flashEnabled={props.flashEnabled}
        scanningEnabled={props.scanningEnabled}
        //View
        style={styles.container}
        ref={viewRef}
        //Result
        onBarcodeScannerResult={_onBarcodeScannerResult}
        onSelectBarcodeResult={_onBarcodeSelectionResult}
      />
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainerStyle: {
    position: 'absolute',
    width: '100%',
    height: '100%',
    zIndex: 2,
  },
});
