import {
  Avatar,
  Box,
  Card,
  CardHeader,
  Checkbox,
  CircularProgress,
  FormControlLabel,
  FormGroup,
  Grid2 as Grid,
  Theme,
  Typography
} from "@mui/material";
import { makeStyles } from "@mui/styles";
import AreaGraph, { AreaData } from "charts/measurementCharts/AreaGraph";
import MultiLineGraph, { LineData, Point } from "charts/measurementCharts/MultiLineGraph";
import UnitMeasurementSummary from "component/UnitMeasurementSummary";
import { useThemeType } from "hooks";
import { cloneDeep } from "lodash";
import { Component } from "models";
import { UnitMeasurement } from "models/UnitMeasurement";
import moment, { Moment } from "moment";
import { GetComponentIcon } from "pbHelpers/ComponentType";
import { describeMeasurement, MeasurementDescriber } from "pbHelpers/MeasurementDescriber";
import { useGlobalState } from "providers";
import React, { useState } from "react";
import { avg } from "utils";

interface Props {
  component: Component;
  measurements: UnitMeasurement[];
  isLoading: boolean;
  xDomain: string[] | number[];
  zoomIn: (domain: string[] | number[]) => void;
}

const useStyles = makeStyles((theme: Theme) => {
  return ({
    card: {
      position: "relative",
      display: "flex",
      height: "100%",
      flexDirection: "column",
      overflow: "visible"
    },
    cardHeader: {
      padding: theme.spacing(1),
      paddingLeft: theme.spacing(2),
      marginRight: 10
    },
    avatarIcon: {
      width: 33,
      height: 33
    }
  })
});

