import React, { useEffect, useState } from "react";
import { extension, GraphFilters, showMultilineCable } from "pbHelpers/ComponentType";
import { Component, Interaction } from "models";
import { pond } from "protobuf-ts/pond";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { quack } from "protobuf-ts/quack";
import { GraphType } from "common/Graph";
import AreaGraph, { AreaData } from "./measurementCharts/AreaGraph";
import moment, { Moment } from "moment";
import MultiLineGraph, { LineData } from "./measurementCharts/MultiLineGraph";
import { ReferenceArea, ReferenceLine } from "recharts";
import { UnitMeasurement } from "models/UnitMeasurement";
import {
  Box,
  Button,
  Card,
  CardContent,
  Checkbox,
  FormControlLabel,
  Grid2 as Grid,
  IconButton,
  Typography
} from "@mui/material";
import GroupSettingsIcon from "@mui/icons-material/Settings";
import { green } from "@mui/material/colors";
import GraphSettings from "./GraphSettings";
import TimeBar from "common/time/TimeBar";
import { useMobile } from "hooks";
import { useGlobalState } from "providers";

interface Props {
  startDate: Moment;
  endDate: Moment;
  component: Component;
  interactions: Interaction[];
  filters?: GraphFilters;
  unitMeasurements: UnitMeasurement[];
  sampleLimit?: number;
  updateDateRange: (start: Moment, end: Moment, live: boolean) => void;
  showOriginal: boolean;
  showAveraged: boolean;
}

interface Range {
  min: number | string;
  max: number | string;
}

