import {
  Box,
  Button,
  DialogActions,
  DialogContent,
  DialogTitle,
  TextField,
  Autocomplete
} from "@mui/material";
//import { Autocomplete } from "@material-ui/lab";
import ResponsiveDialog from "common/ResponsiveDialog";
import { useMobile } from "hooks";
import { clone } from "lodash";
import MapBase, { ViewData } from "maps/MapBase";
//import { GeocoderObject } from "maps/mapControllers/Geocoder";
import { Result } from "@mapbox/mapbox-gl-geocoder";
import GateDrawer from "maps/mapDrawers/GateDrawer";
import TerminalDrawer from "maps/mapDrawers/TerminalDrawer";
import { MarkerData } from "maps/mapMarkers/Markers";
import AviationMapTools from "maps/mapObjectTools/aviationMapTools";
import { Gate } from "models/Gate";
import { Terminal } from "models/Terminal";
import OmniAirDeviceIcon from "products/AviationIcons/OmniAirDeviceIcon";
import PlaneIcon from "products/AviationIcons/PlaneIcon";
import { pond } from "protobuf-ts/pond";
import { useGateAPI, useGlobalState, useSnackbar, useTerminalAPI } from "providers";
import React, { useCallback, useEffect, useState } from "react";

interface Props {
  startingView?: ViewData;
}

