import React, { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
import { NativeSyntheticEvent, StyleSheet, View } from 'react-native';
import { DeepPartial, autorelease, createSBError } from '../../types';
import NativeCroppingView, {
  Commands,
  CroppingViewEvent,
} from '../spec/ScanbotCroppingViewNativeComponent';
import {
  CroppingViewResult,
  CroppingViewRotation,
  FileSrc,
  ImageSrc,
  ScanbotCroppingViewHandle,
  ScanbotCroppingViewProperties,
} from './ScanbotCroppingViewProperties';

function isFileSrc(src: ImageSrc): src is FileSrc {
  return (src as FileSrc).imageFileUri !== undefined;
}

export const ScanbotCroppingView = forwardRef<
  ScanbotCroppingViewHandle,
  ScanbotCroppingViewProperties
>(({ onCroppedAreaResult, onError, ...props }, forwardedRef) => {
  const viewRef = useRef(null);

  useImperativeHandle(forwardedRef, () => {
    return {
      detectPolygon() {
        if (viewRef.current) {
          Commands.detectPolygon(viewRef.current);
        }
      },
      resetPolygon() {
        if (viewRef.current) {
          Commands.resetPolygon(viewRef.current);
        }
      },
      rotate(rotation: CroppingViewRotation) {
        if (viewRef.current) {
          Commands.rotate(viewRef.current, rotation === 'CLOCKWISE');
        }
      },
      extractCroppedArea() {
        if (viewRef.current) {
          Commands.extractCroppedArea(viewRef.current);
        }
      },
    };
  });

  const _nativeViewSrc = useMemo(() => {
    if (isFileSrc(props.src)) {
      return {
        imageFileUri: props.src.imageFileUri,
        imageRef: undefined,
      };
    } else {
      return {
        imageFileUri: undefined,
        imageRef: props.src.imageRefUUID,
      };
    }
  }, [props.src]);

  const _onCroppedAreaResult = useCallback(
    (event: NativeSyntheticEvent<CroppingViewEvent>) => {
      if (event.nativeEvent.result !== null) {
        autorelease(async () => {
          await onCroppedAreaResult(
            new CroppingViewResult(
              JSON.parse(event.nativeEvent.result) as DeepPartial<CroppingViewResult>
            )
          );
        });
      }
    },
    [onCroppedAreaResult]
  );

  const _onError = useCallback(
    (event: NativeSyntheticEvent<any>) => {
      if (onError) {
        onError(createSBError(event.nativeEvent));
      }
    },
    [onError]
  );

  return (
    <View style={[styles.container, props.style]}>
      <NativeCroppingView
        ref={viewRef}
        imageFileSrc={_nativeViewSrc.imageFileUri}
        imageRefSrc={_nativeViewSrc.imageRef}
        onCroppedAreaResult={_onCroppedAreaResult}
        onError={_onError}
        edgeColor={props.edgeColor}
        edgeLineWidth={props.edgeLineWidth}
        edgeColorOnLine={props.edgeColorOnLine}
        anchorPointsColor={props.anchorPointsColor}
        style={styles.container}
      />
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
