import {
  Accordion,
  AccordionDetails,
  AccordionSummary,
  Button,
  DialogActions,
  DialogContent,
  DialogTitle,
  Grid2 as Grid,
  List,
  ListItem,
  MenuItem,
  TextField
} from "@mui/material";
import { ExpandMore } from "@mui/icons-material";
import { Component } from "models";
import { pond, quack } from "protobuf-ts/pond";
import React, { useCallback, useState, useEffect } from "react";
import ResponsiveDialog from "./ResponsiveDialog";

interface Props {
  open: boolean;
  existingMutation?: pond.LinearMutation;
  componentsByDevice: Map<string, Component[]>; //string being device id and component array of that devices components
  onClose: () => void;
  onSubmit: (mutation: pond.LinearMutation) => void;
}

export default function LinearMutationBuilder(props: Props) {
  const { open, onClose, componentsByDevice, onSubmit, existingMutation } = props;
  const [coefficientSets, setCoefficientSets] = useState<pond.ComponentCoefficients[]>([]);
  const [devOptions, setDevOptions] = useState<string[]>([]);
  const [compOptions, setCompOptions] = useState<Component[][]>([]);
  const [selectedDevices, setSelectedDevices] = useState<string[]>([]);
  const [selectedComponents, setSelectedComponents] = useState<string[]>([]);
  const [selectedMutation, setSelectedMutation] = useState(pond.Mutator.MUTATOR_NONE);
  const [mutationConstant, setMutationConstant] = useState(0);
  const [mutationName, setMutationName] = useState("");

  useEffect(() => {
    setDevOptions(Array.from(componentsByDevice.keys()));
    setMutationName("");
    setSelectedMutation(pond.Mutator.MUTATOR_NONE);
    setCoefficientSets([]);
    setMutationConstant(0);
    setSelectedComponents([]);
    setSelectedDevices([]);
    setCompOptions([]);
    if (existingMutation) {
      setMutationName(existingMutation.mutationName);
      setSelectedMutation(existingMutation.mutation);
      setCoefficientSets(existingMutation.componentCoefficients);
      setMutationConstant(existingMutation.constant);
      let co: Component[][] = [];
      let sc: string[] = [];
      let sd: string[] = [];
      existingMutation.componentCoefficients.forEach(compCo => {
        let splitString = compCo.componentKey.split(":", 2);
        if (splitString.length > 1) {
          sd.push(splitString[0]);
          sc.push(splitString[1]);
          let c = componentsByDevice.get(splitString[0]);
          if (c) {
            co.push(c);
          }
        }
      });
      setSelectedComponents(sc);
      setSelectedDevices(sd);
      setCompOptions(co);
    }
  }, [componentsByDevice, existingMutation]);

  const addNewSet = () => {
    let cs = coefficientSets;
    let sd = selectedDevices;
    let co = compOptions;
    let sc = selectedComponents;

    cs.push(pond.ComponentCoefficients.create());
    sd.push("");
    co.push([]);
    sc.push("");

    setCoefficientSets([...cs]);
    setSelectedDevices([...sd]);
    setCompOptions([...co]);
    setSelectedComponents([...sc]);
  };

  const setOptionsFor = (pos: number, dev: string) => {
    let co = compOptions;
    let components = componentsByDevice.get(dev);
    if (components) {
      co[pos] = components;
    }
    setCompOptions([...co]);
  };

  const nodeCoefficientDisplay = (
    deviceID: string,
    componentKey: string,
    set: pond.ComponentCoefficients
  ) => {
    set.componentKey = deviceID + ":" + componentKey; //note: changing the set that is passed in is changing the state variable
    let componentList = componentsByDevice.get(deviceID);
    let component: Component = Component.create();
    if (componentList) {
      componentList.forEach(comp => {
        if (comp.key() === componentKey) {
          component = comp;
        }
      });
    }
    let nodeList: JSX.Element[] = [];
    component.lastMeasurement.forEach((um, k) => {
      if (um.values[0]) {
        if (set.measurementCoefficients[k] === undefined) {
          set.measurementCoefficients[k] = pond.MeasurementCoefficients.create({
            coefficients: [],
            measurementType: um.type
          });
        }
        for (let i = 0; i < um.values[0].values.length; i++) {
          //default to 0 if it isn't set
          if (!set.measurementCoefficients[k].coefficients[i]) {
            set.measurementCoefficients[k].coefficients[i] = 0;
          }
          nodeList.push(
            <ListItem key={componentKey + "node" + i + um.type}>
              <Grid container direction="row" alignContent="center" alignItems="center">
                <Grid size={{ xs: 12 }}>
                  Node {i + 1} ({quack.MeasurementType[um.type]})
                </Grid>
                <Grid size={{ xs: 12 }}>
                  <TextField
                    fullWidth
                    value={set.measurementCoefficients[k].coefficients[i]}
                    type="number"
                    onChange={e => {
                      set.measurementCoefficients[k].coefficients[i] = +e.target.value;
                      setCoefficientSets([...coefficientSets]);
                    }}
                  />
                </Grid>
              </Grid>
            </ListItem>
          );
        }
      }
    });

    return <List>{nodeList}</List>;
  };

  const displaySets = useCallback(() => {
    let setDisplay: JSX.Element[] = [];
    coefficientSets.forEach((set, i) => {
      setDisplay.push(
        <ListItem key={i} style={{ width: 450 }}>
          <Grid container direction="column">
            <Grid
              container
              direction="row"
              alignContent="center"
              alignItems="center"
              justifyContent="space-between">
              <Grid size={{ xs: 5 }}>
                <TextField
                  fullWidth
                  label={"Device"}
                  select
                  value={selectedDevices[i]}
                  onChange={event => {
                    let sd = selectedDevices;
                    sd[i] = event.target.value as string;
                    setSelectedDevices([...sd]);
                    setOptionsFor(i, event.target.value as string);
                  }}>
                  {devOptions.map(dev => (
                    <MenuItem key={dev} value={dev}>
                      {dev}
                    </MenuItem>
                  ))}
                </TextField>
              </Grid>
              <Grid size={{ xs: 5 }}>
                <TextField
                  fullWidth
                  label={"Component"}
                  select
                  value={selectedComponents[i]}
                  onChange={event => {
                    let sc = selectedComponents;
                    sc[i] = event.target.value as string;
                    setSelectedComponents([...sc]);
                    set.measurementCoefficients = [];
                  }}>
                  {compOptions[i] &&
                    compOptions[i].map(comp => (
                      <MenuItem key={comp.key()} value={comp.key()}>
                        {comp.name()}
                      </MenuItem>
                    ))}
                </TextField>
              </Grid>
            </Grid>
            {selectedComponents[i] !== "" && (
              <Grid>
                <Accordion>
                  <AccordionSummary expandIcon={<ExpandMore />}>Node Coefficients</AccordionSummary>
                  <AccordionDetails>
                    {nodeCoefficientDisplay(selectedDevices[i], selectedComponents[i], set)}
                  </AccordionDetails>
                </Accordion>
              </Grid>
            )}
          </Grid>
        </ListItem>
      );
    });
    return <List>{setDisplay}</List>;
  }, [coefficientSets, selectedDevices, compOptions, devOptions, selectedComponents]); // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <ResponsiveDialog open={open} onClose={() => onClose()}>
      <DialogTitle>Build New Mutation</DialogTitle>
      <DialogContent>
        <TextField
          style={{ marginBottom: 10 }}
          value={mutationName}
          fullWidth
          label={"Mutation Name"}
          onChange={e => {
            setMutationName(e.target.value as string);
          }}
        />
        <TextField
          style={{ marginBottom: 10 }}
          value={selectedMutation}
          fullWidth
          select
          label={"Mutation Type"}
          onChange={e => {
            setSelectedMutation(+e.target.value);
          }}>
          <MenuItem key={pond.Mutator.MUTATOR_NONE} value={pond.Mutator.MUTATOR_NONE}>
            Select Type of Calculated Measurement
          </MenuItem>
          <MenuItem key={pond.Mutator.MUTATOR_FUEL_LEVEL} value={pond.Mutator.MUTATOR_FUEL_LEVEL}>
            Fuel Level
          </MenuItem>
        </TextField>
        <TextField
          style={{ marginBottom: 10 }}
          value={mutationConstant}
          fullWidth
          type="number"
          label={"Mutation Constant"}
          onChange={e => {
            setMutationConstant(+e.target.value);
          }}
        />
        {displaySets()}
        <Button variant="contained" color="primary" onClick={addNewSet}>
          Add Component
        </Button>
      </DialogContent>
      <DialogActions>
        {existingMutation ? (
          <React.Fragment>
            <Button
              color="primary"
              onClick={() => {
                existingMutation.mutationName = mutationName;
                existingMutation.constant = mutationConstant;
                existingMutation.mutation = selectedMutation;
                existingMutation.componentCoefficients = coefficientSets;
                onClose();
              }}>
              Finish
            </Button>
          </React.Fragment>
        ) : (
          <React.Fragment>
            <Button color="primary" onClick={onClose}>
              Close
            </Button>
            <Button
              color="primary"
              disabled={selectedMutation === pond.Mutator.MUTATOR_NONE}
              onClick={() => {
                let newMutation = pond.LinearMutation.create();
                newMutation.mutation = selectedMutation;
                newMutation.componentCoefficients = coefficientSets;
                newMutation.constant = mutationConstant;
                newMutation.mutationName = mutationName;
                onSubmit(newMutation);
                onClose();
              }}>
              Add Mutation
            </Button>
          </React.Fragment>
        )}
      </DialogActions>
    </ResponsiveDialog>
  );
}
