import {
  Avatar,
  Box,
  Card,
  CardContent,
  CardHeader,
  List,
  ListItem,
  ListItemIcon,
  ListItemText,
  ListSubheader,
  Menu,
  MenuItem,
  Theme,
  Tooltip,
  Typography
} from "@mui/material";
import ComponentSettings from "component/ComponentSettings";
import { Component, Device } from "models";
import { GetComponentIcon } from "pbHelpers/ComponentType";
import {
  DeviceAvailabilityMap,
  FindAvailablePositions,
  OffsetAvailabilityMap
} from "pbHelpers/DeviceAvailability";
import { GetDeviceProductIcon, GetDeviceProductLabel } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { useComponentAPI, useDeviceAPI, useGlobalState, useSnackbar } from "providers";

import React, { useState } from "react";
import DeviceSVG, { PortInformation } from "./deviceSVG";
import { makeStyles } from "@mui/styles";

interface Props {
  device: Device;
  //permissions: pond.Permission[];
  components: Component[];
  refreshCallback: () => void;
  scanInProgress?: boolean
}

const useStyles = makeStyles((theme: Theme) => {
  return ({
    card: {
      //   position: "relative",
      //   display: "flex",
      //   height: "100%",
      //   flexDirection: "column",
      //   overflow: "visible"
      minHeight: "200px"
    },
    cardHeader: {
      padding: theme.spacing(1),
      paddingLeft: theme.spacing(2)
    },
    cardContent: {
      display: "flex",
      flexDirection: "column",
      justifyContent: "space-between",
      alignItems: "center",
      padding: theme.spacing(1),
      paddingTop: 0
    },
    avatarIcon: {
      width: theme.spacing(3),
      height: theme.spacing(3)
    }
  })
});

