import {
    Box,
    Button,
    DialogActions,
    DialogContent,
    Grid2 as Grid,
    TextField,
    Theme,
    Typography,
    DialogTitle,
    IconButton,
    Menu,
    FormControlLabel,
    Checkbox,
    Autocomplete
  } from "@mui/material";
  import React, { useCallback, useEffect, useRef, useState } from "react";
  import MapBase, { MeasurementData, ViewData } from "../MapBase";
  import ResponsiveDialog from "common/ResponsiveDialog";
  import { useMobile, useSnackbar } from "hooks";
  import {
    useGlobalState,
    useFieldAPI,
    useBinYardAPI,
    useBinAPI,
    useFieldMarkerAPI,
    useDeviceAPI,
    useGrainBagAPI,
    useJohnDeereProxyAPI,
    useCNHiProxyAPI
  } from "providers";
  import { pond } from "protobuf-ts/pond";
  import { Bin as IBin, Field, FieldMarker, Device as DeviceModel } from "models";
  import { Edit } from "@mui/icons-material";
  //import { Autocomplete } from "@material-ui/lab";
  import BinSettings from "bin/BinSettings";
  import FieldMarkerSettings from "../mapMarkers/fieldMarkers/FieldMarkerSettings";
  import { GrainBag as BagModel } from "models/GrainBag";
  import { MarkerData } from "../mapMarkers/Markers";
  import BinYardDrawer from "../mapDrawers/BinYardDrawer";
  import { clone } from "lodash";
  import DeviceDrawer from "../mapDrawers/DeviceDrawer";
  import WeedsIcon from "products/AgIcons/WeedsIcon";
  import PestsIcon from "products/AgIcons/PestsIcon";
  import RocksIcon from "products/AgIcons/RocksIcon";
  import DiseaseIcon from "products/AgIcons/DiseaseIcon";
  import BinsIcon from "products/Bindapt/BinsIcon";
  import BindaptIcon from "products/Bindapt/BindaptIcon";
  import binTop from "assets/products/Ag/binTop.png";
  import BinDrawer from "../mapDrawers/BinDrawer";
  import GrainBagDrawer from "../mapDrawers/GrainBagDrawer";
  import AgMapTools from "../mapObjectTools/agMapTools";
  import FieldDrawer from "../mapDrawers/FieldDrawer";
  import { coordDistance, shapeFromCoords } from "models/GeometryMapping";
  import FieldSettings from "field/FieldSettings";
  import GrainBagSettings from "grainBag/grainBagSettings";
