import { SessionIconButton } from '../icon';
import { animation, contextMenu, Item, Menu } from 'react-contexify';
import { InputItem } from '../../session/utils/calling/CallManager';
import { setFullScreenCall } from '../../state/ducks/call';
import { CallManager, ToastUtils } from '../../session/utils';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getHasOngoingCallWithPubkey } from '../../state/selectors/call';
import { DropDownAndToggleButton } from '../icon/DropDownAndToggleButton';
import styled from 'styled-components';

const videoTriggerId = 'video-menu-trigger-id';
const audioTriggerId = 'audio-menu-trigger-id';
const audioOutputTriggerId = 'audio-output-menu-trigger-id';

export const VideoInputButton = ({
  currentConnectedCameras,
  localStreamVideoIsMuted,
  hideArrowIcon = false,
}: {
  currentConnectedCameras: Array<InputItem>;
  localStreamVideoIsMuted: boolean;
  hideArrowIcon?: boolean;
}) => {
  return (
    <>
      <DropDownAndToggleButton
        iconType="camera"
        isMuted={localStreamVideoIsMuted}
        onMainButtonClick={() => {
          void handleCameraToggle(currentConnectedCameras, localStreamVideoIsMuted);
        }}
        onArrowClick={e => {
          showVideoInputMenu(currentConnectedCameras, e);
        }}
        hidePopoverArrow={hideArrowIcon}
      />

      <VideoInputMenu triggerId={videoTriggerId} camerasList={currentConnectedCameras} />
    </>
  );
};

export const AudioInputButton = ({
  currentConnectedAudioInputs,
  isAudioMuted,
  hideArrowIcon = false,
}: {
  currentConnectedAudioInputs: Array<InputItem>;
  isAudioMuted: boolean;
  hideArrowIcon?: boolean;
}) => {
  return (
    <>
      <DropDownAndToggleButton
        iconType="microphone"
        isMuted={isAudioMuted}
        onMainButtonClick={() => {
          void handleMicrophoneToggle(currentConnectedAudioInputs, isAudioMuted);
        }}
        onArrowClick={e => {
          showAudioInputMenu(currentConnectedAudioInputs, e);
        }}
        hidePopoverArrow={hideArrowIcon}
      />

      <AudioInputMenu triggerId={audioTriggerId} audioInputsList={currentConnectedAudioInputs} />
    </>
  );
};

export const AudioOutputButton = ({
  currentConnectedAudioOutputs,
  isAudioOutputMuted,
  hideArrowIcon = false,
}: {
  currentConnectedAudioOutputs: Array<InputItem>;
  isAudioOutputMuted: boolean;
  hideArrowIcon?: boolean;
}) => {
  return (
    <>
      <DropDownAndToggleButton
        iconType="volume"
        isMuted={isAudioOutputMuted}
        onMainButtonClick={() => {
          void handleSpeakerToggle(currentConnectedAudioOutputs, isAudioOutputMuted);
        }}
        onArrowClick={e => {
          showAudioOutputMenu(currentConnectedAudioOutputs, e);
        }}
        hidePopoverArrow={hideArrowIcon}
      />

      <AudioOutputMenu
        triggerId={audioOutputTriggerId}
        audioOutputsList={currentConnectedAudioOutputs}
      />
    </>
  );
};

const VideoInputMenu = ({
  triggerId,
  camerasList,
}: {
  triggerId: string;
  camerasList: Array<InputItem>;
}) => {
  return (
    <Menu id={triggerId} animation={animation.fade}>
      {camerasList.map(m => {
        return (
          <Item
            key={m.deviceId}
            onClick={() => {
              void CallManager.selectCameraByDeviceId(m.deviceId);
            }}
          >
            {m.label.substr(0, 40)}
          </Item>
        );
      })}
    </Menu>
  );
};

const AudioInputMenu = ({
  triggerId,
  audioInputsList,
}: {
  triggerId: string;
  audioInputsList: Array<InputItem>;
}) => {
  return (
    <Menu id={triggerId} animation={animation.fade}>
      {audioInputsList.map(m => {
        return (
          <Item
            key={m.deviceId}
            onClick={() => {
              void CallManager.selectAudioInputByDeviceId(m.deviceId);
            }}
          >
            {m.label.substr(0, 40)}
          </Item>
        );
      })}
    </Menu>
  );
};

const AudioOutputMenu = ({
  triggerId,
  audioOutputsList,
}: {
  triggerId: string;
  audioOutputsList: Array<InputItem>;
}) => {
  return (
    <Menu id={triggerId} animation={animation.fade}>
      {audioOutputsList.map(m => {
        return (
          <Item
            key={m.deviceId}
            onClick={() => {
              void CallManager.selectAudioOutputByDeviceId(m.deviceId);
            }}
          >
            {m.label.substr(0, 40)}
          </Item>
        );
      })}
    </Menu>
  );
};

const ShowInFullScreenButton = ({ isFullScreen }: { isFullScreen: boolean }) => {
  const dispatch = useDispatch();

  const showInFullScreen = () => {
    if (isFullScreen) {
      dispatch(setFullScreenCall(false));
    } else {
      dispatch(setFullScreenCall(true));
    }
  };

  return (
    <SessionIconButton
      iconSize={60}
      iconPadding="20px"
      iconType="fullscreen"
      backgroundColor="white"
      borderRadius="50%"
      onClick={showInFullScreen}
      iconColor="black"
      margin="10px"
    />
  );
};

export const HangUpButton = () => {
  const ongoingCallPubkey = useSelector(getHasOngoingCallWithPubkey);

  const handleEndCall = async () => {
    // call method to end call connection
    if (ongoingCallPubkey) {
      await CallManager.USER_hangup(ongoingCallPubkey);
    }
  };

  return (
    <SessionIconButton
      iconSize={60}
      iconPadding="20px"
      iconType="hangup"
      backgroundColor="white"
      borderRadius="50%"
      onClick={handleEndCall}
      iconColor="red"
      margin="10px"
    />
  );
};

