import React, { useEffect, useState, useMemo, useRef } from "react";
import {
  StyleSheet,
  View,
  Text,
  Vibration,
  Animated,
  Easing,
} from "react-native";

import { forwardRef } from "react";
import Colors from "react-native-digitcamera/src/common/Colors";
import MIcon from "react-native-vector-icons/MaterialIcons";
import {
  WAITING_PERIOD,
  expectedPoseData,
  expectedPoseDataGDD,
  NO_OF_POSES,
  zIndex_ml_output,
  AUDIO_INSTRUCTIONS_ARR,
} from "./StencilAnimation";
import {
  play,
  digitSoundList,
} from "react-native-digitcamera/src/common/DSound";
import {
  UELogEvent,
  UELogMessage,
} from "../common/libEventLog/UserExperior/LibUserExperior";
import { Dynatrace } from "@dynatrace/react-native-plugin";

// var result = "Pose Result";
// var expectedPose = null;
export var FIREBASE_CONFIG_CONFIDENCE = { value: 0.8 };
var expectedPoseIndex = 0;
var waitId = null;
// var pauseResume = "pause";
const guide = {
  left: "Fit the car",
  right: "Fit the car",
  bottom: "Fit the car",
  top: "Fit the car",
  perfect: "Perfect Catch",
};
export const expectedPoseDataResult = {
  CHP: "Embossed Chassis",
  ECR: "Engine Chassis",
  WS: "Windshield",
  FS: "Front Side",
  FLC: "Front Left Corner",
  LS: "Left Side",
  BLC: "Back Left Corner",
  BS: "Back Side",
  BRC: "Back Right Corner",
  RS: "Right Side",
  FRC: "Front Right Corner",
  DBD: "Dashboard",
};
var voiceVar = true;
const ProcessRawOutput = forwardRef((props, multiRefMl) => {
  const {
    moveNextRef,
    rawOutputRef,
    pauseResumeRef,
    letsContinueAnimRef,
    nextStepRef,
    prevStepRef,
  } = multiRefMl.current;
  const { voice, isGdd } = props;
  voiceVar = voice;
  const [animation] = useState(new Animated.Value(0));
  const [expectedPose, setExpectedPose] = useState(
    isGdd ? expectedPoseDataGDD[0] : expectedPoseData[0]
  );

  const startAnimation = () => {
    animation.setValue(0);
    Animated.timing(animation, {
      toValue: 1,
      duration: 1500,
      easing: Easing.linear,
      useNativeDriver: true,
    }).start(() => {
      animation.setValue(0);
    });
  };
  const animatedStyles = {
    transform: [
      {
        translateX: animation.interpolate({
          inputRange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
          outputRange: [0, 5, -5, 5, -5, 0, 0, 0, 0, 0, 0],
        }),
      },
      {
        translateY: animation.interpolate({
          inputRange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
          outputRange: [0, 0, 0, 0, 0, 0, 5, -5, 5, -5, 0],
        }),
      },
    ],
  };

  const poseWaitingTimer = (pauseResumeRef, constExpectedPose) => {
    // const constExpectedPose = expectedPose;
    const wId = setTimeout(() => {
      console.log(
        "*** ProcessRawOutput poseWaitingTimer WAITING_PERIOD, expectedPose",
        WAITING_PERIOD,
        constExpectedPose
      );
      // clearTimeout(wId);
      if (
        pauseResumeRef &&
        pauseResumeRef.current() == "resume" &&
        expectedPoseIndex <= AUDIO_INSTRUCTIONS_ARR.length
      ) {
        moveToCheckNextPose(true, constExpectedPose, pauseResumeRef);
      } else {
        // in case of "pause", we need to wait until next "resume" (user click) happens
        const intr = setInterval(() => {
          if (
            pauseResumeRef &&
            pauseResumeRef.current() == "resume" &&
            expectedPoseIndex <= AUDIO_INSTRUCTIONS_ARR.length
          ) {
            clearInterval(intr);
            moveToCheckNextPose(true, constExpectedPose, pauseResumeRef);
          }
        }, 1000);
      }
    }, WAITING_PERIOD);
    // setWaitId(waitId)
    waitId = wId;
  };

  const letsContinue = () => {
    expectedPoseIndex = 0;
    setExpectedPose(isGdd ? expectedPoseDataGDD[0] : expectedPoseData[0]);
    poseWaitingTimer(
      pauseResumeRef,
      isGdd ? expectedPoseDataGDD[0] : expectedPoseData[0]
    );
    if (AUDIO_INSTRUCTIONS_ARR && AUDIO_INSTRUCTIONS_ARR.length > 0) {
      const { track, duration } = AUDIO_INSTRUCTIONS_ARR[0];
      playTrack(track);
    } else {
      console.log(
        "****************************************** empty AUDIO_INSTRUCTIONS_ARR ***********************************************"
      );
    }
  };
  const [result, setResult] = useState("Let's Go!");
  const [myPosition, setMyPosition] = useState("");

  const expectedPoseRender = useMemo(
    () => (
      <Text style={styles.poseText}>
        {expectedPoseDataResult[expectedPose]}
      </Text>
    ),
    [expectedPose]
  );
  const resultRender = useMemo(() => {
    const waitTillNextResult = setTimeout(() => {
      clearTimeout(waitTillNextResult);
      Vibration.vibrate(100);
      startAnimation();
    }, 5000);
    return <Text style={styles.poseText}>{result}</Text>;
  }, [result]);
  const calculation = useMemo(() => {
    const waitTillNextPosition = setTimeout(() => {
      clearTimeout(waitTillNextPosition);
      // play(digitSoundList[myPosition]);// Need to replace Nitin's voice
    }, 3000);
  }, [myPosition]);

  const logOutoutEvents = (expectedPose, requiredPoseReceivedConfidence) => {
    UELogEvent("ml_output", {
      expectedPose,
      receivedConfidence: requiredPoseReceivedConfidence,
    });
    Dynatrace.enterAction(
      "ml_output " +
        "expectedPose: " +
        expectedPose +
        ", confidence" +
        requiredPoseReceivedConfidence,
      Platform.OS
    );
  };

  const processOutput = (output, pauseResumeRef) => {
    if (pauseResumeRef.current) {
      // console.log(
      //   "*** ProcessRawOutput puaseResume: " +
      //     pauseResumeRef.current() + " for Pose "+expectedPose+
      //     "  output: " +
      //     JSON.stringify(output)
      // );
      try {
        if (output && pauseResumeRef.current() == "resume" && expectedPose) {
          // there must be something in expectedPose
          const positions = output["positions"];
          const positionConfidence = output["positionConfidence"];
          const poses = output["poses"];
          const poseConfidence = output["poseConfidence"];

          const requiredPoseIndex = poses.indexOf(expectedPose);
          const requiredPoseReceivedConfidence =
            poseConfidence[requiredPoseIndex];
          // const requiredPoseReceivedConfidence =0.9;

          const maxPosiConfi = Math.max.apply(null, positionConfidence);
          const indexMaxPosiConfi = positionConfidence.indexOf(maxPosiConfi);
          const myPosition = positions[indexMaxPosiConfi];
          console.log(
            "*** ProcessRawOutput poseIndex of MLOutput: " +
              requiredPoseIndex +
              " requiredPoseReceivedConfidence: " +
              requiredPoseReceivedConfidence +
              " expectedPose: ",
            expectedPose
          );
          // const maxPoseConfi = Math.max.apply(null, poseConfidence);
          // const indexMaxPoseConfi = poseConfidence.indexOf(maxPoseConfi);
          // var myPose = poses[indexMaxPoseConfi];
          var myPose = "";
          if (
            FIREBASE_CONFIG_CONFIDENCE == null ||
            FIREBASE_CONFIG_CONFIDENCE == undefined
          ) {
            FIREBASE_CONFIG_CONFIDENCE.value = 0.8;
          }
          if (
            requiredPoseReceivedConfidence > FIREBASE_CONFIG_CONFIDENCE.value
          ) {
            myPose = "Perfect Catch";
            if (waitId) clearTimeout(waitId);
            logOutoutEvents(expectedPose, requiredPoseReceivedConfidence);
            moveToCheckNextPose(false, expectedPose, pauseResumeRef);
            setResult(`${myPose}`);
            setMyPosition(myPosition);
            var res = "";
            if (myPosition == "CHP") {
              res = "Fit the Chassis";
            } else if (myPosition == "ECR") {
              res = "Fit the Engine";
            } else if (myPosition == "DBD") {
              res = "Fit the Dashboard";
            } else {
              res = guide[myPosition];
            }
            setResult(res);
            // } else if (maxPosiConfi > 0.8) {
          } else if (maxPosiConfi < FIREBASE_CONFIG_CONFIDENCE.value) {
            // car is not fitting into frame
            // myPose = myPose + " but expected: " + expectedPose;
            // setResult(`${guide[myPosition]+" --> "+myPose}`);
            setMyPosition(myPosition);
            var res = "";
            if (myPosition == "CHP") {
              res = "Fit the Chassis";
            } else if (myPosition == "ECR") {
              res = "Fit the Engine";
            } else if (myPosition == "DBD") {
              res = "Fit the Dashboard";
            } else {
              res = guide[myPosition];
            }
            setResult(res);
          }
          // console.log("*** ProcessRawOutput "+guide[myPosition]+"-"+myPose)
          // setResult(`${guide[myPosition]+"-"+myPose}`)
        }
      } catch (error) {
        console.log(error);
      }
    }
  };

  // if (rawOutputRef) {
  //   rawOutputRef.current = processOutput; // will be called from Camera.js -> registerTFFrameEvent
  // }
  const playTrack = (track) => {
    console.log("*** ProcessRawOutput playTrack voice, track ", voice, track);
    if (voice) {
      track.play((success) => {
        if (success) {
          track.release();
        } else {
          console.log(
            "*** ProcessRawOutput playTrack not success voice, track ",
            voice,
            track
          );
        }
      });
    }
  };

  const moveToCheckNextPose = (isTimeout, processedPose, pauseResumeRef) => {
    try {
      if (pauseResumeRef && pauseResumeRef.current() == "resume") {
        var newExpectedPoseIndex = expectedPoseIndex + 1;
        var newExpectedPose = isGdd
          ? expectedPoseDataGDD[newExpectedPoseIndex]
          : expectedPoseData[newExpectedPoseIndex];
        if (newExpectedPose == undefined) {
          newExpectedPoseIndex = NO_OF_POSES;
          newExpectedPose = isGdd
            ? expectedPoseDataGDD[newExpectedPoseIndex]
            : expectedPoseData[newExpectedPoseIndex];
        }
        setExpectedPose(newExpectedPose);
        expectedPoseIndex = newExpectedPoseIndex;

        // This will move to next step, audio, stencil stride
        if (!isGdd) {
          // There is no logic behing this, to make adjustment of delays we have done this
          console.log(
            "*** ProcessRawOutput newExpectedPoseIndex, AUDIO_INSTRUCTIONS_ARR: ",
            newExpectedPoseIndex,
            AUDIO_INSTRUCTIONS_ARR
          );
          if (newExpectedPoseIndex == 3) {
            if (AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex]) {
              const { track, duration } =
                AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex];
              playTrack(track);
            }
          } else {
            if (newExpectedPoseIndex >= 3) {
              if (moveNextRef) {
                moveNextRef.current(newExpectedPoseIndex - 1); // i.e. moveToCheckNextPose call will execute in AnimationEaseLinear
              }
              setExpectedPose(
                isGdd
                  ? expectedPoseDataGDD[newExpectedPoseIndex - 1]
                  : expectedPoseData[newExpectedPoseIndex - 1]
              ); // This is creating problem for last pose
            } else {
              if (moveNextRef) {
                moveNextRef.current(newExpectedPoseIndex); // i.e. moveToCheckNextPose call will execute in AnimationEaseLinear
              }
            }

            if (AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex]) {
              const { track, duration } =
                AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex];
              playTrack(track);
            }
            if (nextStepRef.current) {
              nextStepRef.current();
            }

            // Windshield instruction coming twice
            if (newExpectedPoseIndex == 12) {
              setTimeout(() => {
                if (nextStepRef.current) {
                  nextStepRef.current();
                }
              }, 5000);
            }
          }
        } else {
          if (AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex]) {
            const { track, duration } =
              AUDIO_INSTRUCTIONS_ARR[newExpectedPoseIndex];
            playTrack(track);
          }
          if (nextStepRef.current) {
            nextStepRef.current();
          }

          // Windshield instruction coming twice
          if (newExpectedPoseIndex == 5) {
            setTimeout(() => {
              if (nextStepRef.current) {
                nextStepRef.current();
              }
            }, 5000);
          }
          if (moveNextRef) {
            moveNextRef.current(newExpectedPoseIndex); // i.e. moveToCheckNextPose call will execute in AnimationEaseLinear
          }
        }
        console.log(
          "*** ProcessRawOutput moveToCheckNextPose isTimeout: " +
            isTimeout +
            " on pose: " +
            newExpectedPose
        );
        if (newExpectedPoseIndex != 0) {
          // In reset pose, we won't go for again to move next pose
          poseWaitingTimer(pauseResumeRef, expectedPose);
        }
      }
    } catch (error) {
      console.log("Error in ProcessRawOutput.js moveToCheckNextPose :", error);
    }
  };

  useEffect(() => {
    if (letsContinueAnimRef) {
      letsContinueAnimRef.current = letsContinue; // it will be called from Camera.js ->
    }
    if (rawOutputRef) {
      rawOutputRef.current = processOutput; // will be called from Camera.js -> registerTFFrameEvent
    }
    return () => {
      // unregister / deallocate
      letsContinueAnimRef.current = undefined;
      rawOutputRef.current = undefined;
    };
  });

  return (
    <Animated.View style={[styles.guideStyle, animatedStyles]}>
      {/* {expectedPoseRender}
       */}
      <MIcon
        name={"phone-android"}
        size={30}
        color={Colors.digitYellow}
        style={{
          backgroundColor: "transparent",
        }}
      />
      {resultRender}
    </Animated.View>
  );
});

const styles = StyleSheet.create({
  guideStyle: {
    width: "7%",
    height: "30%",
    position: "absolute",
    justifyContent: "center",
    alignItems: "center",
    top: "65%",
    right: "0.5%",
    backgroundColor: Colors.white,
    borderRadius: 10,
    zIndex: zIndex_ml_output,
  },
  poseText: {
    fontFamily: "Muli-Regular",
    alignSelf: "center",
    fontSize: 12,
    paddingHorizontal: 1,
    color: Colors.digitYellow,
  },
});

export default ProcessRawOutput;
