import {
  Autocomplete,
  Avatar,
  Box,
  Button,
  Checkbox,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  Divider,
  ListItem,
  ListItemAvatar,
  ListItemSecondaryAction,
  ListItemText,
  ListSubheader,
  TextField,
  Typography,
  useTheme
} from "@mui/material";
import { GetGrainExtensionMap, GrainExtension } from "grain";
import { useComponentAPI, useInteractionsAPI, useSnackbar } from "hooks";
import { lowerCase } from "lodash";
import { Component, Device, Interaction } from "models";
import { GrainCable } from "models/GrainCable";
import { Controller } from "models/Controller";
import { Plenum } from "models/Plenum";
import moment from "moment";
import { GetComponentIcon } from "pbHelpers/ComponentType";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond, quack } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
import CableTopNodeSummary from "bin/CableTopNodeSummary";
import { getTemperatureUnit } from "utils";
import { DevicePreset } from "models/DevicePreset";
import { Ambient } from "models/Ambient";
import { useGlobalState } from "providers";

/*const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    fullHeightCard: {
      height: "100%"
    },
    cardHeader: {
      padding: `${theme.spacing(0.5)}px ${theme.spacing(1)}px`,
      [theme.breakpoints.up("sm")]: {
        padding: theme.spacing(1)
      }
    },
    backdrop: {
      zIndex: theme.zIndex.drawer + 1,
      color: "#fff"
    },
    cooldown: {
      background: "linear-gradient(90deg, rgba(219,50,45,1) 0%, rgba(51,144,218,1) 100%)"
    },
    experimental: {
      background:
        "linear-gradient(90deg, rgba(50,140,73,1) 0%, rgba(98,79,133,1) 69%, rgba(60,44,87,1) 100%)",
      fallbacks: {
        background: "rgb(50,140,73)"
      }
    }
  })
);*/

interface Option {
  label: string;
  id: number;
  icon?: string;
}

interface DevicePresetsProps {
  binKey: string;
  devices: Device[];
  refreshCallback: (success: boolean) => void;
  parentPreset?: pond.BinMode;
  grain?: pond.Grain;
  preferences?: Map<string, pond.BinComponentPreferences>;
  binCables?: GrainCable[];
  compDevMap?: Map<string, number>;
  presets?: DevicePreset[];
}

type Preset =
  | GrainExtension
  | "cooldown"
  | "experimental"
  | "drying"
  | "shop"
  | "storage"
  | "hydrating";

interface PresetDescriber {
  heaterHumidity: number;
  heaterTempC: number;
  fanHumidity: number;
  fanTempC: number;
}

const CooldownPreset: PresetDescriber = {
  heaterHumidity: 45,
  heaterTempC: 10,
  fanHumidity: 60,
  fanTempC: 15
};

const StoragePreset: PresetDescriber = {
  heaterHumidity: 20,
  heaterTempC: 0,
  fanHumidity: 60,
  fanTempC: 15
};

const ExperimentalPreset: PresetDescriber = {
  heaterHumidity: 20,
  heaterTempC: 40,
  fanHumidity: 60,
  fanTempC: 15
};

//hydrating only uses the fan
const HydratingPreset = {
  humidity: 60,
  tempC: 25
};

//shop only uses the heater
const ShopPreset = {
  tempAbove: 18,
  tempBelow: 8
};

