import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView';
import {RouteProp} from '@react-navigation/core';
import {StackNavigationProp} from '@react-navigation/stack';
import React, {useEffect, useRef, useState} from 'react';
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import ImageZoom, {IOnMove} from 'react-native-image-pan-zoom';
import PhotoManipulator from 'react-native-photo-manipulator';
import TrimIcon from '../../assets/icons/trim.svg';
import CloseIcon from '../../assets/ionicons/close-outline.svg';
import {SaveButton, TrimmerPlayer} from '../../components';
import {TaggLoadingIndicator, TaggSquareButton} from '../../components/common';
import {MainStackParams} from '../../routes';
import {
  cropVideo,
  HeaderHeight,
  normalize,
  saveImageToGallery,
  SCREEN_HEIGHT,
  SCREEN_WIDTH,
  trimVideo,
} from '../../utils';

type EditMediaRouteProps = RouteProp<MainStackParams, 'EditMedia'>;
type EditMediaNavigationProps = StackNavigationProp<
  MainStackParams,
  'EditMedia'
>;
interface EditMediaProps {
  route: EditMediaRouteProps;
  navigation: EditMediaNavigationProps;
}

export const EditMedia: React.FC<EditMediaProps> = ({route, navigation}) => {
  const {screenType, media, selectedCategory} = route.params;
  const [aspectRatio, setAspectRatio] = useState<number>(1);
  // width and height of video, if video
  const [origDimensions, setOrigDimensions] = useState<number[]>([0, 0]);
  const vidRef = useRef<View>(null);
  const [cropLoading, setCropLoading] = useState<boolean>(false);
  const [hideTrimmer, setHideTrimmer] = useState<boolean>(true);

  // Stores the coordinates of the cropped image
  const [x0, setX0] = useState<number>();
  const [x1, setX1] = useState<number>();
  const [y0, setY0] = useState<number>();
  const [y1, setY1] = useState<number>();

  // Stores crop information for video
  const [videoCrop, setVideoCrop] = useState<{
    cropWidth?: number;
    cropHeight?: number;
    cropOffsetX?: number;
    cropOffsetY?: number;
  }>({});

  // Stores the current trim endpoints
  const [trimEnds, setTrimEnds] = useState<{
    end: number;
    start: number;
  }>({
    end: 60,
    start: 0,
  });

  // Setting original aspect ratio of image
  useEffect(() => {
    if (media.uri && !media.isVideo) {
      Image.getSize(
        media.uri,
        (w, h) => {
          setAspectRatio(w / h);
        },
        (err) => console.log(err),
      );
    } else if (media.uri && media.isVideo) {
      setVideoCrop((prevState) => ({
        ...prevState,
        cropWidth: origDimensions[0],
        cropHeight: origDimensions[1],
      }));
    }
  }, []);

  // Possible need to delay setting aspect ratio of video until loaded
  useEffect(() => {
    if (media.uri && media.isVideo) {
      setVideoCrop((prevState) => ({
        ...prevState,
        cropWidth: origDimensions[0],
        cropHeight: origDimensions[1],
      }));
    }
  }, [origDimensions]);

  // Crops original image based of (x0, y0) and (x1, y1) coordinates
  const processVideo = (callback: (finalUri: string) => void) => {
    if (!media.isVideo) {
      if (
        x0 !== undefined &&
        x1 !== undefined &&
        y0 !== undefined &&
        y1 !== undefined
      ) {
        PhotoManipulator.crop(media.uri, {
          x: x0,
          y: y1,
          width: Math.abs(x0 - x1),
          height: Math.abs(y0 - y1),
        })
          .then((croppedURL) => {
            // Pass the cropped image
            callback(croppedURL);
          })
          .catch((err) => console.log('err: ', err));
      } else if (
        x0 === undefined &&
        x1 === undefined &&
        y0 === undefined &&
        y1 === undefined
      ) {
        // If no crop coordinates are set, then we will just pass the original image
        callback(media.uri);
      }
    } else {
      if (!videoCrop.cropHeight || !videoCrop.cropWidth) {
        setVideoCrop((prevState) => ({
          ...prevState,
          cropWidth: origDimensions[0],
          cropHeight: origDimensions[1],
        }));
      }
      setCropLoading(true);
      trimVideo(
        media.uri,
        (trimmedURL: string) =>
          cropVideo(
            trimmedURL,
            (croppedURL: string) => {
              setCropLoading(false);
              // Pass the trimmed/cropped video
              callback(croppedURL);
            },
            videoCrop,
          ),
        trimEnds,
      );
    }
  };

  // for whenever the video is altered by the user
  const onVideoMove = (zoomableEvent: any) => {
    const {originalHeight, originalWidth} = zoomableEvent;

    let cropWidth = 0;
    let cropHeight = 0;
    let cropOffsetX = 0;
    let cropOffsetY = 0;

    if (vidRef !== null && vidRef.current !== null) {
      vidRef.current.measure(
        (
          _x: number,
          _y: number,
          width: number,
          height: number,
          pageX: number,
          pageY: number,
        ) => {
          // width
          cropWidth = origDimensions[0] * (originalWidth / width);

          // offsetX
          cropOffsetX = -1 * origDimensions[0] * (pageX / width);
          if (cropOffsetX < 0) {
            cropOffsetX = 0;
          } else if (cropOffsetX + cropWidth > origDimensions[0] - 1) {
            cropOffsetX = origDimensions[0] - cropWidth - 1;
          }

          // height
          if (
            height * (SCREEN_WIDTH / aspectRatio / originalHeight) >
            SCREEN_HEIGHT
          ) {
            const superHeight = width / aspectRatio;
            cropHeight = origDimensions[1] * (originalHeight / superHeight);

            // offsetY
            const topDeadZone = (height - superHeight) / 2;
            const offsetY = topDeadZone + pageY;
            cropOffsetY = -1 * origDimensions[1] * (offsetY / superHeight);
            if (cropOffsetY < 0) {
              cropOffsetY = 0;
            } else if (cropOffsetY + cropHeight > origDimensions[1]) {
              cropOffsetY = origDimensions[1] - cropHeight - 1;
            }
          } else {
            cropHeight = origDimensions[1];
          }
          setVideoCrop((prevState) => ({
            ...prevState,
            cropWidth: cropWidth,
            cropHeight: cropHeight,
            cropOffsetX: cropOffsetX,
            cropOffsetY: cropOffsetY,
          }));
        },
      );
    }
  };

  /* Records (x0, y0) and (x1, y1) coordinates used later for cropping,
   * based on(x, y) - the center of the image and scale of zoom
   */
  const onMove = (position: IOnMove) => {
    Image.getSize(
      media.uri,
      (w, h) => {
        const x = position.positionX;
        const y = position.positionY;
        const scale = position.scale;
        const screen_ratio = SCREEN_HEIGHT / SCREEN_WIDTH;
        let tempx0 = w / 2 - x * (w / SCREEN_WIDTH) - w / 2 / scale;
        let tempx1 = w / 2 - x * (w / SCREEN_WIDTH) + w / 2 / scale;
        if (tempx0 < 0) {
          tempx0 = 0;
        }
        if (tempx1 > w) {
          tempx1 = w;
        }
        const x_distance = Math.abs(tempx1 - tempx0);
        const y_distance = screen_ratio * x_distance;
        let tempy0 = h / 2 - y * (h / SCREEN_HEIGHT) + y_distance / 2;
        let tempy1 = h / 2 - y * (h / SCREEN_HEIGHT) - y_distance / 2;
        if (tempy0 > h) {
          tempy0 = h;
        }
        if (tempy1 < 0) {
          tempy1 = 0;
        }
        setX0(tempx0);
        setX1(tempx1);
        setY0(tempy0);
        setY1(tempy1);
      },
      (err) => console.log(err),
    );
  };

  return (
    <View style={styles.container}>
      {cropLoading && <TaggLoadingIndicator fullscreen />}
      <TouchableOpacity
        style={styles.closeButton}
        onPress={() => navigation.goBack()}>
        <CloseIcon height={25} width={25} color={'white'} />
      </TouchableOpacity>
      {!media.isVideo ? (
        <ImageZoom
          style={styles.zoomView}
          cropWidth={SCREEN_WIDTH}
          cropHeight={SCREEN_HEIGHT}
          imageWidth={SCREEN_WIDTH}
          imageHeight={SCREEN_WIDTH / aspectRatio}
          onMove={onMove}>
          <Image
            style={{width: SCREEN_WIDTH, height: SCREEN_WIDTH / aspectRatio}}
            source={{
              uri: media.uri,
            }}
          />
        </ImageZoom>
      ) : (
        <ReactNativeZoomableView
          maxZoom={10}
          minZoom={1}
          zoomStep={0.5}
          initialZoom={1}
          bindToBorders={true}
          onDoubleTapAfter={(
            _1: any,
            _2: any,
            zoomableViewEventObject: any,
          ) => {
            onVideoMove(zoomableViewEventObject);
          }}
          onShiftingAfter={(_1: any, _2: any, zoomableViewEventObject: any) => {
            onVideoMove(zoomableViewEventObject);
          }}
          onShiftingEnd={(_1: any, _2: any, zoomableViewEventObject: any) => {
            onVideoMove(zoomableViewEventObject);
          }}
          onZoomAfter={(_1: any, _2: any, zoomableViewEventObject: any) => {
            onVideoMove(zoomableViewEventObject);
          }}
          onZoomEnd={(_1: any, _2: any, zoomableViewEventObject: any) => {
            onVideoMove(zoomableViewEventObject);
          }}
          style={styles.zoomView}>
          <View style={styles.videoParent} ref={vidRef}>
            <TrimmerPlayer
              hideTrimmer={hideTrimmer}
              source={media.uri}
              videoStyles={[
                styles.media,
                {
                  height: SCREEN_WIDTH / aspectRatio,
                },
              ]}
              handleLoad={(response: {width: number; height: number}) => {
                const {width, height} = response;
                setOrigDimensions([width, height]);
                setAspectRatio(width / height);
              }}
              onChangedEndpoints={(response: {start: number; end: number}) =>
                setTrimEnds(response)
              }
            />
          </View>
        </ReactNativeZoomableView>
      )}
      {media.isVideo && (
        <View style={styles.iconCarrier}>
          <TouchableOpacity
            style={styles.iconContainer}
            onPress={() => setHideTrimmer((state) => !state)}>
            <TrimIcon />
            <Text style={styles.iconText}>Trim</Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={styles.iconContainer}
            // TODO: finish me
            onPress={() => null}>
            <Image
              style={styles.volumnIcon}
              source={require('../../assets/images/volume-on.png')}
            />
            <Text style={styles.iconText}>Volume</Text>
          </TouchableOpacity>
        </View>
      )}
      {hideTrimmer && (
        <View style={styles.bottomContainer}>
          <SaveButton
            style={styles.saveButton}
            onPress={() =>
              processVideo((uri) =>
                saveImageToGallery(uri, media.isVideo ? 'video' : 'photo'),
              )
            }
          />
          <TaggSquareButton
            style={styles.button}
            onPress={() =>
              processVideo((uri) =>
                navigation.navigate('CaptionScreen', {
                  screenType,
                  media: {
                    uri: uri,
                    isVideo: media.isVideo,
                  },
                  selectedCategory,
                }),
              )
            }
            title={'Next'}
            buttonStyle={'large'}
            buttonColor={'blue'}
            labelColor={'white'}
            labelStyle={styles.buttonLabel}
          />
        </View>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'black',
    height: SCREEN_HEIGHT,
    width: SCREEN_WIDTH,
  },
  closeButton: {
    position: 'absolute',
    top: 0,
    paddingTop: HeaderHeight,
    zIndex: 1,
    marginLeft: '5%',
  },
  bottomContainer: {
    position: 'absolute',
    bottom: SCREEN_HEIGHT * 0.1,
    width: SCREEN_WIDTH * 0.8,
    justifyContent: 'space-between',
    alignItems: 'center',
    alignSelf: 'center',
    flexDirection: 'row',
  },
  saveButton: {
    width: 50,
  },
  button: {
    width: normalize(108),
    height: normalize(36),
  },
  buttonLabel: {
    fontWeight: '700',
    fontSize: normalize(15),
    lineHeight: normalize(17.8),
    letterSpacing: normalize(1.3),
    textAlign: 'center',
  },
  iconCarrier: {
    width: SCREEN_WIDTH * 0.15,
    height: SCREEN_HEIGHT * 0.2,
    borderRadius: SCREEN_WIDTH * 0.1,
    backgroundColor: 'rgba(0, 0, 0, 0.3)',
    position: 'absolute',
    right: SCREEN_WIDTH * 0.025,
    top: SCREEN_HEIGHT * 0.1,
    flexDirection: 'column',
    justifyContent: 'space-evenly',
    alignItems: 'center',
  },
  iconContainer: {
    height: 50,
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  iconText: {
    color: 'white',
    fontSize: normalize(11),
    fontWeight: 'bold',
  },
  media: {
    zIndex: 0,
    flex: 1,
  },
  videoParent: {
    flex: 1,
  },
  zoomView: {
    backgroundColor: 'black',
    flex: 1,
  },
  volumnIcon: {
    width: 25,
    height: 25,
  },
});

export default EditMedia;
