import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import FullscreenIcon from "@mui/icons-material/Fullscreen";
import FullscreenExitIcon from "@mui/icons-material/FullscreenExit";
import KeyboardAltIcon from "@mui/icons-material/KeyboardAlt";
import { IconButton, Tooltip } from "@mui/material";
import React, { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { VncScreen, VncScreenHandle } from "react-vnc";
import { ServerApi } from "../api/ServerApi";
import { VMApi, VMInfo } from "../api/VMApi";
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { time } from "../utils/DateUtils";
import { AsyncWidget } from "../widgets/AsyncWidget";

interface VNCTokenInfo {
  url: string;
  expire: number;
}

export function VNCRoute(): React.ReactElement {
  const { uuid } = useParams();

  const [vm, setVM] = React.useState<VMInfo | undefined>();

  const load = async () => {
    setVM(await VMApi.GetSingle(uuid!));
  };

  return (
    <AsyncWidget
      loadKey={uuid}
      load={load}
      errMsg="Failed to load VM information!"
      build={() => <VNCInner vm={vm!} />}
    />
  );
}

function VNCInner(p: { vm: VMInfo }): React.ReactElement {
  const snackbar = useSnackbar();
  const navigate = useNavigate();

  const [token, setToken] = React.useState<VNCTokenInfo | undefined>();
  const [counter, setCounter] = React.useState(1);
  const [connected, setConnected] = React.useState(false);

  const vncRef = React.useRef<HTMLDivElement>(null);
  const vncScreenRef = React.useRef<VncScreenHandle>(null);

  const connect = async (force: boolean) => {
    try {
      if (force) setCounter(counter + 1);

      // Check if getting new time is useless
      if ((token?.expire ?? 0) > time()) return;

      setToken(undefined);

      const u = await VMApi.OneShotVNCURL(p.vm);
      console.info("VNC URL", u);

      if (!token)
        setToken({
          expire: time() + ServerApi.Config.constraints.vnc_token_duration,
          url: u,
        });
    } catch (e) {
      console.error(e);
      snackbar("Failed to initialize VNC connection!");
    }
  };

  const disconnected = () => {
    setConnected(false);
    connect(true);
  };

  const goBack = () => {
    navigate(p.vm.ViewURL);
  };

  const goFullScreen = () => {
    if (vncRef.current) vncRef.current.requestFullscreen();
  };

  const exitFullScreen = () => {
    document.exitFullscreen();
  };

  useEffect(() => {
    connect(false);

    if (vncRef.current) {
      vncRef.current.onfullscreenchange = () => {
        setCounter(counter + 1);
      };
    }
  });

  if (token === undefined)
    return <p>Please wait, connecting to the machine...</p>;

  return (
    <div ref={vncRef} style={{ display: "flex" }}>
      {/* Controls */}
      <div style={{ display: "flex", flexDirection: "column" }}>
        <IconButton onClick={goBack}>
          <ArrowBackIcon />
        </IconButton>

        {/* Toggle fullscreen */}
        {!document.fullscreenElement ? (
          <IconButton onClick={goFullScreen}>
            <FullscreenIcon />
          </IconButton>
        ) : (
          <IconButton onClick={exitFullScreen}>
            <FullscreenExitIcon />
          </IconButton>
        )}

        {/* Keystrokes */}
        {connected && (
          <Tooltip title="Send Ctrl+Alt+Del">
            <IconButton onClick={() => vncScreenRef.current?.sendCtrlAltDel()}>
              <KeyboardAltIcon />
            </IconButton>
          </Tooltip>
        )}
      </div>

      {/* Screen */}
      <div
        style={{
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          height: "100%",
        }}
      >
        <VncScreen
          ref={vncScreenRef}
          url={token.url}
          onDisconnect={() => {
            console.info("VNC disconnected " + token.url);
            disconnected();
          }}
          onConnect={() => {
            setConnected(true);
          }}
        />
      </div>
    </div>
  );
}