export default function DevicePresets(props: DevicePresetsProps) {
  const { error, success } = useSnackbar();
  const componentAPI = useComponentAPI();
  const interactionAPI = useInteractionsAPI();
  const {
    devices,
    binKey,
    refreshCallback,
    parentPreset,
    grain,
    preferences,
    binCables,
    compDevMap,
    presets
  } = props;
  const grainExtensionMap = GetGrainExtensionMap();
  const [confirmPreset, setConfirmPreset] = useState<Preset | null>(null);
  const [plenumIndex, setPlenumIndex] = useState<number | undefined>(0);
  const [ambientIndex, setAmbientIndex] = useState<number | undefined>();
  const [heaterIndex, setHeaterIndex] = useState<number>();
  const [fanIndex, setFanIndex] = useState<number>();
  const [updatingDeviceInteractions, setUpdatingDeviceInteractions] = useState(false);
  const [DryingPreset, setDryingPreset] = useState<PresetDescriber>({
    heaterHumidity: 20,
    heaterTempC: 40,
    fanHumidity: 55,
    fanTempC: 15
  });
  const [presetOptions, setPresetOptions] = useState<Option[]>([]);
  const [controllerType, setControllerType] = useState(pond.ControllerType.CONTROLLER_TYPE_UNKNOWN);
  const [selectedPreset, setSelectedPreset] = useState<DevicePreset | undefined>(undefined);

  // Device Picker variables
  const theme = useTheme();
  const [deviceComponents, setDeviceComponents] = useState<Map<string, Component[]>>(
    new Map<string, Component[]>()
  );
  const [interactions, setInteractions] = useState<Interaction[]>([]);
  const [deviceIndex, setDeviceIndex] = useState<number>(-1);
  const [options, setOptions] = useState<Option[]>([]);
  const [loading, setLoading] = useState(false);

  // Component types for display
  const [plenums, setPlenums] = useState<Plenum[]>([]);
  const [ambients, setAmbients] = useState<Ambient[]>([]);
  //const [grainCables, setGrainCables] = useState<GrainCable[]>([]);
  const [heaters, setHeaters] = useState<Controller[]>([]);
  const [fans, setFans] = useState<Component[]>([]);
  //const [subscribeBinToAutoTopNode, setSubscribeBinToAutoTopNode] = useState(false);
  const [{as}] = useGlobalState();

  useEffect(() => {
    if (deviceComponents.get(deviceIndex.toString())) return;
    if (!binKey) return;
    if (deviceIndex < 1) return;
    let comps = deviceComponents.get(deviceIndex.toString());
    if (!comps) {
      comps = [];
      setLoading(true);
      let interactionPromise = interactionAPI.listInteractionsByDevice(deviceIndex, undefined, as);
      let componentPromise = componentAPI.list(deviceIndex, undefined, [binKey], ["bin"], true, as);
      let dComponents = new Map<string, Component[]>();
      Promise.all([componentPromise, interactionPromise])
        .then(([compResp, interactionResp]) => {
          let d = pond.ListComponentsResponse.fromObject(compResp.data);
          d.components.forEach(comp => {
            let c = Component.create(comp);
            let deviceNumber = compResp.data.componentDevices[c.key()];
            comps!.push(c);
            let newDComponents = dComponents.get(deviceNumber.toString());
            if (newDComponents === undefined) newDComponents = [];
            newDComponents.push(c);
            dComponents.set(deviceNumber.toString(), newDComponents);
          });
          setDeviceComponents(dComponents);
          //if (comps) {
          //deviceComponents.set(deviceIndex, comps);
          //}
          setInteractions(interactionResp);
        })
        .finally(() => {
          setLoading(false);
        });
    }
  }, [deviceIndex, binKey, componentAPI, interactionAPI, deviceComponents, as]);

  useEffect(() => {
    if (loading) return;
    if (!deviceComponents.get(deviceIndex.toString())) return;
    var plenums: Plenum[] = [];
    var ambients: Ambient[] = [];
    //var grainCables: GrainCable[] = [];
    var heaters: Controller[] = [];
    var fans: Component[] = [];
    deviceComponents.get(deviceIndex.toString())!.forEach(comp => {
      let pref = preferences?.get(comp.key());
      if (pref) {
        if (pref.type) {
          if (pref.type === pond.BinComponent.BIN_COMPONENT_PLENUM)
            plenums.push(Plenum.create(comp));
          if (pref.type === pond.BinComponent.BIN_COMPONENT_AMBIENT)
            ambients.push(Ambient.create(comp));
          // if (pref.type === pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE)
          //   grainCables.push(GrainCable.create(comp));
          if (pref.type === pond.BinComponent.BIN_COMPONENT_HEATER) {
            let heater = Controller.create(comp);
            heater.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_OFF;
            heaters.push(heater);
          }
          if (pref.type === pond.BinComponent.BIN_COMPONENT_FAN) {
            comp.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_OFF;
            fans.push(comp);
          }
        }
      }
      //possibly get rid of this else so that only "set" bin components can be used for interactions
      // else {
      //   if (comp.settings.type === quack.ComponentType.COMPONENT_TYPE_DHT) {
      //     plenums.push(Plenum.create(comp));
      //   }
      //   if (comp.settings.type === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE)
      //     grainCables.push(GrainCable.create(comp));
      //   else if (comp.settings.type === quack.ComponentType.COMPONENT_TYPE_BOOLEAN_OUTPUT) {
      //     if (comp.settings.subtype === 2) heaters.push(Heater.create(comp));
      //     if (comp.settings.subtype === 3) {
      //       fans.push(comp);
      //     }
      //   }
      // }
    });
    setPlenums(plenums);
    setAmbients(ambients);
    //if there is no plenum but there is an ambient make the ambient the default
    if (plenums.length === 0 && ambients.length > 0) {
      setAmbientIndex(0);
      setPlenumIndex(undefined);
    }
    //setGrainCables(grainCables);
    setHeaters(heaters);
    setFans(fans);
  }, [loading, setPlenums, setHeaters, deviceComponents, deviceIndex, preferences]);

  useEffect(() => {
    if (parentPreset) {
      if (parentPreset === pond.BinMode.BIN_MODE_STORAGE) setConfirmPreset("storage");
      if (parentPreset === pond.BinMode.BIN_MODE_COOLDOWN) setConfirmPreset("cooldown");
      if (parentPreset === pond.BinMode.BIN_MODE_DRYING) setConfirmPreset("drying");
      if (parentPreset === pond.BinMode.BIN_MODE_HYDRATING) setConfirmPreset("hydrating");
    }
  }, [parentPreset]);

  useEffect(() => {
    if (grain) {
      grainExtensionMap.forEach((ext, key) => {
        if (key === grain) {
          setDryingPreset({
            heaterHumidity: ext.targetMC,
            heaterTempC: ext.setTempC,
            fanHumidity: 55,
            fanTempC: 15
          });
        }
        //TODO-CS: if grain types get different humidity thresholds set them here
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [grain]);

  useEffect(() => {
    if (presets && parentPreset) {
      let presetOptions: Option[] = [];
      presets.forEach((devicePreset, i) => {
        //if the preset type and the bin mode type "match" and the controller type of the preset matches the controller type that was selected
        if (
          devicePreset.compareToBinMode(parentPreset) &&
          devicePreset.controllerType() === controllerType
        ) {
          //add the preset to the possible options
          if (devicePreset.favorite()) {
            //if it is favorited add it to the front of the array
            presetOptions.unshift({
              label: devicePreset.name,
              id: i
            });
          } else {
            //otherwise add it to the end
            presetOptions.push({
              label: devicePreset.name,
              id: i
            });
          }
        }
      });
      setPresetOptions(presetOptions);
    }
  }, [presets, controllerType, parentPreset]);

  const getSensor = () => {
    let sensor = Plenum.create(Component.create());
    if (ambientIndex !== undefined) sensor = ambients[ambientIndex];
    if (plenumIndex !== undefined) sensor = plenums[plenumIndex];
    if (sensor.key() === "") {
      error("Invalid plenum sensor");
    }
    return sensor;
  };

  const getHeater = () => {
    let heater;
    if (heaterIndex !== undefined) heater = heaters[heaterIndex];
    // if (!heater) {
    //   error("No heater attached");
    // }
    return heater;
  };

  const getFan = () => {
    let fan;
    if (fanIndex !== undefined) fan = fans[fanIndex];
    // if (!fan) {
    //   error("No fan attached");
    // }
    return fan;
  };

  /**
   * function will update the fans and heaters to have the correct ouput state - on the initial load it will have been set to off,
   * then based on selections the output state will be changed
   * this function will then update all of the controllers
   */
  const updateControllers = () => {
    //if the device id was passed in the mode was set to storage and we need to set them to off
    heaters.forEach(heater => {
      componentAPI
        .update(deviceIndex, heater.settings, undefined, undefined, as)
        .then()
        .catch();
    });
    fans.forEach(fan => {
      componentAPI
        .update(deviceIndex, fan.settings, undefined, undefined, as)
        .then()
        .catch();
    });
  };

  const buildFanInteraction = (
    preset: Preset,
    plenum: Plenum,
    fan: Component,
    tempComparison: "greater" | "less",
    humidityComparison: "greater" | "less"
  ) => {
    let interaction = pond.InteractionSettings.create({
      source: plenum.location(),
      sink: fan.location(),
      schedule: pond.InteractionSchedule.create({
        timeOfDayStart: "00:00",
        timeOfDayEnd: "24:00",
        timezone: moment.tz.guess(),
        weekdays: moment.weekdays().map(d => lowerCase(d))
      }),
      notifications: pond.InteractionNotifications.create({
        reports: true
      }),
      result: pond.InteractionResult.create({
        type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
        value: 1
      })
    });

    let tempPreset = 0;
    let humidityPreset = 0;

    //if a custom preset was selected use it
    if (selectedPreset !== undefined) {
      tempPreset = selectedPreset.temp();
      humidityPreset = selectedPreset.hum();
    } else {
      //otherwise use one of the defaults
      switch (preset) {
        case "cooldown":
          tempPreset = CooldownPreset.fanTempC;
          humidityPreset = CooldownPreset.fanHumidity;
          break;
        case "drying":
          tempPreset = DryingPreset.fanTempC;
          humidityPreset = DryingPreset.fanHumidity;
          break;
        case "hydrating":
          tempPreset = HydratingPreset.tempC;
          humidityPreset = HydratingPreset.humidity;
          break;
      }
    }

    let conditions = [];
    let fanConditionOne = pond.InteractionCondition.create({
      measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
      comparison:
        humidityComparison === "greater"
          ? quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN
          : quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
      value: describeMeasurement(
        quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
        plenum.settings.type,
        plenum.settings.subtype
      ).toStored(humidityPreset)
    });
    conditions.push(fanConditionOne);

    //since the measurement describers function toStored does a conversion into celsius for temperature if the users pref is fahrenheit we need to convert the preset value into fahrenheit
    if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      tempPreset = Math.round((tempPreset * (9 / 5) + 32) * 100) / 100;
    }
    let tempVal = describeMeasurement(
      quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
      plenum.settings.type,
      plenum.settings.subtype
    ).toStored(tempPreset);

    let fanConditionTwo = pond.InteractionCondition.create({
      measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
      comparison:
        tempComparison === "greater"
          ? quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN
          : quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
      value: Math.round(tempVal) //and then round the converted value since the interaction does not take decimals
    });
    conditions.push(fanConditionTwo);

    interaction.conditions = conditions;
    return interaction;
  };

  const buildHeatingInteraction = (
    preset: Preset,
    plenum: Plenum | Ambient,
    heater: Controller,
    heaterState: boolean,
    tempComparison: "greater" | "less"
  ): pond.InteractionSettings => {
    let interaction = pond.InteractionSettings.create({
      source: plenum.location(),
      sink: heater.location(),
      schedule: pond.InteractionSchedule.create({
        timeOfDayStart: "00:00",
        timeOfDayEnd: "24:00",
        timezone: moment.tz.guess(),
        weekdays: moment.weekdays().map(d => lowerCase(d))
      }),
      notifications: pond.InteractionNotifications.create({
        reports: true
      }),
      result: pond.InteractionResult.create({
        type:
          preset === "shop"
            ? quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET
            : quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
        value: heaterState ? 1 : 0
      })
    });

    let temp = 0;
    let hum = 0;

    //if a preset was selected use its temp/hum values
    if (selectedPreset !== undefined) {
      temp = selectedPreset.temp();
      hum = selectedPreset.hum();
    } else {
      //otherwise use one of the defautls
      temp =
        preset === "cooldown"
          ? CooldownPreset.heaterTempC
          : preset === "experimental"
          ? ExperimentalPreset.heaterTempC
          : preset === "drying"
          ? DryingPreset.heaterTempC
          : preset === "hydrating"
          ? DryingPreset.heaterTempC
          : preset === "shop"
          ? tempComparison === "less"
            ? ShopPreset.tempBelow
            : ShopPreset.tempAbove
          : preset === "storage"
          ? StoragePreset.heaterTempC
          : preset.setTempC;
      hum = preset === "cooldown" ? CooldownPreset.heaterHumidity : DryingPreset.heaterHumidity;
    }

    let conditions = [];
    if (preset === "cooldown" || preset === "experimental" || preset === "drying") {
      let humidityCondition = pond.InteractionCondition.create({
        measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
        comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
        value: Math.round(
          describeMeasurement(
            quack.MeasurementType.MEASUREMENT_TYPE_PERCENT,
            plenum.settings.type,
            plenum.settings.subtype
          ).toStored(hum)
        )
      });
      conditions.push(humidityCondition);
    }

    //since the measurement describers function to stored does a conversion into celsius if the users pref is fahrenheit for temperature we need to convert the preset value into fahrenheit
    if (getTemperatureUnit() === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      temp = Math.round((temp * (9 / 5) + 32) * 100) / 100;
    }
    let tempVal = describeMeasurement(
      quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
      plenum.settings.type,
      plenum.settings.subtype
    ).toStored(temp);

    let tempCondition = pond.InteractionCondition.create({
      measurementType: quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE,
      comparison:
        tempComparison === "less"
          ? quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN
          : quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
      value: Math.round(tempVal) //and then round the converted value since interaction conditions wont take floats
    });
    conditions.push(tempCondition);
    interaction.conditions = conditions;

    return interaction;
  };

  //kind of a hack comparitor
  const locationsMatch = (loc1: quack.ComponentID, loc2?: quack.ComponentID | null | undefined) => {
    if (!loc2) {
      return false;
    }
    if (
      loc1.address === loc2.address &&
      loc1.addressType === loc2.addressType &&
      loc1.type === loc2.type
    ) {
      return true;
    }
    return false;
  };

  const updateDeviceInteractions = () => {
    const preset = confirmPreset;
    if (preset) {
      setUpdatingDeviceInteractions(true);
      let sensor = getSensor();
      let heater = getHeater();
      let fan = getFan();

      if (!fan && !heater) {
        error("At least one controller must be selected");
        setUpdatingDeviceInteractions(false);
        return null;
      }

      let linkedComponents = deviceComponents.get(deviceIndex.toString());
      let conflictingInteractions = interactions.filter(i => {
        let conflicting = false;
        if (linkedComponents) {
          linkedComponents.forEach(comp => {
            if (locationsMatch(comp.location(), i.settings.sink)) {
              conflicting = true;
            }
          });
        }
        return conflicting;
      });
      let deleteConflictingInteractions = conflictingInteractions.map(i => {
        return interactionAPI.removeInteraction(deviceIndex, i.key(), as);
      });
      Promise.all([...deleteConflictingInteractions]).finally(() => {
        if (preset === "shop") {
          if (heater) {
            interactionAPI.addInteraction(
              deviceIndex,
              buildHeatingInteraction(preset, sensor, heater, false, "greater"),
              as
            );
          }
        }
        if (preset === "hydrating") {
          if (fan) {
            interactionAPI
              .addInteraction(
                deviceIndex,
                buildFanInteraction(preset, sensor, fan, "less", "greater"),
                as
              )
              .then(() => {
                setConfirmPreset(null);
                success("Successfully updated device with Hydrating interaction");
                refreshCallback(true);
              })
              .catch(() => {
                error("Error occurred while updating device with hydrating interaction");
              })
              .finally(() => {
                setUpdatingDeviceInteractions(false);
              });
          }
        } else if (preset === "cooldown" || preset === "drying") {
          let multi = pond.MultiInteractionSettings.create();
          //set the heater interaction if a heater was selected
          if (heater) {
            multi.interactions.push(buildHeatingInteraction(preset, sensor, heater, true, "less"));
            //make an update to the heater to put it into auto mode if it is not already
            heater.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_AUTO;
          }
          //set the fan interaction if a fan was selected
          if (fan) {
            if (heater && preset === "drying") {
              //just turn the fan on if there is a heater and the bin was set to drying
              fan.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_ON;
            } else {
              //set an interaction for the fan if either the bin was set to cooldown (with or without heater) or if there is no heater selected in drying
              //if the preset was drying we want to turn the fan on when the temp is high enough, but cooling we want to turn the fan on if the temp is low enough
              multi.interactions.push(
                buildFanInteraction(
                  preset,
                  sensor,
                  fan,
                  preset === "cooldown" ? "less" : "greater",
                  "less"
                )
              );
              fan.settings.defaultOutputState = quack.OutputMode.OUTPUT_MODE_AUTO;
            }
          }
          if (multi.interactions.length > 0) {
            interactionAPI
              .addMultiInteractions(deviceIndex, multi, as)
              .then(resp => {
                updateControllers();
                setConfirmPreset(null);
                success("Interactions added");
                refreshCallback(true);
              })
              .catch(err => {
                error("There was a problem adding interactions to the device");
              })
              .finally(() => {
                setUpdatingDeviceInteractions(false);
              });
          }
        }
      });
    }
  };

  const deviceComponentsDisplay = (device: number) => {
    if (deviceIndex < 0)
      return (
        <Typography
          variant="subtitle2"
          style={{
            margin: theme.spacing(2),
            display: "flex",
            justifyContent: "center"
          }}>
          Select a Device
        </Typography>
      );
    if (loading) return <CircularProgress />;
    if (
      deviceComponents.get(device.toString()) &&
      deviceComponents.get(device.toString())!.length < 1
    ) {
      return (
        <Typography
          variant="subtitle2"
          style={{
            margin: theme.spacing(2),
            display: "flex",
            justifyContent: "center"
          }}>
          No Sensors
        </Typography>
      );
    }
    return (
      <React.Fragment>
        <ListSubheader component="div" disableGutters>
          {"Plenums and Ambient sensors (Choose one)"}
        </ListSubheader>
        <Divider />
        {/* plenum options */}
        {plenums.map((comp, index) => {
          let cIcon = GetComponentIcon(
            comp.settings.type,
            comp.settings.subtype,
            theme.palette.mode
          );
          let haha = deviceComponents.get(device.toString())?.find(c => {
            return c.key() === comp.key();
          });
          if (haha === undefined) return null;
          return (
            <React.Fragment key={index}>
              <ListItem>
                {cIcon && (
                  <ListItemAvatar>
                    <Avatar
                      variant="square"
                      src={cIcon}
                      alt={comp.name()}
                      style={{ width: theme.spacing(3), height: theme.spacing(3) }}
                    />
                  </ListItemAvatar>
                )}
                <ListItemText inset={cIcon === undefined}>{comp.name()}</ListItemText>
                <ListItemSecondaryAction>
                  <Checkbox
                    checked={index === plenumIndex}
                    onClick={() => {
                      setPlenumIndex(index);
                      setAmbientIndex(undefined);
                    }}
                  />
                </ListItemSecondaryAction>
              </ListItem>
              <Divider />
            </React.Fragment>
          );
        })}
        {/* ambient options */}
        {ambients.map((comp, index) => {
          let cIcon = GetComponentIcon(
            comp.settings.type,
            comp.settings.subtype,
            theme.palette.mode
          );
          let haha = deviceComponents.get(device.toString())?.find(c => {
            return c.key() === comp.key();
          });
          if (haha === undefined) return null;
          return (
            <React.Fragment key={index}>
              <ListItem>
                {cIcon && (
                  <ListItemAvatar>
                    <Avatar
                      variant="square"
                      src={cIcon}
                      alt={comp.name()}
                      style={{ width: theme.spacing(3), height: theme.spacing(3) }}
                    />
                  </ListItemAvatar>
                )}
                <ListItemText inset={cIcon === undefined}>{comp.name()}</ListItemText>
                <ListItemSecondaryAction>
                  <Checkbox
                    checked={index === ambientIndex}
                    onClick={() => {
                      setAmbientIndex(index);
                      setPlenumIndex(undefined);
                    }}
                  />
                </ListItemSecondaryAction>
              </ListItem>
              <Divider />
            </React.Fragment>
          );
        })}
        <React.Fragment>
          <ListSubheader component="div" disableGutters>
            {"Fans"}
          </ListSubheader>
          <Divider />
          {fans.map((comp, index) => {
            let cIcon = GetComponentIcon(
              comp.settings.type,
              comp.settings.subtype,
              theme.palette.mode
            );
            let haha = deviceComponents.get(device.toString())?.find(c => {
              return c.key() === comp.key();
            });
            if (haha === undefined) return null;
            return (
              <React.Fragment key={index}>
                <ListItem>
                  {cIcon && (
                    <ListItemAvatar>
                      <Avatar
                        variant="square"
                        src={cIcon}
                        alt={comp.name()}
                        style={{ width: theme.spacing(3), height: theme.spacing(3) }}
                      />
                    </ListItemAvatar>
                  )}
                  <ListItemText inset={cIcon === undefined}>{comp.name()}</ListItemText>
                  <ListItemSecondaryAction>
                    <Checkbox
                      checked={index === fanIndex}
                      onClick={() => {
                        if (heaterIndex === undefined) {
                          setControllerType(pond.ControllerType.CONTROLLER_TYPE_FAN);
                        }
                        setFanIndex(index);
                      }}
                    />
                  </ListItemSecondaryAction>
                </ListItem>
                <Divider />
              </React.Fragment>
            );
          })}
        </React.Fragment>
        {confirmPreset !== "hydrating" && (
          <React.Fragment>
            <ListSubheader component="div" disableGutters>
              {"Heaters"}
            </ListSubheader>
            <Divider />
            {heaters.map((comp, index) => {
              let cIcon = GetComponentIcon(
                comp.settings.type,
                comp.settings.subtype,
                theme.palette.mode
              );
              let haha = deviceComponents.get(device.toString())?.find(c => {
                return c.key() === comp.key();
              });
              if (haha === undefined) return null;
              return (
                <React.Fragment key={index}>
                  <ListItem>
                    {cIcon && (
                      <ListItemAvatar>
                        <Avatar
                          variant="square"
                          src={cIcon}
                          alt={comp.name()}
                          style={{ width: theme.spacing(3), height: theme.spacing(3) }}
                        />
                      </ListItemAvatar>
                    )}
                    <ListItemText inset={cIcon === undefined}>{comp.name()}</ListItemText>
                    <ListItemSecondaryAction>
                      <Checkbox
                        checked={index === heaterIndex}
                        onClick={() => {
                          setControllerType(pond.ControllerType.CONTROLLER_TYPE_HEATER);
                          setHeaterIndex(index);
                        }}
                      />
                    </ListItemSecondaryAction>
                  </ListItem>
                  <Divider />
                </React.Fragment>
              );
            })}
          </React.Fragment>
        )}
      </React.Fragment>
    );
  };

  useEffect(() => {
    let o: Option[] = [];
    devices.forEach(device => {
      o.push({ id: device.id(), label: device.name() });
    });
    setOptions(o);
  }, [devices, setOptions]);

  const startConditioningDialog = () => {
    return (
      <React.Fragment>
        <DialogTitle>Choose Device for {confirmPreset?.toString()} Method</DialogTitle>
        <DialogContent>
          <DialogContentText>
            {confirmPreset?.toString()} control is handled by interactions between multiple components on the
            same device. Please choose which device and components will handle this bin's{" "}
            {confirmPreset?.toString()}.
          </DialogContentText>
          <Autocomplete
            disablePortal
            options={options}
            fullWidth
            getOptionLabel={option => option.label || ""}
            onChange={(_, newValue) => {
              setDeviceIndex(newValue ? newValue.id : -1);
            }}
            renderInput={params => <TextField {...params} variant="outlined" label="Device" />}
          />
          {deviceComponentsDisplay(deviceIndex)}
          {presetOptions.length > 0 && (
            <Autocomplete
              disablePortal
              options={presetOptions}
              fullWidth
              getOptionLabel={option => option.label || ""}
              onChange={(_, newValue) => {
                if (newValue) {
                  let presetId = newValue.id;
                  if (presets && presets[presetId]) {
                    setSelectedPreset(presets[presetId]);
                  }
                }
              }}
              renderInput={params => <TextField {...params} variant="outlined" label="Preset" />}
            />
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setConfirmPreset(null)}>Cancel</Button>
          <Button
            onClick={() => {
              updateDeviceInteractions();
            }}
            disabled={updatingDeviceInteractions}>
            Submit
          </Button>
        </DialogActions>
      </React.Fragment>
    );
  };
  const endConditioningDialog = () => {
    return (
      <React.Fragment>
        <DialogTitle>Entering Storage Mode</DialogTitle>
        <DialogContent>
          {binCables && binCables?.length > 0 && compDevMap && (
            <Box marginBottom={2}>
              <CableTopNodeSummary
                binKey={binKey}
                cables={binCables}
                componentDevMap={compDevMap}
              />
            </Box>
          )}
          <DialogContentText>
            Select device to turn off controllers and end conditioning. Any interactions will be
            left alone. Heaters/Fans will be turned off.
          </DialogContentText>
          <Autocomplete
            disablePortal
            options={options}
            fullWidth
            getOptionLabel={option => option.label || ""}
            onChange={(_, newValue) => {
              setDeviceIndex(newValue ? newValue.id : -1);
            }}
            renderInput={params => <TextField {...params} variant="outlined" label="Device" />}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setConfirmPreset(null)}>Cancel</Button>
          <Button
            onClick={() => {
              setConfirmPreset(null);
              refreshCallback(true);
              if (deviceIndex) {
                updateControllers();
              }
            }}>
            Confirm
          </Button>
        </DialogActions>
      </React.Fragment>
    );
  };

  return (
    <React.Fragment>
      <Dialog
        open={Boolean(confirmPreset)}
        style={{ padding: theme.spacing(1) }}
        fullWidth
        onClose={() => setConfirmPreset(null)}>
        {confirmPreset === "storage" ? endConditioningDialog() : startConditioningDialog()}
      </Dialog>
    </React.Fragment>
  );
}
