import { CheckBox } from "@mui/icons-material";
import { Button, Checkbox, DialogActions, DialogContent, DialogTitle, FormControl, FormControlLabel, FormLabel, InputAdornment, TextField, Typography } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog";
import { Component, Device } from "models";
import { Gate } from "models/Gate";
import moment from "moment";
import { pond, quack } from "protobuf-ts/pond";
import { useGlobalState, useInteractionsAPI, useSnackbar } from "providers";
import React, { useEffect, useState } from "react";
import { getPressureUnit } from "utils";

interface Props {
  open: boolean;
  close: (canceled: boolean) => void;
  gate: Gate;
  compDevice: pond.ComprehensiveDevice;
  densityTemp: number;
}

//map that the temp is the key and the air density is the value
const densityMap = new Map<number, number>([
  [35, 1.15],
  [30, 1.16],
  [25, 1.18],
  [20, 1.2],
  [15, 1.23],
  [10, 1.25],
  [5, 1.27],
  [0, 1.29],
  [-5, 1.32],
  [-10, 1.34],
  [-15, 1.37],
  [-20, 1.39],
  [-25, 1.42]
]);

//this entire component will only be used for manual setting of each device on the gate
//once we have numbers for presets it may never be used again
export default function GateDeviceInteraction(props: Props) {
  const { open, close, gate, compDevice, densityTemp } = props;
  const [{ user, as }] = useGlobalState();
  const [device, setDevice] = useState<Device>(Device.create())
  const [lowDelta, setLowDelta] = useState(0);
  const [highDelta, setHighDelta] = useState(0);
  const [greenComponent, setGreenComponent] = useState<Component>();
  const [redComponent, setRedComponent] = useState<Component>();
  const [pressureComponent, setPressureComponent] = useState<Component>();
  const interactionsAPI = useInteractionsAPI();
  const [adding, setAdding] = useState(false);
  //boolean to determine if a third condition should be put on the interaction for the red light, requires a V2 device running at least fromware version 2.1.9
  const [useRedOffCondition, setUseRedOffCondition] = useState(false)
  //if the pressure is under this value then the red light should be off
  const [redThreshold, setRedThreshold] = useState("0")
  const { openSnack } = useSnackbar();
  const [pressureSource, setPressureSource] = useState<quack.ComponentID>(quack.ComponentID.create())

  useEffect(() => {
    //math to determine what the delta pressures to set will be
    let celciusTemp = densityTemp;
    if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
      celciusTemp = densityTemp * 1.8 + 32;
    }
    let roundedTemp = Math.round(celciusTemp / 5) * 5;
    let airDensity = densityMap.get(roundedTemp);
    //have default values if the temp is outside the map
    if (roundedTemp > 35 && airDensity === undefined) airDensity = 1.1;
    if (roundedTemp < -25 && airDensity === undefined) airDensity = 1.5;

    //calculate the c value for the equation
    if (airDensity !== undefined) {
      let diameterM = gate.ductDiameter() / 1000;
      let pieRadSquare = 3.14 * Math.pow(diameterM / 2, 2);
      let c = 0.98 * pieRadSquare * Math.sqrt(2 * airDensity);
      let qmHigh = gate.upperFlow();
      let qmLow = gate.lowerFlow();

      setLowDelta(Math.round(Math.pow(qmLow / c, 2)));
      setHighDelta(Math.round(Math.pow(qmHigh / c, 2)));
    }

    //addresses of controller LEDs on a V1 device
    let redAddr = "3-1-512";
    let greenAddr = "3-1-1024";
    if (compDevice.device) {
      let dev = Device.create(compDevice.device);
      if (dev.settings.product === pond.DeviceProduct.DEVICE_PRODUCT_MIPCA_V2) {
        redAddr = "3-1-256";
        greenAddr = "3-1-512";
      }
    }

    //need to find if they have LED's in both of the controller positions
    compDevice.components.forEach(comp => {
      let component = Component.any(comp);
      //checks the address for the LED components to get the red and green LED's
      if (component.locationString() === redAddr && component.subType() === 1) {
        setRedComponent(component);
      } else if (component.locationString() === greenAddr && component.subType() === 1) {
        setGreenComponent(component);
      } else if (
          gate.preferences[component.key()] === pond.GateComponentType.GATE_COMPONENT_TYPE_PRESSURE
        ) {
          console.log(component)
          setPressureComponent(component);
          setPressureSource(
              quack.ComponentID.create({
              type: component.type(),
              address: component.settings.address,
              addressType: component.settings.addressType
            })
          )
        }
      
    });

    if(compDevice.device){
      setDevice(Device.create(compDevice.device))
    }
  }, [gate, densityTemp, user, compDevice, gate.preferences]);

  const buttonDisabled = () => {
    if (greenComponent === undefined) return true
    if (redComponent === undefined) return true
    if (adding) return true
    if (useRedOffCondition && isNaN(parseFloat(redThreshold))) return true
    return false
  }

  const createInteractions = async () => {
    //the interactions to be made
    if (
      greenComponent !== undefined &&
      redComponent !== undefined &&
      pressureComponent !== undefined
    ) {
      //get the number of nodes in the pressure components
      let nodes = pressureComponent.numNodes()
      let lightInteractions: pond.InteractionSettings[] = []
      //making variables for the parameters of the interactions
      const redSink = quack.ComponentID.create({
        type: redComponent.type(),
        address: redComponent.settings.address,
        addressType: redComponent.settings.addressType
      })

      const greenSink = quack.ComponentID.create({
        type: greenComponent.type(),
        address: greenComponent.settings.address,
        addressType: greenComponent.settings.addressType
      })

      const lightSchedule = pond.InteractionSchedule.create({
        timezone: moment.tz.guess(),
        weekdays: ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"],
        timeOfDayStart: "00:00",
        timeOfDayEnd: "24:00"
      })
      
      //TOGGLE green ON when pressure within range of upper and lower, this will always be the same regardless
      lightInteractions.push(pond.InteractionSettings.create({
        sink: greenSink,
        source: pressureSource,
        conditions: [
          pond.InteractionCondition.create({
            comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
            value: highDelta,
            measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
          }),
          pond.InteractionCondition.create({
            comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
            value: lowDelta,
            measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
          })
        ],
        nodeOne: nodes,
        subtype: nodes+1,
        schedule: lightSchedule,
        result: pond.InteractionResult.create({
          type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
          value: 1
        })
      }))

      //if they want the red light off when below a certain pressure, it will need 4 seperate SET interactions
      if(useRedOffCondition){
        //need to make sure the redthreshold is in pascals
        let thresholdPascals = 0
        if(!isNaN(parseFloat(redThreshold))){
          if(getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS){
            thresholdPascals = parseFloat(redThreshold)*1000
          }else if (getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER){
            thresholdPascals = parseFloat(redThreshold)*249
          }
        }

        //SET red on when above high
        lightInteractions.push(pond.InteractionSettings.create({
          sink: redSink,
          source: pressureSource,
          conditions: [
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
              value: highDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            })
          ],
        nodeOne: nodes,
        subtype: nodes+1,
          schedule: lightSchedule,
          result: pond.InteractionResult.create({
            type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
            value: 1
          })
        }))
        //SET red off when below high and above low
        lightInteractions.push(pond.InteractionSettings.create({
          sink: redSink,
          source: pressureSource,
          conditions: [
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
              value: highDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            }),
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
              value: lowDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            })
          ],
        nodeOne: nodes,
        subtype: nodes+1,
          schedule: lightSchedule,
          result: pond.InteractionResult.create({
            type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
            value: 0
          })
        }))
        //SET red on when below low and above threshold
        lightInteractions.push(pond.InteractionSettings.create({
          sink: redSink,
          source: pressureSource,
          conditions: [
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
              value: lowDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            }),
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
              value: thresholdPascals,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            }),
          ],
        nodeOne: nodes,
        subtype: nodes+1,
          schedule: lightSchedule,
          result: pond.InteractionResult.create({
            type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
            value: 1
          })
        }))
        //SET red off when below threshold
        lightInteractions.push(pond.InteractionSettings.create({
          sink: redSink,
          source: pressureSource,
          conditions: [
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
              value: thresholdPascals,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            }),
          ],
          nodeOne: nodes,
          subtype: nodes+1,
          schedule: lightSchedule,
          result: pond.InteractionResult.create({
            type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_SET,
            value: 0
          })
        }))
      }else{
        //otherwise use the regular single TOGGLE interaction
        //TOGGLE red OFF when pressure within range of upper and lower
        lightInteractions.push(pond.InteractionSettings.create({
          sink: redSink,
          source: pressureSource,
          conditions: [
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN,
              value: highDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            }),
            pond.InteractionCondition.create({
              comparison: quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN,
              value: lowDelta,
              measurementType: quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE
            })
          ],
          nodeOne: nodes,
          subtype: nodes+1,
          schedule: lightSchedule,
          result: pond.InteractionResult.create({
            type: quack.InteractionResultType.INTERACTION_RESULT_TYPE_TOGGLE,
            value: 0
          })
        }))
      }

      let deviceID = Device.any(compDevice.device).id();
      if (deviceID !== undefined) {
        let multi = pond.MultiInteractionSettings.create({
          interactions: lightInteractions
        });
        interactionsAPI
          .addMultiInteractions(deviceID, multi, as)
          .then(resp => {
            openSnack("Interactions added");
          })
          .catch(err => {
            openSnack("There was a problem adding interactions to the device");
          })
          .finally(() => {
            setAdding(false);
          });
      }
    }
    close(false);
  };

  return (
    <ResponsiveDialog
      open={open}
      onClose={() => {
        close(true);
      }}>
      <DialogTitle>Set Interaction For Light Toggle</DialogTitle>
      <DialogContent>
        {/* Your Delta Pressures, in pascals, will be:
        <Typography>low = {lowDelta}</Typography>
        <Typography>high = {highDelta}</Typography>
        <Typography>{greenComponent ? "" : "Green LED Component not found"}</Typography>
        <Typography>{redComponent ? "" : "Red LED Component not found"}</Typography> */}
        <Typography>
          This will clear existing interactions on the pressure chain and set new ones to toggle the green light on and the red light off 
          when the difference between the pressures falls within this range and vice versa:
        </Typography>
        <br />
        <Typography>Upper Limit: {getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? (highDelta/249.089).toFixed(2) + " iwg" : highDelta/1000 + " kPa"}</Typography>
        <Typography>Lower Limit: {getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? (lowDelta/249.089).toFixed(2) + " iwg" : lowDelta/1000 + " kPa"}</Typography>
        <br />

        <Typography>If you would like to have the interactions set in such a way that if the average pressure falls below a given value set the use off condition and set the value to use</Typography>
        <FormControlLabel
          control={
            <Checkbox
              checked={useRedOffCondition}
              onChange={(e, checked) => {
                setUseRedOffCondition(checked)
              }}
            />
          }
          label={<Typography>Use Off Condition</Typography>}
        />
        <TextField 
          type="number"
          fullWidth
          disabled={!useRedOffCondition}
          value={redThreshold}
          error={isNaN(parseFloat(redThreshold))}
          helperText={isNaN(parseFloat(redThreshold)) ? "Must be a valid number" : ""}
          InputProps={{
            endAdornment: (
              <InputAdornment position="end">
                {getPressureUnit() === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER ? "iwg" : "kPa"}
              </InputAdornment>
            )
          }}
          onChange={e => {
            setRedThreshold(e.target.value)
          }}
        />

      </DialogContent>
      <DialogActions>
        <Button
          variant="contained"
          color="primary"
          onClick={() => {
            //TODO: Once we figure out how to fix the backend to handle rapid addition/removal of interactions
            setAdding(true);
            interactionsAPI.clearInteractions(device.id(), [pressureSource]).then(resp => {
              createInteractions();
            }).catch(err => {
              console.error(err)
            })
          }}
          disabled={buttonDisabled()}
          >
          {adding ? "Adding Interaction" : "Create Interaction"}
        </Button>
      </DialogActions>
    </ResponsiveDialog>
  );
}