const showAudioInputMenu = (
  currentConnectedAudioInputs: Array<any>,
  e: React.MouseEvent<HTMLDivElement>
) => {
  if (currentConnectedAudioInputs.length === 0) {
    ToastUtils.pushNoAudioInputFound();
    return;
  }
  contextMenu.show({
    id: audioTriggerId,
    event: e,
  });
};

const showAudioOutputMenu = (
  currentConnectedAudioOutputs: Array<any>,
  e: React.MouseEvent<HTMLDivElement>
) => {
  if (currentConnectedAudioOutputs.length === 0) {
    ToastUtils.pushNoAudioOutputFound();
    return;
  }
  contextMenu.show({
    id: audioOutputTriggerId,
    event: e,
  });
};

const showVideoInputMenu = (
  currentConnectedCameras: Array<InputItem>,
  e: React.MouseEvent<HTMLDivElement>
) => {
  if (currentConnectedCameras.length === 0) {
    ToastUtils.pushNoCameraFound();
    return;
  }
  contextMenu.show({
    id: videoTriggerId,
    event: e,
  });
};

const handleCameraToggle = async (
  currentConnectedCameras: Array<InputItem>,
  localStreamVideoIsMuted: boolean
) => {
  if (!currentConnectedCameras.length) {
    ToastUtils.pushNoCameraFound();

    return;
  }
  if (localStreamVideoIsMuted) {
    // select the first one
    await CallManager.selectCameraByDeviceId(currentConnectedCameras[0].deviceId);
  } else {
    await CallManager.selectCameraByDeviceId(CallManager.DEVICE_DISABLED_DEVICE_ID);
  }
};

const handleMicrophoneToggle = async (
  currentConnectedAudioInputs: Array<InputItem>,
  isAudioMuted: boolean
) => {
  if (!currentConnectedAudioInputs.length) {
    ToastUtils.pushNoAudioInputFound();

    return;
  }
  if (isAudioMuted) {
    // selects the first one
    await CallManager.selectAudioInputByDeviceId(currentConnectedAudioInputs[0].deviceId);
  } else {
    await CallManager.selectAudioInputByDeviceId(CallManager.DEVICE_DISABLED_DEVICE_ID);
  }
};

const handleSpeakerToggle = async (
  currentConnectedAudioOutputs: Array<InputItem>,
  isAudioOutputMuted: boolean
) => {
  if (!currentConnectedAudioOutputs.length) {
    ToastUtils.pushNoAudioInputFound();

    return;
  }
  if (isAudioOutputMuted) {
    // selects the first one
    await CallManager.selectAudioOutputByDeviceId(currentConnectedAudioOutputs[0].deviceId);
  } else {
    await CallManager.selectAudioOutputByDeviceId(CallManager.DEVICE_DISABLED_DEVICE_ID);
  }
};

const StyledCallWindowControls = styled.div<{ makeVisible: boolean }>`
  position: absolute;

  bottom: 0px;
  width: 100%;
  height: 100%;
  align-items: flex-end;
  padding: 10px;
  border-radius: 10px;
  margin-left: auto;
  margin-right: auto;
  left: 0;
  right: 0;
  transition: all 0.25s ease-in-out;

  display: flex;

  justify-content: center;
  opacity: ${props => (props.makeVisible ? 1 : 0)};
`;

export const CallWindowControls = ({
  currentConnectedCameras,
  currentConnectedAudioInputs,
  currentConnectedAudioOutputs,
  isAudioMuted,
  isAudioOutputMuted,
  remoteStreamVideoIsMuted,
  localStreamVideoIsMuted,
  isFullScreen,
}: {
  isAudioMuted: boolean;
  isAudioOutputMuted: boolean;
  localStreamVideoIsMuted: boolean;
  remoteStreamVideoIsMuted: boolean;
  currentConnectedAudioInputs: Array<InputItem>;
  currentConnectedAudioOutputs: Array<InputItem>;
  currentConnectedCameras: Array<InputItem>;
  isFullScreen: boolean;
}) => {
  const [makeVisible, setMakeVisible] = useState(true);

  const setMakeVisibleTrue = () => {
    setMakeVisible(true);
  };
  const setMakeVisibleFalse = () => {
    setMakeVisible(false);
  };

  useEffect(() => {
    setMakeVisibleTrue();
    document.addEventListener('mouseenter', setMakeVisibleTrue);
    document.addEventListener('mouseleave', setMakeVisibleFalse);

    return () => {
      document.removeEventListener('mouseenter', setMakeVisibleTrue);
      document.removeEventListener('mouseleave', setMakeVisibleFalse);
    };
  }, [isFullScreen]);
  return (
    <StyledCallWindowControls makeVisible={makeVisible}>
      {!remoteStreamVideoIsMuted && <ShowInFullScreenButton isFullScreen={isFullScreen} />}

      <VideoInputButton
        currentConnectedCameras={currentConnectedCameras}
        localStreamVideoIsMuted={localStreamVideoIsMuted}
        hideArrowIcon={isFullScreen}
      />
      <AudioInputButton
        currentConnectedAudioInputs={currentConnectedAudioInputs}
        isAudioMuted={isAudioMuted}
        hideArrowIcon={isFullScreen}
      />
      <AudioOutputButton
        currentConnectedAudioOutputs={currentConnectedAudioOutputs}
        isAudioOutputMuted={isAudioOutputMuted}
        hideArrowIcon={isFullScreen}
      />
      <HangUpButton />
    </StyledCallWindowControls>
  );
};