export default function BinComponentGraph(props: Props) {
  const { component, measurements, isLoading, xDomain, zoomIn } = props;
  const [selectedNodes, setSelectedNodes] = useState<string[]>(["all"]);
  const themeType = useThemeType();
  const classes = useStyles();
  const [{ user }] = useGlobalState();

  const handleGrainCableControlsChange = (name: string) => (event: any) => {
    let nodes = selectedNodes;
    if (event.target.checked) {
      if (name === "all" || name === "grain" || name === "air") {
        nodes = [name];
      } else {
        nodes = nodes.filter(item => {
          let str = item.toString();
          return str !== "all" && str !== "air" && str !== "grain";
        });
        nodes.push(name);
      }
    } else {
      if (nodes.includes(name)) {
        nodes = nodes.filter(item => item.toString() !== name.toString());
        if (nodes.length < 1 && name !== "all") {
          nodes = ["all"];
        }
      }
    }
    setSelectedNodes(nodes);
  };

  const cableControls = (numNodes: number) => {
    if (numNodes <= 1) {
      return null;
    }

    let grainCableControls = [];
    // let split = isBinSplit(graphFilters);
    // let splitAt = binSplitAt(graphFilters);

    for (var i = numNodes; i > 0; i--) {
      if (i === numNodes) {
        grainCableControls.push(
          <Grid key="group-selectors">
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedNodes.includes("all") ? true : false}
                  onChange={handleGrainCableControlsChange("all")}
                  value={"all"}
                  disabled={selectedNodes.includes("all") ? true : false}
                />
              }
              label={"All"}
            />
          </Grid>
        );
      }

      let nodeLabel = "Node " + i.toString();
      if (i === 1) {
        nodeLabel = "Bottom";
      } else if (i === numNodes) {
        nodeLabel = "Top";
      }
      grainCableControls.push(
        <Grid key={"node-" + (i - 1).toString()}>
          <FormControlLabel
            //className={split && i <= splitAt ? classes.grainNode : classes.airNode}
            control={
              <Checkbox
                checked={selectedNodes.includes((i - 1).toString()) ? true : false}
                onChange={handleGrainCableControlsChange((i - 1).toString())}
                value={(i - 1).toString()}
              />
            }
            label={<Typography variant="body2">{nodeLabel}</Typography>}
          />
        </Grid>
      );
    }
    return numNodes > 0 ? (
      <FormGroup>
        <Grid container direction="row">
          {grainCableControls}
        </Grid>
      </FormGroup>
    ) : null;
  };

  const graphHeader = () => {
    const componentIcon = GetComponentIcon(
      component.settings.type,
      component.settings.subtype,
      themeType
    );
    let nodes = 1;
    let lastMeasurement = cloneDeep(component.status.lastGoodMeasurement);
    if (
      lastMeasurement[0] &&
      lastMeasurement[0].values[0] &&
      lastMeasurement[0].values[0].values.length > 1
    ) {
      nodes = lastMeasurement[0].values[0].values.length;
    }

    return (
      <CardHeader
        avatar={
          <Avatar
            variant="square"
            src={componentIcon}
            className={classes.avatarIcon}
            alt={component.name()}
          />
        }
        title={
          <React.Fragment>
            <Typography style={{ fontSize: 25, fontWeight: 650 }}>{component.name()}</Typography>
            {nodes > 1 && (
              <Grid container direction="row">
                {cableControls(nodes)}
              </Grid>
            )}
          </React.Fragment>
        }
        subheader={
          <UnitMeasurementSummary
            component={component}
            reading={UnitMeasurement.convertLastMeasurement(
              lastMeasurement.map(m => UnitMeasurement.create(m, user))
            )}
          />
        }
      />
    );
  };

  const areaGraph = (
    unitMeasurement: UnitMeasurement,
    describer: MeasurementDescriber,
    averages?: number
  ) => {
    if (unitMeasurement.timestamps.length > 0) {
      let firstTime: Moment | undefined;
      let lastTime: Moment | undefined;
      //build line data to pass to graph
      let areaData: AreaData[] = [];
      let avgData: AreaData[] = [];
      let maxVals: number[] = [];
      let minVals: number[] = [];

      unitMeasurement.values.forEach((val, j) => {
        let currentMax = Math.min(...val.values);
        let currentMin = Math.max(...val.values);
        let dataPoint: AreaData = {
          timestamp: moment(unitMeasurement.timestamps[j]).valueOf(),
          value: [currentMax, currentMin]
        };
        if (averages) {
          if (minVals.length === 0) {
            firstTime = moment(unitMeasurement.timestamps[j]);
          }
          maxVals.push(currentMax);
          minVals.push(currentMin);
          if (minVals.length >= averages) {
            lastTime = moment(unitMeasurement.timestamps[j]);
          }
        }
        if (firstTime && lastTime) {
          avgData.push({
            timestamp:
              Math.abs(firstTime.diff(lastTime)) / 2 +
              Math.min(firstTime.valueOf(), lastTime.valueOf()),
            value: [avg(maxVals), avg(minVals)]
          });
          firstTime = undefined;
          lastTime = undefined;
          maxVals = [];
          minVals = [];
        }
        areaData.push(dataPoint);
      });
      return (
        <AreaGraph
          key={unitMeasurement.type}
          customHeight={350}
          data={areaData}
          //averagedData={showAveraged ? avgData : []}
          describer={describer}
          tooltip
          newXDomain={xDomain}
          multiGraphZoom={zoomIn}
          multiGraphZoomOut
        />
      );
    }
  };

  const selectedNodeVals = (vals: number[]) => {
    let points: Point[] = [];
    vals.forEach((val, i) => {
      if (selectedNodes.includes("all") || selectedNodes.includes(i.toString())) {
        points.push({ value: val, node: i });
      }
    });
    return points;
  };

  const lineGraph = (
    unitMeasurement: UnitMeasurement,
    describer: MeasurementDescriber,
    averages?: number
  ) => {
    if (unitMeasurement.values.length > 0) {
      let firstTime: Moment | undefined;
      let lastTime: Moment | undefined;
      //build line data to pass to graph
      let lineData: LineData[] = [];
      let averagedData: LineData[] = [];
      let valMap: Map<number, number[]> = new Map<number, number[]>();

      unitMeasurement.values.forEach((vals, i) => {
        let avgVals: Point[] = [];
        let newLineData: LineData = {
          timestamp: moment(unitMeasurement.timestamps[i]).valueOf(),
          points: selectedNodeVals(vals.values)
        };
        lineData.push(newLineData);
        if (averages) {
          vals.values.forEach((val, k) => {
            let entry = valMap.get(k);
            if (entry) {
              entry.push(val);
              if (entry.length >= averages) {
                lastTime = moment(unitMeasurement.timestamps[i]);
                avgVals.push({
                  node: k,
                  value: avg(entry)
                });
              }
            } else {
              valMap.set(k, [val]);
              firstTime = moment(unitMeasurement.timestamps[i]);
            }
          });
        }
        if (firstTime && lastTime) {
          averagedData.push({
            points: avgVals,
            timestamp:
              Math.abs(firstTime.diff(lastTime)) / 2 +
              Math.min(firstTime.valueOf(), lastTime.valueOf())
          });
          firstTime = undefined;
          lastTime = undefined;
          valMap = new Map<number, number[]>();
        }
      });
      return (
        <MultiLineGraph
          customHeight={300}
          key={unitMeasurement.type}
          lineData={lineData}
          //averagedData={showAveraged ? averagedData : []}
          numLines={unitMeasurement.values.length > 1 ? unitMeasurement.values[0].values.length : 0}
          describer={describer}
          tooltip
          newXDomain={xDomain}
          multiGraphZoom={zoomIn}
          multiGraphZoomOut
        />
      );
    }
  };

  if (isLoading) {
    return (
      <Card raised key={component.key()} style={{ padding: 10, marginBottom: 15 }}>
        <Box display="flex" justifyContent="center" alignItems="center" style={{ height: 300 }}>
          <CircularProgress size={150} thickness={1.5} />
        </Box>
      </Card>
    );
  } else {
    return (
      <Card raised key={component.key()} style={{ padding: 10, marginBottom: 15 }}>
        {graphHeader()}
        {selectedNodes.includes("all") && component.numNodes() > 1
          ? measurements?.map(um =>
              areaGraph(
                um,
                describeMeasurement(um.type, component.type(), component.subType()),
                component.settings.smoothingAverages
              )
            )
          : measurements?.map(um =>
              lineGraph(
                um,
                describeMeasurement(um.type, component.type(), component.subType()),
                component.settings.smoothingAverages
              )
            )}
      </Card>
    );
  }
}