export default function MeasurementsChart(props: Props) {
  const {
    component,
    interactions,
    filters,
    unitMeasurements,
    updateDateRange,
    startDate,
    endDate,
    showOriginal,
    showAveraged,
    sampleLimit
  } = props;
  const [currentMutator, setCurrentMutator] = useState<pond.Mutator | undefined>();
  const [graphSettingsDialog, setGraphSettingsDialog] = useState(false);
  const [graphRanges, setGraphRanges] = useState<Range[]>([]);
  const [rangeIndex, setRangeIndex] = useState(0);
  const [currentMin, setCurrentMin] = useState<number | string>("dataMin");
  const [currentMax, setCurrentMax] = useState<number | string>("dataMax");
  const [newChart, setNewChart] = useState(false);
  const [xDomain, setXDomain] = useState<string[] | number[]>(["dataMin", "dataMax"]);
  const [{ showErrors }, dispatch] = useGlobalState();
  const isMobile = useMobile();
  // const [zoomStart, setZoomStart] = useState<Moment>();
  // const [zoomEnd, setZoomEnd] = useState<Moment>();
  // const [zoomed, setZoomed] = useState<boolean>(false);

  // const zoomOut = () => {
  //   setXDomain(["dataMin", "dataMax"]);
  //   setZoomed(false);
  // };

  const zoomIn = (domain: number[] | string[]) => {
    let start;
    let end;
    if (domain[0]) {
      start = moment(domain[0]);
    }
    if (domain[1]) {
      //add 1 second to the end time to prevent the last measurement from being cut off due to the milliseconds on the time in the database
      end = moment(domain[1]).add(1, "second");
    }
    setXDomain(domain);
    //setZoomed(true);
    //console.log(sampleLimit);
    //console.log(unitMeasurements);
    //check if the sample limit was set and we have unit measurements
    if (sampleLimit && unitMeasurements.length > 0) {
      //if the number of measurements in the unit measurement is less than half of the sample limit it means there are no more measurements to get,
      //so if it is bigger then will need to hit the backend to get a new set of measurements
      if (unitMeasurements[0].values.length >= sampleLimit / 2) {
        if (start && end) {
          updateDateRange(start, end, false);
          setXDomain(["dataMin", "dataMax"]);
        }
      }
    }
  };

  useEffect(() => {
    let ranges: Range[] = [];
    for (let i = 0; i < unitMeasurements.length; i++) {
      ranges.push({
        min: "dataMax",
        max: "dataMin"
      });
    }
    setGraphRanges(ranges);
  }, [unitMeasurements]);

  //builds the reference lines to show on the graph
  const getInteractionLines = (measurementType: quack.MeasurementType) => {
    let selectedInteractions: JSX.Element[] = [];
    interactions.forEach(interaction => {
      if (filters?.selectedInteractions?.includes(interaction.key())) {
        interaction.settings.conditions.forEach((condition, j) => {
          if (condition.measurementType === measurementType) {
            let describer = describeMeasurement(condition.measurementType);
            selectedInteractions.push(
              <ReferenceLine
                key={interaction.key() + ":condition" + j}
                y={describer.toDisplay(condition.value)}
                ifOverflow="extendDomain"
                stroke="red"
                strokeDasharray={"5 2"}
              />
            );
          }
        });
      }
    });
    return selectedInteractions;
  };

  //builds the overlays to show on the graph
  const getOverlays = (measurementType: quack.MeasurementType) => {
    let overlays: JSX.Element[] = [];
    component.settings.overlays.forEach((overlay, i) => {
      if (
        overlay.measurementType === measurementType &&
        filters?.selectedOverlays?.includes(i.toString())
      ) {
        overlays.push(
          <ReferenceArea
            key={i}
            y1={overlay.max}
            y2={overlay.min}
            stroke={overlay.colour}
            strokeWidth={2}
            opacity={0.75}
            fill={overlay.colour}
            ifOverflow="extendDomain">
            {/* could possibly show overlay message here */}
          </ReferenceArea>
        );
      }
    });
    return overlays;
  };

  const buildGraphs = (
    unitMeasurements: UnitMeasurement[],
    component: Component,
    filters?: GraphFilters
  ) => {
    let graphs: JSX.Element[] = [];
    let hasData = false;
    let noData: JSX.Element = (
      <Box width="100%" height={350} paddingTop={15}>
        <Typography variant="h6" color="textPrimary" align="center">
          Not enough data to display
        </Typography>
        <Typography variant="subtitle2" color="textSecondary" align="center">
          {"(Queried between " +
            moment(startDate).calendar() +
            " and " +
            moment(endDate).calendar() +
            ")"}
        </Typography>
      </Box>
    );
    let elem: JSX.Element;
    let graph: JSX.Element = noData;
    let numBasicGraphs = unitMeasurements.length - 1;
    let ext = extension(component.type(), component.subType());

    unitMeasurements.forEach((measurements, i) => {
      let m = pond.UnitMeasurementsForComponent.fromObject(measurements);
      let describer = describeMeasurement(
        m.type,
        component.settings.type,
        component.settings.subtype
      );
      let min: string | number = "dataMin";
      let max: string | number = "dataMax";
      if (graphRanges[i]) {
        min = graphRanges[i].min;
        max = graphRanges[i].max;
      }
      if (showMultilineCable(component.type(), filters)) {
        let lineChartData = ext.lineChartData(m, component.settings.smoothingAverages, filters);
        let lineData: LineData[] = lineChartData.lines;
        let averagedData: LineData[] = lineChartData.average;
        if (lineData.length > 0) {
          hasData = true;
          graph = (
            <MultiLineGraph
              yMin={min}
              yMax={max}
              customHeight={isMobile ? 200 : 350}
              lineData={showOriginal ? lineData : []}
              averagedData={showAveraged ? averagedData : []}
              numLines={m.values.length > 1 ? m.values[0].values.length : 0}
              describer={describer}
              interactionLines={getInteractionLines(m.type)}
              overlays={getOverlays(m.type)}
              tooltip
              newXDomain={xDomain}
              multiGraphZoom={domain => {
                zoomIn(domain);
              }}
              multiGraphZoomOut
            />
          );
        }
      } else {
        switch (describer.graph()) {
          case GraphType.AREA:
            let chartData = ext.areaChartData(m, component.settings.smoothingAverages, filters);
            let areaData: AreaData[] = chartData.area;
            let avgData: AreaData[] = chartData.average;
            if (areaData.length > 0) {
              hasData = true;
              graph = (
                <AreaGraph
                  customHeight={isMobile ? 200 : 350}
                  yMin={min}
                  yMax={max}
                  data={showOriginal ? areaData : []}
                  averagedData={showAveraged ? avgData : []}
                  describer={describer}
                  interactionLines={getInteractionLines(m.type)}
                  overlays={getOverlays(m.type)}
                  tooltip
                  newXDomain={xDomain}
                  multiGraphZoom={domain => {
                    zoomIn(domain);
                  }}
                  multiGraphZoomOut
                />
              );
            }
            break;
          //for the moment not going to worry about these graphs and possibly just make them as needed
          case GraphType.SCATTER:
            break;
          case GraphType.RADAR:
            break;
          case GraphType.BAR:
            break;
          default:
            //line graph
            let lineChartData = ext.lineChartData(m, component.settings.smoothingAverages, filters);
            let lineData: LineData[] = lineChartData.lines;
            let averagedData: LineData[] = lineChartData.average;
            if (lineData.length > 0) {
              hasData = true;
              graph = (
                <MultiLineGraph
                  yMin={min}
                  yMax={max}
                  customHeight={isMobile ? 200 : 350}
                  lineData={showOriginal ? lineData : []}
                  averagedData={showAveraged ? averagedData : []}
                  numLines={m.values.length > 1 ? m.values[0].values.length : 0}
                  describer={describer}
                  interactionLines={getInteractionLines(m.type)}
                  overlays={getOverlays(m.type)}
                  tooltip
                  newXDomain={xDomain}
                  multiGraphZoom={domain => {
                    zoomIn(domain);
                  }}
                  multiGraphZoomOut
                />
              );
            }
            break;
        }
      }

      elem = (
        <React.Fragment key={i}>
          <Card raised style={{ width: "100%", margin: 5 }}>
            <CardContent>
              <Box display="flex">
                <Box width="97%">{graph}</Box>
                <Box justifyContent="center">
                  <IconButton
                    onClick={() => {
                      if (i + 1 > numBasicGraphs) {
                        setCurrentMutator(component.settings.defaultMutations[i - numBasicGraphs]);
                      } else {
                        setCurrentMutator(undefined);
                      }
                      setGraphSettingsDialog(true);
                      setRangeIndex(i);
                      setNewChart(false);
                      if (graphRanges[i]) {
                        setCurrentMax(max);
                        setCurrentMin(min);
                      }
                    }}>
                    <GroupSettingsIcon />
                  </IconButton>
                </Box>
              </Box>
            </CardContent>
          </Card>
        </React.Fragment>
      );
      graphs.push(elem);
    });
    return hasData ? graphs : noData;
  };

  return (
    <React.Fragment>
      {!isMobile ? (
        <Grid container direction="row" justifyContent="space-between" style={{ marginBottom: 10 }}>
          <Grid>
            <TimeBar updateDateRange={updateDateRange} startDate={startDate} endDate={endDate} />
          </Grid>
          <Grid>
            <FormControlLabel
              label="Show Errors"
              control={
                <Checkbox
                  checked={showErrors}
                  onChange={() => {
                    //setShowErrors(!showErrors);
                    dispatch({ key: "showErrors", value: !showErrors });
                  }}
                />
              }
            />
          </Grid>
          {/* <Grid item>
            {zoomed && (
              <Button
                variant="outlined"
                onClick={() => {
                  if (zoomStart && zoomEnd) {
                    updateDateRange(zoomStart, zoomEnd, false);
                    setXDomain(["dataMin", "dataMax"]);
                  }
                }}>
                Enhance
              </Button>
            )}
            {zoomed && (
              <Button style={{ marginLeft: 5 }} variant="outlined" onClick={zoomOut}>
                Zoom Out
              </Button>
            )}
          </Grid> */}
        </Grid>
      ) : (
        <TimeBar updateDateRange={updateDateRange} startDate={startDate} endDate={endDate} />
      )}

      {buildGraphs(unitMeasurements, component, filters)}
      <Button
        onClick={() => {
          setCurrentMutator(pond.Mutator.MUTATOR_NONE);
          setGraphSettingsDialog(true);
          setNewChart(true);
        }}
        variant="contained"
        style={{ width: "100%", height: 50, background: green["500"] }}>
        +
      </Button>
      <GraphSettings
        open={graphSettingsDialog}
        closeDialog={() => {
          setGraphSettingsDialog(false);
        }}
        newChart={newChart}
        component={component}
        mutation={currentMutator}
        currentMin={currentMin}
        currentMax={currentMax}
        adjustMin={val => {
          graphRanges[rangeIndex].min = val;
        }}
        adjustMax={val => {
          graphRanges[rangeIndex].max = val;
        }}
      />
    </React.Fragment>
  );
}