import { makeStyles } from "@mui/styles";
import { Result } from "@mapbox/mapbox-gl-geocoder";
  // import { useJohnDeereProxyAPI } from "providers/pond/johnDeereProxyAPI";
  // import { useCNHiProxyAPI } from "providers/pond/cnhiProxyAPI";
  
  const useStyles = makeStyles((theme: Theme) => ({
      map: {
        height: "100%"
      },
      cycleButton: {
        height: 25,
        width: 25,
        marginLeft: 10,
        marginRight: 10,
        //position: "absolute",
        backgroundColor: theme.palette.background.default,
        opacity: 0.8
      }
  }));
  
  interface Props {
    startingView?: ViewData;
  }
  
  export default function AgMapController(props: Props) {
    const transDuration = 3000;
    const [{ user, as }] = useGlobalState();
    const [currentView, setCurrentView] = useState<ViewData>(
      props.startingView
        ? props.startingView
        : {
            latitude: 52.1579,
            longitude: -106.6702,
            zoom: user.settings.mapZoom
          }
    );
  
    const classes = useStyles();
    const [fieldDrawer, setFieldDrawer] = useState(false);
    const [update, setUpdate] = useState(false);
    const isMobile = useMobile();
    const userId = user.id();
    const fieldAPI = useFieldAPI();
    const johnDeereAPI = useJohnDeereProxyAPI();
    const cnhiAPI = useCNHiProxyAPI();
    const { openSnack } = useSnackbar();
    const grainBagAPI = useGrainBagAPI();
    const binYardAPI = useBinYardAPI();
    const [yardOptions, setYardOptions] = useState<pond.BinYardSettings[]>([]);
    const [markerDialogOpen, setMarkerDialogOpen] = useState(false);
    const [newMarkerLong, setNewMarkerLong] = useState(0);
    const [newMarkerLat, setNewMarkerLat] = useState(0);
    const [markerType, setMarkerType] = useState<pond.ObjectType>(0);
    const [selectKey, setSelectKey] = useState("");
    const binAPI = useBinAPI();
    const [binOptions, setBinOptions] = useState<IBin[]>([]);
    const [endTime, setEndTime] = useState(0);
    const fieldMarkerAPI = useFieldMarkerAPI();
    const [openFMSettings, setOpenFMSettings] = useState(false);
    const deviceAPI = useDeviceAPI();
    const [deviceOptions, setDeviceOptions] = useState<DeviceModel[]>([]);
    const [binSettingsOpen, setBinSettingsOpen] = useState(false);
    const [loading, setLoading] = useState(false);
    const [whoAmI, setWhoAmI] = useState(as ?? userId);
    const [fields, setFields] = useState<Map<string, Field>>(new Map());
    const [jdFields, setJDFields] = useState<Map<string, Field>>(new Map());
    const [cnhiFields, setCNHIFields] = useState<Map<string, Field>>(new Map());
    const fieldsRef = useRef<Map<string, Field>>(new Map());
    const [devices, setDevices] = useState<Map<string, DeviceModel>>(new Map());
    const [bins, setBins] = useState<Map<string, IBin>>(new Map());
    const [grainBags, setGrainBags] = useState<Map<string, BagModel>>(new Map());
    const [bagOptions, setBagOptions] = useState<BagModel[]>([]);
    const [openBagSelect, setOpenBagSelect] = useState(false);
    const [openBagSettings, setOpenBagSettings] = useState(false);
    const bagCoordinates = useRef<{ start: pond.Coordinates; end: pond.Coordinates } | undefined>();
    const grainBagsRef = useRef<Map<string, BagModel>>(new Map());
    const [binYards, setBinYards] = useState<Map<string, pond.BinYardSettings>>(
      new Map<string, pond.BinYardSettings>()
    );
  
    const [openFieldSettings, setOpenFieldSettings] = useState(false);
    const [fieldBorders, setFieldBorders] = useState<pond.Shape[]>([]);
  
    //Data for the stuff to be displayed on the map
    const [anchorfieldMarker, setAnchorFieldMarker] = React.useState<null | HTMLElement>(null); // to be used for the menu to display when a field mareker is click
    const [markerData, setMarkerData] = useState<MarkerData[]>([]);
    const [objectKey, setObjectKey] = useState<string>(""); //using this key to replace the marker index variable
    const [isNewObject, setIsNewObject] = useState(false);
    const [yardMarkerData, setYardMarkerData] = useState<Map<string, MarkerData>>(new Map());
    const [yardDrawer, setYardDrawer] = useState(false);
    const [deviceMarkerData, setDeviceMarkerData] = useState<Map<string, MarkerData>>(new Map());
    const [deviceDrawer, setDeviceDrawer] = useState(false);
    const [fieldMarkers, setFieldMarkers] = useState<Map<string, FieldMarker>>(new Map());
    const [fieldMarkerData, setFieldMarkerData] = useState<Map<string, MarkerData>>(new Map());
    const [fmIndexKey, setFMIndexKey] = useState<number>(0);
    const [binMarkerData, setBinMarkerData] = useState<Map<string, MarkerData>>(new Map());
    const [showBinDetails, setShowBinDetails] = useState(false);
    const [binDrawer, setBinDrawer] = useState(false);
    const [bagMarkerData, setBagMarkerData] = useState<Map<string, MarkerData>>(new Map());
    const bagMarkerRef = useRef(bagMarkerData);
    const [bagDrawer, setBagDrawer] = useState(false);
    const [geoData, setGeoData] = useState<Map<string, pond.GeoData>>(new Map());
    const geoRef = useRef(geoData);
    const [showFieldTitles, setShowFieldTitles] = useState(false);
    const [ignoreFeatures, setIgnoreFeatures] = useState(false);
    const [editorMode, setEditorMode] = useState<
      "edit" | "drawPolygon" | "drawLine" | "drawPoint" | "none" | "delete" | undefined
    >();
    const [objectTypeToDraw, setObjectTypeToDraw] = useState<pond.ObjectType>(
      pond.ObjectType.OBJECT_TYPE_UNKNOWN
    );
    const zoomLevels = {
      far: 14,
      close: 18
    };
    const [customSearchEntries, setCustomSearchEntries] = useState<Result[]>([]);
    const [fieldSearchEntries, setFieldSearchEntries] = useState<Result[]>([]);
    const [binSearchEntries, setBinSearchEntries] = useState<Result[]>([]);
    const [bagSearchEntries, setBagSearchEntries] = useState<Result[]>([]);
    const [deviceSearchEntries, setDeviceSearchEntries] = useState<Result[]>([]);
    const [yardSearchEntries, setYardSearchEntries] = useState<Result[]>([]);
    const [fieldMarkerSearchEntries, setFieldMarkerSearchEntries] = useState<Result[]>([]);
    const [jdFieldSearchEntries, setJDFieldSearchEntries] = useState<Result[]>([]);
    const [cnhiFieldSearchEntries, setCNHIFieldSearchEntries] = useState<Result[]>([]);
    const [enableFields, setEnableFields] = useState(true);
    const [showAdaptiveFields, setShowAdaptiveFields] = useState(true);
    const [showJDFields, setShowJDFields] = useState(true);
    const [showCNHFields, setShowCNHFields] = useState(true);
    const [measurement, setMeasurement] = useState(false);
    const [measurementData, setMeasurementData] = useState<Map<string, MeasurementData>>(new Map());
    const measurementRef = useRef(measurementData);
    const zoomRef = useRef(15)
    const fieldMarkerZoom = 15
  
    //watches for changes to the viewing as and sets the load boolean to trigger a load
    useEffect(() => {
      if (as !== whoAmI) {
        setWhoAmI(as);
        setLoading(false);
      }
    }, [as, whoAmI]);
  
    const closeDrawers = () => {
      setFieldDrawer(false);
      setYardDrawer(false);
      setDeviceDrawer(false);
      setBinDrawer(false);
      setBagDrawer(false);
    };
  
    const markerClick = (key: string, long: number, lat: number, isMobile: boolean) => {
      closeDrawers();
      clickDelay();
      setIgnoreFeatures(true);
      setObjectKey(key);
      moveMap(lat, long, zoomLevels.close, isMobile);
    };
  
    /**
     * used to load the fields managed by us
     */
    const loadFields = useCallback(() => {
      fieldAPI
        .listFields(500, 0, "asc", "fieldName", undefined, as)
        .then(resp => {
          let fields: Map<string, Field> = new Map();
          let fieldEntries: Result[] = [];
          resp.data.fields.forEach(f => {
            let field = Field.any(f);
            fields.set(field.key(), field);
            fieldEntries.push({
              id: field.key(),
              place_name: field.name(),
              place_type: ["field"],
              center: [field.center().longitude, field.center().latitude],
              address: "",
              type: "Feature",
              text: field.name(),
              bbox: [0,0,0,0],
              geometry: {type: "Point", coordinates: [field.center().longitude, field.center().latitude]},
              context: [],
              properties: {},
              relevance: 0
            });
          });
          setFields(fields);
          setFieldSearchEntries(fieldEntries);
        })
        .catch(err => {
          openSnack("Failed to load Field Mapping");
        });
    }, [fieldAPI, as, openSnack]);
  
    /**
     * used to load the fields managed by John Deere
     */
    const loadJDFields = useCallback(() => {
      johnDeereAPI
        .listFields(100, 0, as)
        .then(resp => {
          let fields: Map<string, Field> = new Map();
          let fieldEntries: Result[] = [];
          if (resp.data.fields) {
            resp.data.fields.forEach(f => {
              let field = Field.any(f);
              fields.set(field.key(), field);
              fieldEntries.push({
                id: field.key(),
                place_name: field.name(),
                place_type: ["field"],
                center: [field.center().longitude, field.center().latitude]
              } as Result);
            });
          }
          setJDFields(fields);
          setJDFieldSearchEntries(fieldEntries);
        })
        .catch(err => {
          openSnack("Failed to load JD Field Mappings");
        });
    }, [johnDeereAPI, as, openSnack]);
  
    /**
     * used to load the fields managed by John Deere
     */
    const loadCNHIFields = useCallback(() => {
      cnhiAPI
        .listFields(100, 0, as)
        .then(resp => {
          let fields: Map<string, Field> = new Map();
          let fieldEntries: Result[] = [];
          if (resp.data.fields) {
            resp.data.fields.forEach(f => {
              let field = Field.any(f);
              fields.set(field.key(), field);
              fieldEntries.push({
                id: field.key(),
                place_name: field.name(),
                place_type: ["field"],
                center: [field.center().longitude, field.center().latitude]
              } as Result);
            });
          }
  
          setCNHIFields(fields);
          setCNHIFieldSearchEntries(fieldEntries);
        })
        .catch(err => {
          openSnack("Failed to load CNHI Field Mappings");
        });
    }, [cnhiAPI, as, openSnack]);
  
    const loadGrainBags = useCallback(() => {
      grainBagAPI
        .listGrainBags(100, 0, undefined, undefined, undefined, undefined, undefined, undefined, as)
        .then(resp => {
          let bags: Map<string, BagModel> = new Map();
          let bagOp: BagModel[] = [];
          let bagMarkerData: Map<string, MarkerData> = new Map();
          let bagEntries: Result[] = [];
          resp.data.grainBags.forEach(bag => {
            let b = BagModel.create(bag);
            bags.set(b.key(), b);
            if (bag.settings) {
              if (!b.settings.startLocation?.longitude && !b.settings.endLocation?.longitude) {
                bagOp.push(b);
              } else {
                //build the data for the marker
                //TODO-CS: think about making the creation of marker data a function in the object model
                let mData: MarkerData = {
                  title: b.name(),
                  longitude: b.centerLocation().longitude,
                  latitude: b.centerLocation().latitude,
                  visibleLevels: { max: 13 },
                  colour: b.settings.theme?.color ?? "white",
                  clickFunc: (e, i, isMobile) => {
                    clickDelay();
                    closeDrawers();
                    setBagDrawer(true);
                    setIgnoreFeatures(true);
                    setObjectKey(b.key());
                    moveMap(
                      b.centerLocation().latitude,
                      b.centerLocation().longitude,
                      zoomLevels.far,
                      isMobile
                    );
                  }
                };
                bagMarkerData.set(b.key(), mData);
                bagEntries.push({
                  center: [b.centerLocation().longitude, b.centerLocation().latitude],
                  id: b.key(),
                  place_name: b.name(),
                  place_type: ["grainBag"],
                  address: "",
                  bbox: [0,0,0,0],
                  context: [],
                  geometry: {type: "Point", coordinates: [b.centerLocation().longitude, b.centerLocation().latitude]},
                  properties: {},
                  relevance: 0,
                  text: "",
                  type: "Feature"
                });
              }
            }
          });
          setBagOptions(bagOp);
          setGrainBags(bags);
          grainBagsRef.current = bags;
          setBagMarkerData(bagMarkerData);
          setBagSearchEntries(bagEntries);
          bagMarkerRef.current = bagMarkerData;
        })
        .catch(err => {
          openSnack("Failed to load Grain Bags");
        });
    }, [grainBagAPI, openSnack, as]); // eslint-disable-line react-hooks/exhaustive-deps
  
    const loadYards = useCallback(() => {
      let yardMap: Map<string, pond.BinYardSettings> = new Map();
      let yardOps: pond.BinYardSettings[] = [];
      let newYardMarkers: Map<string, MarkerData> = new Map();
      let yardEntries: Result[] = [];
  
      binYardAPI
        .listBinYards(400, 0, "asc", undefined, undefined, undefined, as)
        .then(resp => {
          resp.data.yard.forEach(yard => {
            if (yard.settings) {
              yardMap.set(yard.settings.key, yard.settings);
              if (
                (!yard.settings.latitude && !yard.settings.longitude) ||
                (yard.settings.latitude === 0 && yard.settings.longitude === 0)
              ) {
                yardOps.push(yard.settings);
              } else {
                let mData: MarkerData = {
                  title: yard.settings.name,
                  longitude: yard.settings.longitude,
                  latitude: yard.settings.latitude,
                  colour: yard.settings.theme?.color ?? "green",
                  markerIcon: <BinsIcon width={50 * 0.6} height={50 * 0.6} type="light" />,
                  visibleLevels: { max: 17 },
                  clickFunc: (e, i, isMobile) => {
                    if (yard.settings) {
                      clickDelay();
                      closeDrawers();
                      setYardDrawer(true);
                      setIgnoreFeatures(true);
                      setObjectKey(yard.settings.key);
                      moveMap(
                        yard.settings.latitude,
                        yard.settings.longitude,
                        zoomLevels.close,
                        isMobile
                      );
                    }
                  },
                  updateFunc: location => {
                    if (yard.settings) {
                      updateYardMarkers(yard.settings, location.longitude, location.latitude);
                    }
                  }
                };
                newYardMarkers.set(yard.settings.key, mData);
                yardEntries.push({
                  center: [yard.settings.longitude, yard.settings.latitude],
                  id: yard.settings.key,
                  place_name: yard.settings.name,
                  place_type: ["binYard"]
                } as Result);
              }
            }
          });
          setYardMarkerData(newYardMarkers);
          setYardSearchEntries(yardEntries);
          setBinYards(yardMap);
          setYardOptions(yardOps);
        })
        .catch(() => {
          openSnack("Failed to load yard data");
        });
    }, [binYardAPI, openSnack, as]); // eslint-disable-line react-hooks/exhaustive-deps
  
    const loadBins = useCallback(() => {
      binAPI
        .listBins(2000, 0, "asc", "name", undefined, as)
        .then(resp => {
          let binMap: Map<string, IBin> = new Map();
          let markerData: Map<string, MarkerData> = new Map();
          let searchEntries: Result[] = [];
          let binOp: IBin[] = [];
          resp.data.bins.forEach(bin => {
            let b = IBin.create(bin);
            binMap.set(b.key(), b);
            if (b.settings) {
              if (!b.settings.location?.longitude && !b.settings.location?.latitude) {
                binOp.push(b);
              } else {
                let marker: MarkerData = b.getMarkerData(
                  (e, i, isMobile) => {
                    if (b.settings.location?.longitude && b.settings.location?.latitude) {
                      markerClick(
                        b.key(),
                        b.settings.location.longitude,
                        b.settings.location.latitude,
                        isMobile
                      );
                      setBinDrawer(true);
                    }
                  },
                  location => {
                    updateBinMarkers(b, location.longitude, location.latitude);
                  }
                );
                marker.customImage = (
                  <img
                    width={"100%"}
                    height={"100%"}
                    src={binTop}
                    alt="binTopImage"
                    style={{
                      position: "absolute",
                      padding: 6,
                      paddingBottom: 9,
                      paddingLeft: 8,
                      pointerEvents: "none"
                    }}
                  />
                );
                markerData.set(b.key(), marker);
                searchEntries.push({
                  id: b.key(),
                  center: [b.settings.location.longitude, b.settings.location.latitude],
                  place_name: b.name(),
                  place_type: ["bin"]
                } as Result);
              }
            }
          });
          setBinMarkerData(markerData);
          setBinSearchEntries(searchEntries);
          setBins(binMap);
          setBinOptions(binOp);
        })
        .catch(err => {
          openSnack("Failed to load Bin data");
        });
    }, [binAPI, as, openSnack]); // eslint-disable-line react-hooks/exhaustive-deps
  
    const loadDevices = useCallback(() => {
      deviceAPI
        .list(500, 0, "asc", "name", undefined, undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,as)
        .then(resp => {
          let map = new Map<string, DeviceModel>();
          let newDevMarkers: Map<string, MarkerData> = new Map();
          let searchEntries: Result[] = [];
  
          let options: DeviceModel[] = [];
          resp.data.devices.forEach((device, i) => {
            //add device to the main map
            let d = DeviceModel.any(device);
            map.set(d.id().toString(), d);
            //check to make sure the settings are there
            if (d.settings) {
              let long = d.settings.longitude;
              let lat = d.settings.latitude;
              //if location data is undefined or 0/0 make it an option to be mapped
              if ((!long && !lat) || (long === 0 && lat === 0)) {
                let dOption: DeviceModel = d;
                options.push(dOption);
                //otherwise make a new marker for the device
              } else {
                let marker: MarkerData = d.getMarkerData(
                  (e, i, isMobile) => {
                    markerClick(
                      d.id().toString(),
                      d.location().longitude,
                      d.location().latitude,
                      isMobile
                    );
                    setDeviceDrawer(true);
                  },
                  location => {
                    updateDeviceMarkers(d, location.longitude, location.latitude);
                  }
                );
                marker.markerIcon = <BindaptIcon size={50 * 0.6} type="light" />;
                newDevMarkers.set(d.id().toString(), marker);
                searchEntries.push({
                  center: [d.location().longitude, d.location().latitude],
                  id: d.id().toString(),
                  place_name: d.name(),
                  place_type: ["device"]
                } as Result);
              }
            }
          });
          setDevices(map);
          setDeviceOptions(options);
          setDeviceMarkerData(newDevMarkers);
          setDeviceSearchEntries(searchEntries);
        })
        .catch(() => {
          openSnack("Failed to load Devices");
        });
    }, [deviceAPI, openSnack, as]); // eslint-disable-line react-hooks/exhaustive-deps
  
    const fmIcon = (type: pond.FieldMarkerType, size: number) => {
      switch (type) {
        case pond.FieldMarkerType.FIELD_MARKER_TYPE_PESTS:
          return <PestsIcon width={size} height={size} type="light" />;
        case pond.FieldMarkerType.FIELD_MARKER_TYPE_ROCKS:
          return <RocksIcon width={size} height={size} type="light" />;
        case pond.FieldMarkerType.FIELD_MARKER_TYPE_DISEASE:
          return <DiseaseIcon width={size} height={size} type="light" />;
        case pond.FieldMarkerType.FIELD_MARKER_TYPE_WEEDS:
          return <WeedsIcon width={size} height={size} type="light" />;
      }
    };
  
    const nextFM = (fm: FieldMarker) => {
      let arr = Array.from(fieldMarkers.values());
      let i = arr.indexOf(fm);
      let domKey = "marker-" + fmIndexKey;
      let newID = fmIndexKey;
      let newKey = "";
      if (i === arr.length - 1) {
        newID = fmIndexKey - (arr.length - 1);
        //domKey = "marker-" + newID;
        newKey = arr[0].key();
      } else {
        newID = fmIndexKey + 1;
        //domKey = "marker-" + newID;
        newKey = arr[i + 1].key();
      }
      let newFM = fieldMarkers.get(newKey);
      if (newFM) {
        let view = {
          latitude: newFM.lat(),
          longitude: newFM.long(),
          zoom: currentView.zoom,
          transitionDuration: 1000
        }
        setCurrentView(view);
      }
      setObjectKey(newKey);
      setFMIndexKey(newID);
      //setting the anchor to the marker that is in the center (marker you are moving from) so that the menu stays in the correct position
      setAnchorFieldMarker(document.getElementById(domKey));
    };
  
    const prevFM = (fm: FieldMarker) => {
      let arr = Array.from(fieldMarkers.values());
      let i = arr.indexOf(fm);
      let domKey = "marker-" + fmIndexKey;
      let newID = fmIndexKey;
      let newKey = "";
      if (i === 0) {
        newID = fmIndexKey + (arr.length - 1);
        //domKey = "marker-" + newID;
        newKey = arr[arr.length - 1].key();
      } else {
        newID = fmIndexKey - 1;
        //domKey = "marker-" + newID;
        newKey = arr[i - 1].key();
      }
      let newFM = fieldMarkers.get(newKey);
      if (newFM) {
        let view = {
          latitude: newFM.lat(),
          longitude: newFM.long(),
          zoom: currentView.zoom,
          transitionDuration: 1000
        }
        setCurrentView(view);
      }
      setObjectKey(newKey);
      setFMIndexKey(newID);
      //setting the anchor to the marker that is in the center (marker you are moving from) so that the menu stays in the correct position (center of the map)
      setAnchorFieldMarker(document.getElementById(domKey));
    };
  
    //uses the anchor to show a menu at the active marker, not really good for dynamically moving through the markers so I do want to think of a way to use
    //react-map-gl Popup components
    const fieldMarkerMenu = () => {
      let fm = fieldMarkers.get(objectKey);
      return (
        <Menu
          id="groupMenu"
          anchorEl={anchorfieldMarker}
          //getContentAnchorEl={null}
          open={Boolean(anchorfieldMarker)}
          onClose={() => {
            if (new Date().valueOf() > endTime) {
              setAnchorFieldMarker(null);
              setObjectKey("");
            }
          }}
          anchorOrigin={{ vertical: "top", horizontal: "center" }}
          transformOrigin={{ vertical: "top", horizontal: "center" }}
          //style={{marginTop: -5}}
          PaperProps={{
            style: { background: "none" }
          }}>
          <Box display="flex" justifyContent="center" alignItems="center" paddingBottom={1}>
            <IconButton
              className={classes.cycleButton}
              onClick={() => {
                if (fm) {
                  prevFM(fm);
                }
              }}>
              {"<"}
            </IconButton>
            <IconButton
              className={classes.cycleButton}
              onClick={() => {
                if (fm) {
                  nextFM(fm);
                }
              }}>
              {">"}
            </IconButton>
          </Box>
          <Box width={isMobile ? "90vw" : 300} style={{ background: "black", padding: 10 }}>
            <Grid container direction="row" alignItems="center">
              <Grid size={2}>
                <Grid container direction="column" alignItems="center">
                  <Grid>{fm && fmIcon(fm.type(), 30)}</Grid>
                  <Grid>
                    <Typography style={{ fontSize: 10 }}>{fm && fm.typeString()}</Typography>
                  </Grid>
                </Grid>
              </Grid>
              <Grid size={7}>
                <Grid container direction="column">
                  <Grid>
                    <Typography style={{ fontSize: 15, fontWeight: 650 }}>
                      {fm && fm.name()}
                    </Typography>
                  </Grid>
                  <Grid>
                    <Typography style={{ fontSize: 10 }}>{fm && fm.description()}</Typography>
                  </Grid>
                </Grid>
              </Grid>
              <Grid size={3}>
                <Button 
                  style={{ margin: "auto" }} 
                  onClick={() => {
                    setOpenFMSettings(true)
                    setAnchorFieldMarker(null)
                  }}>
                  <Edit />
                </Button>
              </Grid>
            </Grid>
          </Box>
        </Menu>
      );
    };
  
    const loadFieldMarkers = useCallback(() => {
      fieldMarkerAPI
        .listFieldMarkers(500, 0, "asc", "key", undefined, undefined, as)
        .then(resp => {
          let fmMap: Map<string, FieldMarker> = new Map();
          let markerData: Map<string, MarkerData> = new Map();
          let searchEntries: Result[] = [];
          resp.data.fieldMarker.forEach(fieldMarker => {
            let fm = FieldMarker.create(fieldMarker);
            fmMap.set(fm.key(), fm);
            markerData.set(fm.key(), {
              title: fm.name(),
              longitude: fm.long(),
              latitude: fm.lat(),
              colour: fm.defaultColor(),
              markerIcon: fmIcon(fm.type(), 20 * 0.6),
              subtype: fm.type(),
              mini: true,
              clickFunc: (event, index) => {
                console.log(zoomRef)
                clickDelay();
                closeDrawers();
                setIgnoreFeatures(true);
                setCurrentView({
                  latitude: fm.lat(),
                  longitude: fm.long(),
                  zoom: zoomRef.current,
                  transitionDuration: 0
                  //transitionInterpolator: new FlyToInterpolator()
                });
                setAnchorFieldMarker(event.currentTarget);
                setFMIndexKey(index);
                setObjectKey(fm.key());
              }
            });
            searchEntries.push({
              center: [fm.long(), fm.lat()],
              id: fm.key(),
              place_name: fm.name(),
              place_type: ["fieldMarker"]
            } as Result);
          });
          setFieldMarkers(fmMap);
          setFieldMarkerData(markerData);
          setFieldMarkerSearchEntries(searchEntries);
        })
        .catch(() => {
          openSnack("Failed to load Scouting Markers");
        });
    }, [fieldMarkerAPI, openSnack, as]);
  
    useEffect(() => {
      if (!loading) {
        setLoading(true);
        loadFields();
        loadJDFields();
        loadCNHIFields();
        loadBins();
        loadYards();
        loadDevices();
        loadFieldMarkers();
        loadGrainBags();
      }
    }, [
      as,
      loadBins,
      loadYards,
      loadDevices,
      loadFieldMarkers,
      loadFields,
      loadJDFields,
      loadCNHIFields,
      loadGrainBags,
      loading
    ]);
  
    //combine the different object markerdata into a single array to pass into the map
    useEffect(() => {
      let yArray = Array.from(yardMarkerData.values());
      let dArray = Array.from(deviceMarkerData.values());
      let fmArray = Array.from(fieldMarkerData.values());
      let bArray = Array.from(binMarkerData.values());
      let bagArray = Array.from(bagMarkerData.values());
      let md = yArray.concat(dArray, fmArray, bArray, bagArray);
      setMarkerData(md);
    }, [
      yardMarkerData,
      deviceMarkerData,
      fieldMarkerData,
      binMarkerData,
      bagMarkerData
      //fieldSearchEntries
    ]);
  
    //combine the seperate objects search entries into a single array to pass into the map
    useEffect(() => {
      setCustomSearchEntries(
        fieldSearchEntries.concat(
          binSearchEntries,
          bagSearchEntries,
          yardSearchEntries,
          deviceSearchEntries,
          fieldMarkerSearchEntries,
          jdFieldSearchEntries,
          cnhiFieldSearchEntries
        )
      );
    }, [
      fieldSearchEntries,
      binSearchEntries,
      bagSearchEntries,
      yardSearchEntries,
      deviceSearchEntries,
      fieldMarkerSearchEntries,
      jdFieldSearchEntries,
      cnhiFieldSearchEntries
    ]);
  
    useEffect(() => {
      let gd: Map<string, pond.GeoData> = new Map();
      let currentFields: Map<string, Field> = new Map();
      if (enableFields) {
        if (showAdaptiveFields) {
          fields.forEach(field => {
            if (field.settings.fieldGeoData) {
              let geo = field.settings.fieldGeoData;
              geo.objectKey = field.settings.key;
              geo.title = field.settings.fieldName;
              if (!geo.origin) {
                geo.origin = pond.DataOrigin.DATA_ORIGIN_ADAPTIVE;
              }
              gd.set(field.key(), geo);
              currentFields.set(field.key(), field);
            }
          });
        }
        if (showJDFields) {
          jdFields.forEach(field => {
            if (field.settings.fieldGeoData) {
              let geo = field.settings.fieldGeoData;
              geo.colour = "green";
              geo.objectKey = field.settings.key;
              geo.title = field.settings.fieldName;
              geo.origin = pond.DataOrigin.DATA_ORIGIN_JOHN_DEERE;
              gd.set(field.key(), geo);
              currentFields.set(field.key(), field);
            }
          });
        }
        if (showCNHFields) {
          cnhiFields.forEach(field => {
            if (field.settings.fieldGeoData) {
              let geo = field.settings.fieldGeoData;
              geo.colour = "green";
              geo.objectKey = field.settings.key;
              geo.title = field.settings.fieldName;
              geo.origin = pond.DataOrigin.DATA_ORIGIN_CASE_NEW_HOLLAND;
              gd.set(field.key(), geo);
              currentFields.set(field.key(), field);
            }
          });
        }
      }
  
      grainBags.forEach(grainbag => {
        let bagGeoData: pond.GeoData = pond.GeoData.create({
          colour: "white",
          geoShape: "LineString",
          objectKey: grainbag.key(),
          shapes: [
            pond.Shape.create({
              points: [grainbag.startLocation(), grainbag.endLocation()]
            })
          ]
        });
        gd.set(grainbag.key(), bagGeoData);
      });
  
      setGeoData(gd);
      //fieldsRef.current = new Map([...fields, ...jdFields, ...cnhiFields]);
      fieldsRef.current = currentFields;
      geoRef.current = gd;
    }, [
      fields,
      grainBags,
      jdFields,
      cnhiFields,
      enableFields,
      showAdaptiveFields,
      showJDFields,
      showCNHFields
    ]);
  
    const closeNewMarkerDialog = () => {
      if (new Date().valueOf() > endTime) {
        setMarkerDialogOpen(false);
        setSelectKey("");
      }
    };
  
    const newMarker = (long: number, lat: number) => {
      clickDelay();
      setNewMarkerLong(long);
      setNewMarkerLat(lat);
      setUpdate(false);
      if (markerType === pond.ObjectType.OBJECT_TYPE_FIELDMARKER) {
        setOpenFMSettings(true);
      } else if (markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
        if (isNewObject) {
          createNewObject();
        } else {
          setMarkerDialogOpen(true);
        }
      }
    };
  
    const updateYardMarkers = (
      yard: pond.BinYardSettings,
      long: number,
      lat: number,
      newMarker?: boolean
    ) => {
      yard.longitude = long;
      yard.latitude = lat;
      binYardAPI
        .updateBinYard(yard.key, yard, undefined, as)
        .then(resp => {
          openSnack("BinYard Location Updated");
          if (yardOptions.includes(yard)) {
            yardOptions.splice(yardOptions.indexOf(yard), 1);
          }
          if (newMarker) {
            //make new marker data
            let mData: MarkerData = {
              title: yard.name,
              longitude: yard.longitude,
              latitude: yard.latitude,
              colour: yard.theme?.color ?? "green",
              markerIcon: <BinsIcon width={50 * 0.6} height={50 * 0.6} type="light" />,
              visibleLevels: { max: 17 },
              clickFunc: (e, i, isMobile) => {
                if (yard) {
                  clickDelay();
                  closeDrawers();
                  setYardDrawer(true);
                  setIgnoreFeatures(true);
                  setObjectKey(yard.key);
                  moveMap(yard.latitude, yard.longitude, zoomLevels.close, isMobile);
                }
              },
              updateFunc: location => {
                if (yard) {
                  updateYardMarkers(yard, location.longitude, location.latitude);
                }
              }
            };
            //add it into the state and clone then set to force and update
            let data = clone(yardMarkerData);
            data.set(yard.key, mData);
            setYardMarkerData(data);
          }
        })
        .catch(err => {
          openSnack("Could not update BinYard location");
        });
    };
  
    const createNewObject = () => {
      clickDelay();
      if (markerType === pond.ObjectType.OBJECT_TYPE_BIN) {
        setBinSettingsOpen(true);
      }
    };
  
    const updateBinMarkers = (bin: IBin, long: number, lat: number, newMarker?: boolean) => {
      bin.setLocation(long, lat);
      binAPI
        .updateBin(bin.key(), bin.settings, as)
        .then(resp => {
          openSnack("Bin Location Updated");
          //remove from options
          if (binOptions.includes(bin)) {
            binOptions.splice(binOptions.indexOf(bin), 1);
          }
          if (newMarker) {
            //create marker
            let marker: MarkerData = bin.getMarkerData(
              (e, i, isMobile) => {
                if (bin.settings.location?.longitude && bin.settings.location?.latitude) {
                  markerClick(
                    bin.key(),
                    bin.settings.location.longitude,
                    bin.settings.location.latitude,
                    isMobile
                  );
                  setBinDrawer(true);
                }
              },
              location => {
                updateBinMarkers(bin, location.longitude, location.latitude);
              }
            );
            marker.customImage = (
              <img
                width={"100%"}
                height={"100%"}
                src={binTop}
                alt="binTopImage"
                style={{
                  position: "absolute",
                  padding: 6,
                  paddingBottom: 9,
                  paddingLeft: 8,
                  pointerEvents: "none"
                }}
              />
            );
            let data = clone(binMarkerData);
            data.set(bin.key(), marker);
            setBinMarkerData(data);
          }
        })
        .catch(err => {
          openSnack("Could not update Bin Location");
        });
    };
  
    const updateDeviceMarkers = (
      device: DeviceModel,
      long: number,
      lat: number,
      newMarker?: boolean
    ) => {
      device.settings.longitude = long;
      device.settings.latitude = lat;
      deviceAPI
        .update(device.id(), device.settings, as)
        .then(resp => {
          openSnack("Device location Updated");
          if (deviceOptions.includes(device)) {
            deviceOptions.splice(deviceOptions.indexOf(device), 1);
          }
          if (newMarker) {
            let marker: MarkerData = device.getMarkerData(
              (e, i, isMobile) => {
                markerClick(
                  device.id().toString(),
                  device.location().longitude,
                  device.location().latitude,
                  isMobile
                );
                setDeviceDrawer(true);
              },
              location => {
                updateDeviceMarkers(device, location.longitude, location.latitude);
              }
            );
            marker.markerIcon = <BindaptIcon size={50 * 0.6} type="light" />;
            let data = clone(deviceMarkerData);
            data.set(device.id().toString(), marker);
            setDeviceMarkerData(data);
          }
        })
        .catch(err => {
          openSnack("Could not update Device Location");
        });
    };
  
    const validSelection = () => {
      switch (markerType) {
        case pond.ObjectType.OBJECT_TYPE_BIN:
          return bins.get(selectKey) === undefined;
        case pond.ObjectType.OBJECT_TYPE_BINYARD:
          return binYards.get(selectKey) === undefined;
        case pond.ObjectType.OBJECT_TYPE_DEVICE:
          return devices.get(selectKey) === undefined;
        default:
          return false;
      }
    };
  
    const setMarker = () => {
      if (markerType === pond.ObjectType.OBJECT_TYPE_BIN) {
        let bin = bins.get(selectKey);
        bin && updateBinMarkers(bin, newMarkerLong, newMarkerLat, true);
      }
      if (markerType === pond.ObjectType.OBJECT_TYPE_BINYARD) {
        let yard = binYards.get(selectKey);
        yard && updateYardMarkers(yard, newMarkerLong, newMarkerLat, true);
      }
      if (markerType === pond.ObjectType.OBJECT_TYPE_DEVICE) {
        let device = devices.get(selectKey);
        device && updateDeviceMarkers(device, newMarkerLong, newMarkerLat, true);
      }
      closeNewMarkerDialog();
    };
  
    const moveMap = (
      lat: number,
      long: number,
      zoom: number,
      mobileOffset?: boolean,
      center?: boolean
    ) => {
      let xOff = !mobileOffset ? -250 : undefined;
      let yOff = mobileOffset ? -150 : undefined;
  
      let view = {
        latitude: lat,
        longitude: long,
        zoom: zoom,
        transitionDuration: transDuration,
        xOffset: center ? 0 : xOff,
        yOffset: center ? 0 : yOff
      }
      setCurrentView(view);
      zoomRef.current = zoom
    };
  
    const markerDialog = () => {
      return (
        <ResponsiveDialog open={markerDialogOpen} onClose={closeNewMarkerDialog} fullScreen={false}>
          <DialogTitle>{update ? "Update Marker" : "Place Marker"}</DialogTitle>
          <DialogContent>
            <Box width={300}>
              {markerType === pond.ObjectType.OBJECT_TYPE_BINYARD && (
                <Autocomplete
                  id="autoYardList"
                  options={yardOptions}
                  getOptionLabel={(option: pond.BinYardSettings) => option.name}
                  fullWidth
                  onChange={(e, val) => {
                    val && setSelectKey(val.key);
                  }}
                  renderInput={params => <TextField {...params} label="Yards" />}
                />
              )}
              {markerType === pond.ObjectType.OBJECT_TYPE_BIN && (
                <Autocomplete
                  id="autoBinList"
                  options={binOptions.sort((a, b) =>
                    b.settings.yardKey.localeCompare(a.settings.yardKey)
                  )}
                  groupBy={options => {
                    let yard = binYards.get(options.settings.yardKey);
                    if (yard) {
                      return yard.name;
                    }
                    return "No Yard";
                  }}
                  getOptionLabel={(option: IBin) => option.name()}
                  fullWidth
                  onChange={(e, val) => {
                    val && setSelectKey(val.key());
                  }}
                  renderInput={params => <TextField {...params} label="Bins" />}
                />
              )}
              {markerType === pond.ObjectType.OBJECT_TYPE_DEVICE && (
                <Autocomplete
                  id="autoDeviceList"
                  options={deviceOptions}
                  getOptionLabel={(option: DeviceModel) => option.name()}
                  fullWidth
                  onChange={(e, val) => {
                    val && setSelectKey(val.id().toString());
                  }}
                  renderInput={params => <TextField {...params} label="Devices" />}
                />
              )}
            </Box>
          </DialogContent>
          <DialogActions>
            <Box>
              <Button onClick={closeNewMarkerDialog}>Cancel</Button>
              <Button onClick={setMarker} disabled={validSelection()}>
                Plot New Marker
              </Button>
            </Box>
          </DialogActions>
        </ResponsiveDialog>
      );
    };
  
    //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 updateFieldGeo = (key: string, geoData: pond.GeoData) => {
      let field = fieldsRef.current.get(key);
      if (field) {
        let newSettings = field.settings;
        newSettings.fieldGeoData = geoData;
        fieldAPI
          .updateField(key, newSettings, undefined, as)
          .then(resp => {
            let cloneGeo = clone(geoRef.current);
            geoData.objectKey = key;
            cloneGeo.set(key, geoData);
            geoRef.current = cloneGeo;
            setGeoData(cloneGeo);
            openSnack("updated fields geographical data");
          })
          .catch(err => {
            openSnack("failed to update fields geographical data");
          });
      }
    };
  
    const removeFieldGeo = (key: string) => {
      let field = fieldsRef.current.get(key);
      if (field) {
        fieldAPI
          .removeField(key, as)
          .then(resp => {
            let cloneGeo = clone(geoRef.current);
            cloneGeo.delete(key);
            geoRef.current = cloneGeo;
            setGeoData(cloneGeo);
            openSnack("field deleted");
          })
          .catch(err => {
            openSnack("there was a problem deleting the field");
          });
      }
    };
  
    const updateMapBags = (newBag: BagModel) => {
      //update the grainbags ref
      grainBagsRef.current.set(newBag.key(), newBag);
  
      //update geo data
      let newGeo = pond.GeoData.create();
      let start = newBag.startLocation();
      let end = newBag.endLocation();
      let shape = shapeFromCoords([
        [start.longitude, start.latitude],
        [end.longitude, end.latitude]
      ]);
      newGeo.geoShape = "LineString";
      newGeo.colour = "white";
      newGeo.objectKey = newBag.key();
      newGeo.shapes.push(shape);
      let data = clone(geoRef.current);
      data.set(newBag.key(), newGeo);
      setGeoData(data);
      geoRef.current = data;
  
      // update markerdata
      let mData: MarkerData = {
        title: newBag.name(),
        longitude: newBag.centerLocation().longitude,
        latitude: newBag.centerLocation().latitude,
        colour: newBag.settings.theme?.color ?? "white",
        visibleLevels: { max: 14 },
        clickFunc: (e, i, isMobile) => {
          closeDrawers();
          setBagDrawer(true);
          setIgnoreFeatures(true);
          setObjectKey(newBag.key);
          moveMap(
            newBag.centerLocation().latitude,
            newBag.centerLocation().longitude,
            zoomLevels.far,
            isMobile
          );
        }
        // updateFunc: key => {
        //   console.log("updated marker: " + key);
        // }
      };
      let mClone = clone(bagMarkerRef.current);
      mClone.set(newBag.key(), mData);
      bagMarkerRef.current = mClone;
      setBagMarkerData(mClone);
    };
  
    const updateBag = (bag: BagModel) => {
      //update the bag
      grainBagAPI
        .updateGrainBag(bag.key(), bag.name(), bag.settings, as)
        .then(resp => {
          openSnack("Grain Bag location set");
          //if the update succeeded than update the geo state and ref
          if (bag) updateMapBags(bag);
        })
        .catch(err => {
          openSnack("there was a problem setting the bags location");
        });
    };
  
    const setBag = () => {
      if (selectKey === "") {
        setOpenBagSettings(true);
      } else {
        let bag = grainBagsRef.current.get(selectKey);
        if (bag) {
          bag.settings.startLocation = bagCoordinates.current?.start;
          bag.settings.endLocation = bagCoordinates.current?.end;
          updateBag(bag);
        }
        setSelectKey("");
      }
      setOpenBagSelect(false);
    };
  
    const bagSelectDialog = () => {
      return (
        <ResponsiveDialog
          open={openBagSelect}
          onClose={() => {
            setOpenBagSelect(false);
          }}>
          <DialogTitle>Map Grain Bag</DialogTitle>
          <DialogContent style={{ minWidth: 400 }}>
            <Autocomplete
              id="autoBagList"
              options={bagOptions}
              getOptionLabel={(option: BagModel) => option.name()}
              fullWidth
              onChange={(e, val) => {
                val && setSelectKey(val.key());
              }}
              renderInput={params => (
                <TextField {...params} label="Bags (leave blank to create a new one)" />
              )}
            />
          </DialogContent>
          <DialogActions>
            <Box>
              <Button
                onClick={() => {
                  setOpenBagSelect(false);
                  setSelectKey("");
                }}>
                Cancel
              </Button>
              <Button onClick={setBag}>Plot Grain Bag</Button>
            </Box>
          </DialogActions>
        </ResponsiveDialog>
      );
    };
  
    const geocoderResult = (result: Result) => {
      if(result.id !== undefined){
        setObjectKey(result.id.toString());
        if (result.place_type.includes("bin")) {
          setBinDrawer(true);
        } else if (result.place_type.includes("field")) {
          setFieldDrawer(true);
        } else if (result.place_type.includes("grainBag")) {
          setBagDrawer(true);
        } else if (result.place_type.includes("device")) {
          setDeviceDrawer(true);
        } else if (result.place_type.includes("binYard")) {
          setYardDrawer(true);
        }
      }
    };
  
    const geocoderTransition = (result: Result) => {
      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("field") || result.place_type.includes("grainBag")) {
          centered = false;
        } else if (
          result.place_type.includes("bin") ||
          result.place_type.includes("device") ||
          result.place_type.includes("binYard")
        ) {
          zoom = zoomLevels.close;
          centered = false;
        }
        moveMap(lat, long, zoom, isMobile, centered);
      }
    };
  
    return (
      <Box className={classes.map}>
        {/* dialogs */}
        {markerDialog()}
        {fieldMarkerMenu()}
        {bagSelectDialog()}
  
        {/* main map component */}
        <MapBase
          customSearchEntries={customSearchEntries}
          geocoderResultFunction={geocoderResult}
          geocoderTransitionFunction={geocoderTransition}
          placingMarker={markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN}
          linePointLimit={objectTypeToDraw === pond.ObjectType.OBJECT_TYPE_GRAIN_BAG ? 2 : undefined}
          cutHoleInPolygon={(object, coords, type) => {
            //because this function is technically run in an event handler you need to use refs for state variables
            if (type === "Polygon") {
              coords.forEach(set => {
                let newHole = shapeFromCoords(set);
                let fieldsGeo = geoRef.current.get(object);
                if (fieldsGeo && fieldsGeo.origin === pond.DataOrigin.DATA_ORIGIN_ADAPTIVE) {
                  fieldsGeo.holes.push(newHole);
                  updateFieldGeo(object, fieldsGeo);
                } else {
                  openSnack("Field Boundaries maintained externally");
                }
              });
            }
          }}
          objectTitles={showFieldTitles}
          currentView={currentView}
          drawMeasurement={measurement}
          mapTools={
            <AgMapTools
              editorMode={editorMode}
              binMarkerDetails={showBinDetails}
              toggleBinMarkerDetails={show => {
                setShowBinDetails(show);
              }}
              toggleObjectEditType={setObjectTypeToDraw}
              toggleEditorMode={setEditorMode}
              toggleFieldTitles={setShowFieldTitles}
              toggleIsNewObject={setIsNewObject}
              toggleMarkerType={setMarkerType}
              toggleMeasurement={setMeasurement}
            />
          }
          layerOptions={
            <Box style={{ margin: 5 }}>
              <fieldset style={{ border: "1px solid white", borderRadius: 10 }}>
                <legend>
                  <FormControlLabel
                    label="Field Layer"
                    control={
                      <Checkbox
                        checked={enableFields}
                        onChange={(_, checked) => {
                          setEnableFields(!enableFields);
                        }}
                      />
                    }
                  />
                </legend>
                <Box>
                  <FormControlLabel
                    label="Adaptive"
                    control={
                      <Checkbox
                        disabled={!enableFields}
                        checked={showAdaptiveFields}
                        onChange={(_, checked) => {
                          setShowAdaptiveFields(!showAdaptiveFields);
                        }}
                      />
                    }
                  />
                  <FormControlLabel
                    label="John Deere"
                    control={
                      <Checkbox
                        disabled={!enableFields}
                        checked={showJDFields}
                        onChange={(_, checked) => {
                          setShowJDFields(!showJDFields);
                        }}
                      />
                    }
                  />
                  <FormControlLabel
                    label="Case New Holland"
                    control={
                      <Checkbox
                        disabled={!enableFields}
                        checked={showCNHFields}
                        onChange={(_, checked) => {
                          setShowCNHFields(!showCNHFields);
                        }}
                      />
                    }
                  />
                </Box>
              </fieldset>
            </Box>
          }
          editorMode={editorMode}
          editGeoCallback={(object, borders, holes) => {
            let field = fieldsRef.current.get(object);
            let grainbag = grainBagsRef.current.get(object);
            if (field) {
              let fieldGeo = field.settings.fieldGeoData;
              if (fieldGeo) {
                fieldGeo.shapes = borders;
                if (holes) {
                  fieldGeo.holes = holes;
                }
                updateFieldGeo(object, fieldGeo);
              }
            } else if (grainbag) {
              if (borders.length > 0) {
                let line = borders[0];
                if (line.points.length > 0) {
                  let start = line.points[0];
                  let end = line.points[line.points.length - 1];
                  grainbag.settings.startLocation = start;
                  grainbag.settings.endLocation = end;
                  updateBag(grainbag);
                }
              }
            }
          }}
          addNewShape={(coords, type) => {
            if (type === "Polygon") {
              //if it is a polygon it is a field (for now until we add more objects that use polygons)
              let newShapes: pond.Shape[] = [];
              coords.forEach(set => {
                newShapes.push(shapeFromCoords(set));
              });
              setFieldBorders(newShapes);
              setOpenFieldSettings(true);
            } else if (type === "LineString") {
              //if it is a line string it is a grain bag
              let line = shapeFromCoords(coords); //if it is a grainbag type the points in the line will be limited to 2 while drawing
              if (line.points.length > 0) {
                let start = line.points[0];
                let end = line.points[line.points.length - 1];
                bagCoordinates.current = { start, end };
              }
              setOpenBagSelect(true);
            }
          }}
          addNewMeasurement={(coords, type) => {
            let measurements = new Map(measurementRef.current);
            if (type === "LineString") {
              //determine length of line from the coordinates
              let totalLengthKm = 0;
              let segmentsKm: number[] = [];
              let prevPoint: number[] = [];
              let currentPoint: number[] = [];
              //loop thorugh the array determine the length of each segment
              coords.forEach(pair => {
                currentPoint = pair;
                if (prevPoint.length === 2) {
                  let long1 = prevPoint[0];
                  let lat1 = prevPoint[1];
                  let long2 = currentPoint[0];
                  let lat2 = currentPoint[1];
  
                  let segmentLengthKm = coordDistance(lat1, long1, lat2, long2);
                  segmentsKm.push(segmentLengthKm);
                  totalLengthKm = totalLengthKm + segmentLengthKm;
                }
                prevPoint = pair;
              });
              //make a new measurement data
              let measurement: MeasurementData = {
                key: "measurement" + measurements.size,
                coordinates: coords,
                geoShape: type,
                segmentDistanceKm: segmentsKm,
                totalDistanceKm: totalLengthKm
              };
              //add to the list of measurements
              measurements.set(measurement.key, measurement);
              measurementRef.current = measurements;
              setMeasurementData(measurements);
            }
          }}
          objectGeoData={geoData}
          measurementData={measurementData}
          mapClick={event => {
            if (markerType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
              newMarker(event.lngLat.lng, event.lngLat.lat);
            } else if (editorMode === "delete") {
              if (event.features) {
                let selected = event.features[0];
                if (selected && selected.properties && selected.properties.objectKey) {
                  removeFieldGeo(selected.properties.objectKey);
                }
              }
            } else if (editorMode === "none" || !editorMode) {
              //if no modes are active it was just a click on the map
              if (event.features && !ignoreFeatures) {
                let selected = event.features[0];
                if (selected && selected.properties && selected.properties.objectKey) {
                  let center = pond.Location.create({
                    latitude: currentView.latitude,
                    longitude: currentView.longitude
                  });
                  setObjectKey(selected.properties.objectKey);
                  closeDrawers();
                  let bag = grainBagsRef.current.get(selected.properties.objectKey);
                  let field = fieldsRef.current.get(selected.properties.objectKey);
                  if (bag) {
                    center = bag.centerLocation();
                    setBagDrawer(true);
                  } else if (field) {
                    center = field.center();
                    setFieldDrawer(true);
                  }
                  moveMap(center.latitude, center.longitude, zoomLevels.far, isMobile);
                }
              } else {
                //if the feature was ignored that means they clicked on a marker so can set the ignore back to false during the click so that the user
                //can still click on a "feature" (field) while the drawer is still open
                setIgnoreFeatures(false);
              }
            }
          }}
          ignoreHomeLoad={props.startingView && true}
          markerData={markerData}
          displayMarkerDetails={showBinDetails}
        />
  
        {/* new objectDrawers - consider making an ag drawers file to contain these components to shirnk this file down further*/}
        {objectKey !== "" && (
          <React.Fragment>
            <BinYardDrawer
              open={yardDrawer}
              onClose={() => {
                if (new Date().valueOf() > endTime) {
                  setYardDrawer(false);
                  setObjectKey("");
                }
              }}
              selectedYard={objectKey}
              yards={binYards}
              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(yardMarkerData);
                  newMarkers.delete(key);
                  setYardMarkerData(newMarkers);
                  //adds it back to the options
                  let yard = binYards.get(key);
                  if (yard) {
                    yardOptions.push(yard);
                  }
                }
              }}
              updateMarker={(key, objectSettings) => {
                if (new Date().valueOf() > endTime) {
                  let newMarkers = clone(yardMarkerData);
                  let toupdate = newMarkers.get(key);
                  if (toupdate) {
                    if (objectSettings.theme) {
                      toupdate.colour = objectSettings.theme.color;
                    }
                  }
                  setYardMarkerData(newMarkers);
                }
              }}
              moveMap={(long, lat) => {
                moveMap(lat, long, zoomLevels.close, isMobile);
              }}
            />
            <DeviceDrawer
              open={deviceDrawer}
              onClose={() => {
                if (new Date().valueOf() > endTime) {
                  setDeviceDrawer(false);
                  setObjectKey("");
                }
              }}
              selectedDevice={objectKey}
              devices={devices}
              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(deviceMarkerData);
                  newMarkers.delete(key);
                  setDeviceMarkerData(newMarkers);
  
                  //adds it back to the options
                  let dev = devices.get(key);
                  if (dev) {
                    deviceOptions.push(dev);
                  }
                }
              }}
              updateMarker={(key, objectSettings) => {
                if (new Date().valueOf() > endTime) {
                  let newMarkers = clone(deviceMarkerData);
                  let toupdate = newMarkers.get(key);
                  if (toupdate) {
                    if (objectSettings.theme) {
                      toupdate.colour = objectSettings.theme.color;
                    }
                  }
                  setDeviceMarkerData(newMarkers);
                }
              }}
              moveMap={(long, lat) => {
                moveMap(lat, long, zoomLevels.far, isMobile);
              }}
            />
            <BinDrawer
              open={binDrawer}
              onClose={() => {
                if (new Date().valueOf() > endTime) {
                  setBinDrawer(false);
                  setObjectKey("");
                }
              }}
              selectedBin={objectKey}
              bins={bins}
              removeMarker={key => {
                if (new Date().valueOf() > endTime) {
                  let newMarkers = clone(binMarkerData);
                  newMarkers.delete(key);
                  setBinMarkerData(newMarkers);
                  //adds it back to the options
                  let bin = bins.get(key);
                  if (bin) {
                    binOptions.push(bin);
                  }
                }
              }}
              updateMarker={(key, objectSettings) => {
                if (new Date().valueOf() > endTime) {
                  let newMarkers = clone(binMarkerData);
                  let toupdate = newMarkers.get(key);
                  if (toupdate) {
                    if (objectSettings.theme) {
                      toupdate.customSize = objectSettings.theme.height;
                      //toupdate.colour = objectSettings.theme.color;
                    }
                  }
                  setBinMarkerData(newMarkers);
                }
              }}
              moveMap={(long, lat) => {
                moveMap(lat, long, zoomLevels.close, isMobile);
              }}
            />
            <GrainBagDrawer
              open={bagDrawer}
              onClose={() => {
                if (new Date().valueOf() > endTime) {
                  setBagDrawer(false);
                  setObjectKey("");
                }
              }}
              grainBags={grainBagsRef.current}
              selectedBag={objectKey}
              removeMarker={key => {
                if (new Date().valueOf() > endTime) {
                  //remove the line from the map
                  let geo = clone(geoRef.current);
                  geo.delete(key);
                  geoRef.current = geo;
                  setGeoData(geo);
  
                  //removes the marker from the map
                  let newMarkers = clone(bagMarkerRef.current);
                  newMarkers.delete(key);
                  bagMarkerRef.current = newMarkers;
                  setBagMarkerData(newMarkers);
  
                  //adds it back to the options
                  let bag = grainBags.get(key);
                  if (bag) {
                    bagOptions.push(bag);
                  }
                }
              }}
              updateMarker={(key, objectSettings) => {
                if (new Date().valueOf() > endTime) {
                  let newMarkers = clone(bagMarkerData);
                  let toupdate = newMarkers.get(key);
                  if (toupdate) {
                    if (objectSettings.theme) {
                      toupdate.colour = objectSettings.theme.color;
                    }
                  }
                  setBinMarkerData(newMarkers);
                }
              }}
              moveMap={(long, lat) => {
                moveMap(lat, long, zoomLevels.far, isMobile);
              }}
            />
            <FieldDrawer
              open={fieldDrawer}
              closeDrawer={() => {
                setFieldDrawer(false);
                setObjectKey("");
              }}
              openSettings={fieldKey => {
                setObjectKey(fieldKey);
                setOpenFieldSettings(true);
              }}
              fields={fieldsRef.current}
              selectedFieldKey={objectKey}
              moveMap={(long, lat) => {
                moveMap(lat, long, zoomLevels.far, isMobile);
              }}
            />
          </React.Fragment>
        )}
  
        {/* settings components for editing objects from the map */}
        <BinSettings
          mode="add"
          open={binSettingsOpen}
          onClose={refresh => {
            if (new Date().valueOf() > endTime) {
              setBinSettingsOpen(false);
            }
            if (refresh) {
              loadBins();
            }
          }}
          canEdit={true}
          userID={user.id()}
          coords={{ longitude: newMarkerLong, latitude: newMarkerLat }}
          binYards={Array.from(binYards.values())}
        />
        <FieldMarkerSettings
          open={openFMSettings}
          close={refresh => {
            if (new Date().valueOf() > endTime) {
              if (refresh) {
                //TODO: possibly change this to just modify the array
                loadFieldMarkers();
              }
              setOpenFMSettings(false);
            }
          }}
          fieldMarker={fieldMarkers.get(objectKey)}
          longitude={newMarkerLong}
          latitude={newMarkerLat}
        />
        <FieldSettings
          selectedField={fieldsRef.current.get(objectKey)}
          updateFields={(key, settings) => {
            let data = clone(geoData);
            settings.key = key;
            if (settings.fieldGeoData) {
              data.set(
                key,
                pond.GeoData.create({
                  colour: settings.fieldGeoData.colour,
                  geoShape: "Polygon",
                  shapes: settings.fieldGeoData.shapes,
                  holes: settings.fieldGeoData.holes,
                  objectKey: key,
                  title: settings.fieldName,
                  origin: settings.fieldGeoData.origin
                })
              );
              geoRef.current = data;
              setGeoData(data);
  
              fieldsRef.current.set(key, Field.create(pond.Field.create({ settings: settings })));
            }
          }}
          removeField={field => {
            let data = clone(geoData);
            data.delete(field.key());
            geoRef.current = data;
            setGeoData(data);
            fieldsRef.current.delete(field.key());
            setFieldDrawer(false);
            setObjectKey("");
          }}
          open={openFieldSettings}
          onClose={() => {
            setOpenFieldSettings(false);
          }}
          borders={fieldBorders}
        />
        <GrainBagSettings
          open={openBagSettings}
          coordinates={bagCoordinates.current}
          close={newBag => {
            if (newBag) {
              updateMapBags(newBag);
            }
            bagCoordinates.current = undefined;
            setOpenBagSettings(false);
          }}
        />
      </Box>
    );
  }