export default function AviationMapController(props: Props) {
  const [{ user, as }] = useGlobalState();
  const transDuration = 3000;
  const [currentView, setCurrentView] = useState(
    props.startingView
      ? props.startingView
      : {
          latitude: 52.1579,
          longitude: -106.6702,
          zoom: user.settings.mapZoom
        }
  );
  const terminalAPI = useTerminalAPI();
  const gateAPI = useGateAPI();
  const [terminals, setTerminals] = useState<Map<string, Terminal>>(new Map());
  const [gates, setGates] = useState<Map<string, Gate>>(new Map());
  const [terminalOptions, setTerminalOptions] = useState<Terminal[]>([]);
  const [gateOptions, setGateOptions] = useState<Gate[]>([]);
  const [terminalMarkers, setTerminalMarkers] = useState<Map<string, MarkerData>>(new Map());
  const [gateMarkers, setGateMarkers] = useState<Map<string, MarkerData>>(new Map());
  const [markers, setMarkers] = useState<MarkerData[]>([]);
  const { openSnack } = useSnackbar();
  const [gateDrawerOpen, setGateDrawerOpen] = useState(false);
  const [terminalDrawerOpen, setTerminalDrawerOpen] = useState(false);
  const [objectKey, setObjectKey] = useState("");
  const [endTime, setEndTime] = useState(0);
  const isMobile = useMobile();
  const [markerType, setMarkerType] = useState(pond.ObjectType.OBJECT_TYPE_UNKNOWN);
  const [markerDisplay, setMarkerDisplay] = useState(false);
  const [openMarkerDialog, setOpenMarkerDialog] = useState(false);
  const [selectKey, setSelectKey] = useState("");
  const [newLong, setNewLong] = useState(0);
  const [newLat, setNewLat] = useState(0);
  const zoomLevels = {
    far: 14,
    close: 16
  };
  const [customSearchEntries, setCustomSearchEntries] = useState<Result[]>([]);
  const [terminalSearchEntries, setTerminalSearchEntries] = useState<Result[]>([]);
  const [gateSearchEntries, setGateSearchEntries] = useState<Result[]>([]);

  const closeDrawers = () => {
    setTerminalDrawerOpen(false);
    setGateDrawerOpen(false);
  };

  const markerClick = (key: string, long: number, lat: number, isMobile: boolean) => {
    closeDrawers();
    clickDelay();
    setObjectKey(key);
    moveMap(lat, long, zoomLevels.close, isMobile);
  };

  //this click delay is implemented to fix an issue with touch screens that caused the drawer to close immediately after opening
  const clickDelay = () => {
    let endTime = new Date().valueOf() + 1000;
    setEndTime(endTime);
  };

  const updateTerminalMarkers = (
    terminal: Terminal,
    longitude: number,
    latitude: number,
    newMarker?: boolean
  ) => {
    let settings = terminal.settings;
    settings.longitude = longitude;
    settings.latitude = latitude;
    terminalAPI
      .updateTerminal(terminal.key, terminal.name, settings, as)
      .then(resp => {
        openSnack("Terminal Location Updated");
        if (newMarker) {
          //create new marker
          let termMarker = terminal.getMarkerData(
            (e, i, isMobile) => {
              markerClick(terminal.key, terminal.longitude(), terminal.latitude(), isMobile);
              setTerminalDrawerOpen(true);
            },
            location => {
              updateTerminalMarkers(terminal, location.longitude, location.latitude);
            }
          );
          termMarker.markerIcon = <PlaneIcon width={50 * 0.6} height={50 * 0.6} />;
          //clone data to update the state with and put the marker into the clone
          let cloneData = clone(terminalMarkers);
          cloneData.set(terminal.key, termMarker);
          setTerminalMarkers(cloneData);
          //remove from options
          if (terminalOptions.includes(terminal)) {
            terminalOptions.splice(terminalOptions.indexOf(terminal), 1);
          }
        }
      })
      .catch(err => {
        openSnack("Failed to update location");
      });
  };

  const updateGateMarkers = (
    gate: Gate,
    longitude: number,
    latitude: number,
    newMarker?: boolean
  ) => {
    let settings = gate.settings;
    settings.longitude = longitude;
    settings.latitude = latitude;
    gateAPI
      .updateGate(gate.key, gate.name, settings, as)
      .then(resp => {
        openSnack("Gate Location Updated");
        if (newMarker) {
          let marker = gate.getMarkerData(
            (e, i, isMobile) => {
              markerClick(gate.key, gate.longitude(), gate.latitude(), isMobile);
              setGateDrawerOpen(true);
            },
            location => {
              updateGateMarkers(gate, location.longitude, location.latitude);
            }
          );
          marker.markerIcon = <OmniAirDeviceIcon size={50 * 0.6} />;
          let cloneData = clone(gateMarkers);
          cloneData.set(gate.key, marker);
          setGateMarkers(cloneData);
          //remove from options
          if (gateOptions.includes(gate)) {
            gateOptions.splice(gateOptions.indexOf(gate), 1);
          }
        } else {
          //change the click function in the markers data to use the new long and lat
        }
      })
      .catch(err => {
        openSnack("Failed to update location");
      });
  };

  const loadTerminals = useCallback(() => {
    terminalAPI.listTerminals(0, 0, undefined, undefined, undefined, as).then(resp => {
      let terminalMap: Map<string, Terminal> = new Map();
      let terminalMarkers: Map<string, MarkerData> = new Map();
      let terminalOps: Terminal[] = [];
      let searchEntries: Result[] = [];
      resp.data.terminals.forEach(term => {
        let terminal = Terminal.create(term);
        terminalMap.set(terminal.key, terminal);
        if (
          terminal.longitude() &&
          terminal.longitude() !== 0 &&
          terminal.latitude() &&
          terminal.latitude() !== 0
        ) {
          //make marker
          let termMarker = terminal.getMarkerData(
            (e, i, isMobile) => {
              markerClick(terminal.key, terminal.longitude(), terminal.latitude(), isMobile);
              setTerminalDrawerOpen(true);
            },
            location => {
              updateTerminalMarkers(terminal, location.longitude, location.latitude);
            }
          );
          termMarker.markerIcon = <PlaneIcon width={50 * 0.6} height={50 * 0.6} />;
          terminalMarkers.set(terminal.key, termMarker);
          searchEntries.push({
            id: terminal.key,
            center: [terminal.longitude(), terminal.latitude()],
            place_name: terminal.name,
            place_type: ["terminal"]
          } as Result);
        } else {
          //no location was set so add to the options
          terminalOps.push(terminal);
        }
      });
      setTerminals(terminalMap);
      setTerminalMarkers(terminalMarkers);
      setTerminalSearchEntries(searchEntries);
      setTerminalOptions(terminalOps);
    });
  }, [terminalAPI, as]); // eslint-disable-line react-hooks/exhaustive-deps

  const loadGates = useCallback(() => {
    gateAPI.listGates(0, 0, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, as).then(resp => {
      let gates: Map<string, Gate> = new Map();
      let gateOptions: Gate[] = [];
      let gateMarkers: Map<string, MarkerData> = new Map();
      let searchEntries: Result[] = [];
      resp.data.gates.forEach(g => {
        let gate = Gate.create(g);
        gates.set(gate.key, gate);
        if (
          gate.longitude() &&
          gate.longitude() !== 0 &&
          gate.latitude() &&
          gate.latitude() !== 0
        ) {
          let marker = gate.getMarkerData(
            (e, i, isMobile) => {
              markerClick(gate.key, gate.longitude(), gate.latitude(), isMobile);
              setGateDrawerOpen(true);
            },
            location => {
              updateGateMarkers(gate, location.longitude, location.latitude);
            }
          );
          marker.markerIcon = <OmniAirDeviceIcon size={50 * 0.6} />;
          gateMarkers.set(gate.key, marker);
          searchEntries.push({
            id: gate.key,
            center: [gate.longitude(), gate.latitude()],
            place_name: gate.name,
            place_type: ["gate"]
          } as Result);
        } else {
          gateOptions.push(gate);
        }
      });
      setGates(gates);
      setGateOptions(gateOptions);
      setGateMarkers(gateMarkers);
      setGateSearchEntries(searchEntries);
    });
  }, [gateAPI, as]); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    loadTerminals();
    loadGates();
  }, [loadTerminals, loadGates]);

  useEffect(() => {
    let tArr = Array.from(terminalMarkers.values());
    let gArr = Array.from(gateMarkers.values());
    setMarkers(tArr.concat(gArr));
  }, [terminalMarkers, gateMarkers]);

  useEffect(() => {
    setCustomSearchEntries(terminalSearchEntries.concat(gateSearchEntries));
  }, [terminalSearchEntries, gateSearchEntries]);

  const moveMap = (
    lat: number,
    long: number,
    zoom: number,
    mobileOffset?: boolean,
    center?: boolean
  ) => {
    let xOff = !mobileOffset ? -250 : undefined;
    let yOff = mobileOffset ? -150 : undefined;

    setCurrentView({
      latitude: lat,
      longitude: long,
      zoom: zoom,
      transitionDuration: transDuration,
      xOffset: center ? 0 : xOff,
      yOffset: center ? 0 : yOff
    });
  };

  const closeNewMarkerDialog = () => {
    if (new Date().valueOf() > endTime) {
      setOpenMarkerDialog(false);
      setSelectKey("");
    }
  };

  const setMarker = () => {
    if (markerType === pond.ObjectType.OBJECT_TYPE_TERMINAL) {
      let terminal = terminals.get(selectKey);
      if (terminal) {
        updateTerminalMarkers(terminal, newLong, newLat, true);
      }
    }
    if (markerType === pond.ObjectType.OBJECT_TYPE_GATE) {
      let gate = gates.get(selectKey);
      if (gate) {
        updateGateMarkers(gate, newLong, newLat, true);
      }
    }
    setOpenMarkerDialog(false);
  };

  const markerDialog = () => {
    return (
      <ResponsiveDialog
        open={openMarkerDialog}
        onClose={() => {
          setOpenMarkerDialog(false);
        }}>
        <DialogTitle>Place Marker</DialogTitle>
        <DialogContent>
          <Box width={300}>
            {markerType === pond.ObjectType.OBJECT_TYPE_TERMINAL && (
              <Autocomplete
                id="autoTerminalList"
                options={terminalOptions}
                getOptionLabel={(option: Terminal) => option.name}
                fullWidth
                onChange={(e, val) => {
                  val && setSelectKey(val.key);
                }}
                renderInput={params => <TextField {...params} label="Terminal" />}
              />
            )}
            {markerType === pond.ObjectType.OBJECT_TYPE_GATE && (
              <Autocomplete
                id="autoGateList"
                options={gateOptions}
                getOptionLabel={(option: Gate) => option.name}
                fullWidth
                onChange={(e, val) => {
                  val && setSelectKey(val.key);
                }}
                renderInput={params => <TextField {...params} label="Gate" />}
              />
            )}
          </Box>
        </DialogContent>
        <DialogActions>
          <Box>
            <Button onClick={closeNewMarkerDialog}>Cancel</Button>
            <Button onClick={setMarker}>Plot New Marker</Button>
          </Box>
        </DialogActions>
      </ResponsiveDialog>
    );
  };

  return (
    <React.Fragment>
      {markerDialog()}
      <MapBase
        displayMarkerTitles={markerDisplay}
        customSearchEntries={customSearchEntries}
        geocoderResultFunction={result => {
          //open drawer
          setObjectKey(result.id?.toString() ?? "");
          if (result.place_type.includes("terminal")) {
            setTerminalDrawerOpen(true);
          } else if (result.place_type.includes("gate")) {
            setGateDrawerOpen(true);
          }
        }}
        geocoderTransitionFunction={result => {
          //override the geocoders transition with our own
          let long = result.center[0];
          let lat = result.center[1];
          if (long && lat) {
            let centered = true;
            let zoom = zoomLevels.far;
            if (result.place_type.includes("terminal")) {
              centered = false;
            } else if (result.place_type.includes("gate")) {
              zoom = zoomLevels.close;
              centered = false;
            }
            moveMap(lat, long, zoom, isMobile, centered);
          }
        }}
        markerData={markers}
        placingMarker={markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN}
        mapTools={
          <AviationMapTools
            toggleMarkerType={setMarkerType}
            toggleMarkerDisplay={setMarkerDisplay}
          />
        }
        mapClick={e => {
          setNewLong(e.lngLat.lng);
          setNewLat(e.lngLat.lat);
          if (markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
            setOpenMarkerDialog(true);
          }
        }}
        currentView={currentView}
      />
      <GateDrawer
        gates={gates}
        moveMap={(long, lat) => {
          moveMap(lat, long, zoomLevels.close, isMobile);
        }}
        open={gateDrawerOpen}
        onClose={() => {
          if (new Date().valueOf() > endTime) {
            setObjectKey("");
            setGateDrawerOpen(false);
          }
        }}
        selectedGate={objectKey}
        removeMarker={key => {
          if (new Date().valueOf() > endTime) {
            //use the key to remove it from the map of yard markers which should cause the markerData array to update
            let newMarkers = clone(gateMarkers);
            newMarkers.delete(key);
            setGateMarkers(newMarkers);
            //add it back to the options for terminals
            let gate = gates.get(key);
            if (gate) {
              gateOptions.push(gate);
            }
          }
        }}
      />
      <TerminalDrawer
        terminals={terminals}
        moveMap={(long, lat) => {
          moveMap(lat, long, zoomLevels.close, isMobile);
        }}
        open={terminalDrawerOpen}
        onClose={() => {
          if (new Date().valueOf() > endTime) {
            setObjectKey("");
            setTerminalDrawerOpen(false);
          }
        }}
        selectedKey={objectKey}
        removeMarker={key => {
          if (new Date().valueOf() > endTime) {
            //use the key to remove it from the map of yard markers which should cause the markerData array to update
            let newMarkers = clone(terminalMarkers);
            newMarkers.delete(key);
            setTerminalMarkers(newMarkers);
            //add it back to the options for terminals
            let terminal = terminals.get(key);
            if (terminal) {
              terminalOptions.push(terminal);
            }
          }
        }}
      />
    </React.Fragment>
  );
}