export default function DeviceWizard(props: Props) {
  const { device, components, refreshCallback, scanInProgress } = props;
  const [{as}] = useGlobalState();
  const [componentDialogOpen, setComponentDialogOpen] = useState(false);
  const [availableOffsets, setAvailableOffsets] = useState<OffsetAvailabilityMap>(new Map());
  const [availablePositions, setAvailablePositions] = useState<DeviceAvailabilityMap>(new Map());
  const compAPI = useComponentAPI();
  const [menuAnchor, setMenuAnchor] = useState<null | Element>(null);
  const [portComponents, setPortComponents] = useState<Component[]>([]);
  const [componentToUpdate, setComponentToUpdate] = useState<Component | undefined>();
  const { error, success } = useSnackbar();
  const [ComponentSettingsMode, setComponentSettingsMode] = useState<"add" | "remove" | "update">(
    "add"
  );
  const classes = useStyles();
  const [selectedPort, setSelectedPort] = useState<PortInformation>();
  const deviceAPI = useDeviceAPI()
  const { openSnack } = useSnackbar()
  const [buttonClicked, setButtonClicked] = useState(false);
  //const [addressType, setAddressType] = useState<quack.AddressType>(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY);

  const wizardMenu = () => {
    return (
      <Menu
        id="groupMenu"
        anchorEl={menuAnchor}
        open={Boolean(menuAnchor)}
        onClose={() => setMenuAnchor(null)}
        keepMounted
        disableAutoFocusItem>
        <MenuItem
          key={"add"}
          onClick={() => {
            setComponentSettingsMode("add");
            setComponentToUpdate(undefined);
            setComponentDialogOpen(true);
          }}>
          Add Component
        </MenuItem>
        {/* <MenuItem key={"list_error"} onClick={() => {
          }}>
            List Errors
          </MenuItem> */}
        {selectedPort && selectedPort.autoDetectable && (
          <Tooltip title="Used to Detect Grain Cables">
            <MenuItem
              key={"auto"}
              onClick={() => {
                setAutoDetect();
              }}>
              Auto Detect
            </MenuItem>
          </Tooltip>
        )}
        {selectedPort && selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_I2C && device.featureSupported("detectI2C") && (
          <Tooltip title="Used to tell the Device to scan its I2C port for any plugged in sensors">
            <MenuItem
              key={"scanI2C"}
              disabled={scanInProgress || buttonClicked}
              onClick={() => {
                setButtonClicked(true)
                deviceAPI.detectI2C(device.id())
                .then(resp => {
                  openSnack("Device will scan port on next check-in")
                }).catch(err => {
                  openSnack("Command failed to send to device")
                })
              }}>
              Scan Port
            </MenuItem>
          </Tooltip>
        )}
        {selectedPort && 
          selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY && 
          device.featureSupported("detectOneWire") && 
          selectedPort.autoDetectable && (
          <Tooltip title="Used to tell the Device to scan this port for any plugged in sensors">
            <MenuItem
              key={"scanOneWire"}
              disabled={scanInProgress || buttonClicked}
              onClick={() => {
                setButtonClicked(true)
                console.log(selectedPort)
                deviceAPI.detectOneWire(device.id(), selectedPort.address)
                .then(resp => {
                  openSnack("Device will scan port on next check-in")
                }).catch(err => {
                  openSnack("Command failed to send to device")
                })
              }}>
              Scan Port
            </MenuItem>
          </Tooltip>
        )}
        {portComponents.length > 0 && (
          <List>
            <ListSubheader>Current Components</ListSubheader>
            {portComponents.map(comp => (
              <ListItem
                key={comp.key()}
                onClick={() => {
                  setComponentToUpdate(comp);
                  setComponentSettingsMode("update");
                  setComponentDialogOpen(true);
                }}>
                <ListItemIcon>
                  <Avatar
                    className={classes.avatarIcon}
                    alt={comp.subTypeName() + "Icon"}
                    src={GetComponentIcon(comp.type(), comp.subType())}
                  />
                </ListItemIcon>
                <ListItemText>{comp.name()}</ListItemText>
              </ListItem>
            ))}
          </List>
        )}
      </Menu>
    );
  };

  /**
   * sets an autodetect component on the port that was selected
   * TODO: when the auto detect gets expanded out of being a grain cable component this will have to be re-factored to use the new component type since it is hard coded
   */
  const setAutoDetect = () => {
    if (selectedPort) {
      let componentSettings = pond.ComponentSettings.create();
      componentSettings.type = quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE; // use the grain cable component
      componentSettings.subtype = quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG; //use the port diagnostic subtype
      componentSettings.addressType = quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY;
      componentSettings.address = selectedPort.address; // the address of the selected port
      componentSettings.measurementPeriodMs = 600000; //set the measurement period to 10 mins
      componentSettings.reportPeriodMs = 600000;
      componentSettings.name = "Port " + selectedPort.label + " Auto Detect";
      compAPI
        .add(device.id(), componentSettings, as)
        .then(resp => {
          success("Added Auto Detect Component to Port");
        })
        .catch(err => {
          error("Failed to Add Auto Detect Component to Port");
        })
        .finally(() => {
          refreshCallback();
        });
    }
  };

  /**
   * function that restricts the pin availabilty in the availability map to the pin of the port that was selected
   * @param port port that was selected
   * @param availability availability map of the device
   * @returns modified availability map
   */
  const adjustAvailablePositions = (port: PortInformation, availability: DeviceAvailabilityMap) => {
    let currentAvailability = availability;
    switch (port.addressType) {
      case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
        currentAvailability.set(port.addressType, [{ address: port.address, label: port.label }]);
        //since we now have a component type that can be pins or i2c need to remove i2c options if they selected a pin port
        currentAvailability.set(quack.AddressType.ADDRESS_TYPE_I2C, new Map());
        break;
      case quack.AddressType.ADDRESS_TYPE_I2C:
        //since we now have a component type that can be pins or i2c need to remove pin options if they selected the i2c port
        currentAvailability.set(quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, []);
        break;
    }
    return currentAvailability;
  };

  const findPortComponents = (components: Component[], port: PortInformation) => {
    let pc: Component[] = [];
    switch (port.addressType) {
      case quack.AddressType.ADDRESS_TYPE_I2C:
        components.forEach(comp => {
          if (comp.settings.addressType === quack.AddressType.ADDRESS_TYPE_I2C) {
            pc.push(comp);
          }
        });
        break;
      case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
        components.forEach(comp => {
          if (comp.settings.address === port.address) {
            pc.push(comp);
          }
        });
    }
    return pc;
  };

  const setupCard = () => {
    const deviceIcon = GetDeviceProductIcon(device)
    return (
      <Card className={classes.card} raised>
        <CardHeader
          avatar={
            <Avatar
              variant="square"
              src={deviceIcon}
              className={classes.avatarIcon}
              alt={"devIcon"}
            />
          }
          title={GetDeviceProductLabel(device.settings.product) + " - ID:" + device.id()}
          className={classes.cardHeader}
        />
        <CardContent style={{ paddingTop: 0 }}>
          <Typography variant="caption">
            Select a port on the device below to modify components
          </Typography>
          <Box paddingLeft={"15%"} paddingRight={"15%"} paddingTop={"20px"}>
            <DeviceSVG
              device={device}
              openMenu={(e, port) => {
                let available = FindAvailablePositions(components, device.settings.product);
                setAvailableOffsets(available.offsetAvailability);
                setAvailablePositions(adjustAvailablePositions(port, available.availability));
                //setAddressType(port.addressType);
                setSelectedPort(port);
                setPortComponents(findPortComponents(components, port));
                setMenuAnchor(e.currentTarget);
              }}
            />
          </Box>
        </CardContent>
      </Card>
    );
  };

  return (
    <React.Fragment>
      {setupCard()}
      <ComponentSettings
        mode={ComponentSettingsMode}
        component={componentToUpdate}
        device={device}
        isDialogOpen={componentDialogOpen}
        closeDialogCallback={() => setComponentDialogOpen(false)}
        availablePositions={availablePositions}
        availableOffsets={availableOffsets}
        refreshCallback={refreshCallback}
        canEdit
        addressTypeRestriction={selectedPort && selectedPort.addressType}
      />
      {wizardMenu()}
    </React.Fragment>
  );
}
