import { Box, Theme, ToggleButton, ToggleButtonGroup, darken } from "@mui/material";
import { makeStyles, withStyles } from "@mui/styles";
import ButtonGroup from "common/ButtonGroup";
import { ExtractMoisture } from "grain";
import { GrainCable } from "models/GrainCable";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond, quack } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useState } from "react";
import { getThemeType } from "theme/themeType";
import { avg } from "utils";

const GRAIN_COLOUR = "#fff302";
const BIN_ASPECT_RATIO = 0.45;

const useStyles = makeStyles((theme: Theme) => {
  return ({
    "@keyframes hotNode": {
      from: {
        fill: "#fff"
      },
      to: {
        fill: "#c42605"
      }
    },
    "@keyframes coldNode": {
      from: {
        fill: "#fff"
      },
      to: {
        fill: "#0575E6"
      }
    },
    "@keyframes headspaceWarning": {
      from: {
        fill: "#d18221"
      },
      to: {
        fill: "#9a9d9f"
      }
    },
    "@keyframes headspaceCritical": {
      from: {
        fill: "#ff0000"
      },
      to: {
        fill: "#9a9d9f"
      }
    },
    outerShell: {
      fill: "url(#outerShellGradient)"
    },
    innerShell: {
      fill: "url(#linearGradient2)"
    },
    container: {
      fill: "#a1a9b1"
    },
    headspaceNormal: {
      fill: "#9a9d9f"
    },
    headspaceWarning: {
      fill: "#9a9d9f",
      animation: "$headspaceWarning 0.5s alternate infinite"
    },
    headspaceCritical: {
      fill: "#9a9d9f",
      animation: "$headspaceCritical 0.5s alternate infinite"
    },
    bottom: {
      fill: "#9c9fa2"
    },
    lidarGrainEstimate: {
      stroke: "#FFFFFF",
      strokeWidth: 10,
      strokeOpacity: 0.85
    },
    grainInventory: {
      fill: GRAIN_COLOUR,
      opacity: 0.5
    },
    cableLine: {
      strokeWidth: 10,
      strokeOpacity: 0.85
    },
    cableNode: {
      fill: "#fff"
    },
    excludedNode: {
      fill: "grey"
    },
    hotNode: {
      fill: "#fff",
      animation: "$hotNode 1.0s infinite"
    },
    coldNode: {
      fill: "#fff",
      animation: "$coldNode 1.0s infinite"
    },
    smallToggle: {
      position: "relative",
      marginBottom: 10
    },
    fullToggle: {
      position: "inherit",
      marginBottom: 20
    },
    tempHum: {
      fontSize: 30,
      strokeWidth: 2.5,
      alignmentBaseline: "middle",
      textAnchor: "middle",
      filter: "drop-shadow(20px 20px 20px rgb(0 0 0))"
    },
    clickableArea: {
      fill: "transparent",
      "&:hover": {
        fill: "lightGrey",
        opacity: 0.3,
        cursor: "pointer"
      }
    },
    cableGrainEstimate: {
      stroke: "white",
      strokeWidth: 20,
      strokeOpacity: 0.85,
      fill: "transparent"
    }
  });
});

interface Props {
  height: number;
  binShape?: pond.BinShape;
  fillPercentage: number;
  lidarEstimate?: number;
  cables?: GrainCable[];
  co2Sensors: pond.BinCO2[];
  hasMinHeight?: boolean;
  highTemp: number;
  lowTemp: number;
  showTempHum?: boolean;
  isFullScreen?: boolean;
  grainType?: pond.Grain;
  cableNodeClicked?: (cable: GrainCable, fillNode: number) => void;
  cableEstimate?: boolean;
  hottestNodeTemp?: number;
  coldestNodeTemp?: number;
  inventoryControl?: pond.BinInventoryControl;
}

interface GrainNodePoint {
  x: number;
  y: number;
}

interface EllipseData {
  key: string | number;
  xCenter: string | number;
  yCenter: string | number;
  xRadius: string | number;
  yRadius: string | number;
  fillColour: string;
}

interface NodeData { //this could be either a box or a circle depending
  key: string | number
  x: number;
  y: number
  width: string | number
  height: string | number
  borderRad: string | number
  nodeNumber: number
  nodeTemp: number,
  excluded: boolean,
}

interface CircleData {
  key: string | number
  centerX: string | number
  centerY: string | number
  radius: string | number
  onClick?: () => void
}

interface TextData {
  key: number | string,
  x: number,
  y: number,
  stroke: string,
  filter?: string,
  value: string
}

export default function BinSVGV2(props: Props) {
  const classes = useStyles();
  const {
    binShape,
    height,
    fillPercentage,
    lidarEstimate,
    cables,
    hasMinHeight,
    highTemp,
    lowTemp,
    showTempHum,
    isFullScreen,
    grainType,
    cableNodeClicked,
    cableEstimate,
    hottestNodeTemp,
    coldestNodeTemp,
    inventoryControl,
    co2Sensors
  } = props;
  const [{ user }] = useGlobalState();
  const [extraDetails, setExtraDetails] = useState<"temps" | "hums">("temps");
  const [nodeWarning, setNodeWarning] = useState(false);

  // const StyledToggleButtonGroup = withStyles(theme => ({
  //   grouped: {
  //     margin: theme.spacing(-0.5),
  //     border: "none",
  //     padding: theme.spacing(1),
  //     "&:not(:first-child):not(:last-child)": {
  //       borderRadius: 24,
  //       marginRight: theme.spacing(0.5),
  //       marginLeft: theme.spacing(0.5)
  //     },
  //     "&:first-child": {
  //       borderRadius: 24,
  //       marginLeft: theme.spacing(0.25)
  //     },
  //     "&:last-child": {
  //       borderRadius: 24,
  //       marginRight: theme.spacing(0.25)
  //     }
  //   },
  //   root: {
  //     backgroundColor: darken(
  //       theme.palette.background.paper,
  //       getThemeType() === "light" ? 0.05 : 0.25
  //     ),
  //     borderRadius: 24,
  //     content: "border-box"
  //   }
  // }))(ToggleButtonGroup);

  // const StyledToggle = withStyles({
  //   root: {
  //     backgroundColor: "transparent",
  //     overflow: "visible",
  //     content: "content-box",
  //     "&$selected": {
  //       backgroundColor: "gold",
  //       color: "black",
  //       borderRadius: 24,
  //       fontWeight: "bold"
  //     },
  //     "&$selected:hover": {
  //       backgroundColor: "rgb(255, 255, 0)",
  //       color: "black",
  //       borderRadius: 24
  //     }
  //   },
  //   selected: {}
  // })(ToggleButton);

  const gradients = () => {
    const burning = "#8a1f0d";
    const hot = "#c42605";
    const warm = "#d75719";
    const cool = "#429bb8";
    const cold = "#0575E6";
    const freezing = "#021B79";
    return (
      <React.Fragment>
        <radialGradient id="nodeBurning">
          <stop offset="0%" stopColor={burning} stopOpacity="0.8" />
          <stop offset="60%" stopColor={hot} stopOpacity="0.4" />
          <stop offset="80%" stopColor={warm} stopOpacity="0.2" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeHot">
          <stop offset="10%" stopColor={hot} stopOpacity="0.8" />
          <stop offset="55%" stopColor={warm} stopOpacity="0.4" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeWarm">
          <stop offset="20%" stopColor={warm} stopOpacity="0.8" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeStable">
          <stop offset="0%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeCool">
          <stop offset="20%" stopColor={cool} stopOpacity="0.8" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeCold">
          <stop offset="10%" stopColor={cold} stopOpacity="0.8" />
          <stop offset="55%" stopColor={cool} stopOpacity="0.4" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <radialGradient id="nodeFreezing">
          <stop offset="0%" stopColor={freezing} stopOpacity="0.8" />
          <stop offset="60%" stopColor={cold} stopOpacity="0.4" />
          <stop offset="80%" stopColor={cool} stopOpacity="0.2" />
          <stop offset="100%" stopColor={GRAIN_COLOUR} stopOpacity="0" />
        </radialGradient>
        <linearGradient id="outerShellGradient" gradientTransform="rotate(90)">
          <stop offset="10%" stopColor="#cacdd3" />
          <stop offset="90%" stopColor="#b5bcc4" />
        </linearGradient>
        <linearGradient id="innerShellGradient" gradientTransform="rotate(90)">
          <stop offset="10%" stopColor="#b5bcc4" />
          <stop offset="90%" stopColor="#cacdd3" />
        </linearGradient>
      </React.Fragment>
    );
  };

  const getGrainGradient = (temperature: number): string => {
    let nodeTemp = temperature;
    if (highTemp === 0 && lowTemp === 0) {
      if (nodeTemp >= 30) return "url(#nodeBurning)";
      else if (nodeTemp >= 25) return "url(#nodeHot)";
      else if (nodeTemp >= 20) return "url(#nodeWarm)";
      else if (nodeTemp >= 15) return "url(#nodeStable)";
      else if (nodeTemp >= 10) return "url(#nodeCool)";
      else if (nodeTemp >= 5) return "url(#nodeCold)";
      else return "url(#nodeFreezing)";
    } else {
      if (nodeTemp >= highTemp + 10) return "url(#nodeBurning)";
      else if (nodeTemp >= highTemp + 5) return "url(#nodeHot)";
      else if (nodeTemp >= highTemp) return "url(#nodeWarm)";
      else if (nodeTemp < highTemp && nodeTemp > lowTemp) return "url(#nodeStable)";
      else if (nodeTemp >= lowTemp) return "url(#nodeCool)";
      else if (nodeTemp >= lowTemp - 5) return "url(#nodeCold)";
      else return "url(#nodeFreezing)";
    }
  };

  const headspaceClass = () => {
    if (!co2Sensors || co2Sensors.length === 0) return classes.headspaceNormal;
    // let testSensor = [pond.BinCO2.create({
    //   device: 4,
    //   key: "testSensor",
    //   name: "test Sensor",
    //   ppm: 1600
    // })]
    let highestReading = 0;
    //testSensor.forEach(sensor => {
    co2Sensors.forEach(sensor => {
      //if the current sensor is greater the the currently highest reading change it to the current sensor
      highestReading = sensor.ppm > highestReading ? sensor.ppm : highestReading;
    });
    if (highestReading > 1500) return classes.headspaceCritical;
    if (highestReading > 1000) return classes.headspaceWarning;
    return classes.headspaceNormal;
  };

  const borderColor = () => {
    //determine color of border based on node status: > high or < low = red, within both = green
    return !cables || cables.length === 0 ? "#000000" : nodeWarning ? "#ff0000" : "#00ff00";
  };

  //determine the pixel values for where the bottom and the top of the inventory piece are (NOTE 0,0 is the top left corner)
  const inventoryTopVal = () => {
    return binShape === pond.BinShape.BIN_SHAPE_FLAT_BOTTOM ? 160 : 180;
  };

  const inventoryBottomVal = () => {
    return binShape === pond.BinShape.BIN_SHAPE_FLAT_BOTTOM ? 1170.3 : 1155;
  };

  const lidarGrainEstimate = () => {
    const binTop = inventoryTopVal();
    const binBottom = inventoryBottomVal();
    //the y axis for svg starts at the top and goes down so in order to 'flip' the line to use the bottom of the bin subtract it from 100 since the estimate is a percentage
    const grainFillTo =
      ((lidarEstimate ? 100 - lidarEstimate : 0) * (binBottom - binTop)) / 100 + binTop;
    let controlY = grainFillTo - 60;

    return (
      <g key={"estimate"} id={"estimate"} data-name={"estimate"}>
        <path
          d={
            "M 54 " +
            grainFillTo +
            "C 54 " +
            controlY +
            ", 528 " +
            controlY +
            ", 527 " +
            grainFillTo
          }
          className={classes.lidarGrainEstimate}
          strokeDasharray="20"
          fill="transparent"
        />
      </g>
    );
  };

  const topNodeHat = (x: number, y: number) => {
    return (
      <path
        d={
          "M " +
          (x - 40) +
          " " +
          (y - 30) + //starting point of the curve
          " C " +
          (x - 2) +
          " " +
          (y - 50) +
          ", " + //first control point
          (x + 20) +
          " " +
          (y - 50) +
          ", " + //second control point
          (x + 40) +
          " " +
          (y - 30) // ending point of the curve
        }
        stroke="white"
        strokeWidth={10}
        fill="transparent"
      />
    );
  };

  const cableGrainEstimate = (
    points: GrainNodePoint[],
    cableSpacing: number,
    startY: number,
    endY: number
  ) => {
    let cableGrainPath = "M 54 " + startY; //set the start
    points.forEach((pathPoint, i) => {
      if (i === 0) {
        //for the first point
        cableGrainPath = cableGrainPath + " L " + pathPoint.x + " " + pathPoint.y;
      } else {
        //for all other points
        if (pathPoint.y === points[i - 1].y) {
          //if the elevation has not changed from the last point draw a straight line
          cableGrainPath = cableGrainPath + " L " + pathPoint.x + " " + pathPoint.y;
        } else {
          // if it has changed draw a line with 2 opposite curves
          //get the midpoint for the x and y values to draw the first curve too
          let midX = pathPoint.x - cableSpacing / 2;
          //subtract half the difference between the two points from the current y value
          let midY = pathPoint.y - (pathPoint.y - points[i - 1].y) / 2;

          //the control points x is the mid point
          let controlX = midX;
          //the control points y is where the line is starting
          let controlY = points[i - 1].y;

          //draw the first curve from the previous point to the midpoint
          cableGrainPath =
            cableGrainPath + " Q " + controlX + " " + controlY + ", " + midX + " " + midY;
          /**
           * draw the second curve from the midpoint to the current point
           * using T makes the svg curv derive what the control point needs to be based on the curve before it to create the opposite curve
           * if there is no curve in front (using Q) it should just draw a straight line
           */
          cableGrainPath = cableGrainPath + " T " + pathPoint.x + " " + pathPoint.y;
        }
      }
    });
    cableGrainPath = cableGrainPath + " L 528 " + endY;
    //setCableEstimatPath(cableGrainPath)
    return cableGrainPath;
  };

  const nodeClass = (nodeTemp: number, underTop: boolean) => {
    if (hottestNodeTemp && hottestNodeTemp.toFixed(2) === nodeTemp.toFixed(2) && underTop) {
      return classes.hotNode;
    }
    if (coldestNodeTemp && coldestNodeTemp.toFixed(2) === nodeTemp.toFixed(2) && underTop) {
      return classes.coldNode;
    }
    return classes.cableNode;
  };

  const renderText = (text: TextData[]) => {
    return text.map(text => (
          <text
            key={text.key}
            x={text.x}
            y={text.y}
            stroke={text.stroke}
            filter={text.filter}
            className={classes.tempHum}>
            {text.value}
          </text>
    ))
  }

  const multiCables = () => {
    if (!cables) return;
    //sort the cables by node number so the one with the most nodes is in the middle
    let sortedCables = cables.sort((a, b) => {
      //if the cables have the same number of nodes
      if (b.temperatures.length === a.temperatures.length) {
        //sort by temp only last and any type that has humidity first
        if ((avg(a.humidities) === 0 || a.humidities.length === 0) && (avg(b.humidities) !== 0 || b.humidities.length > 0)) {
          return 1;
        } else if ((avg(b.humidities) === 0 || b.humidities.length === 0) && (avg(a.humidities) !== 0 || a.humidities.length > 0)) {
          return -1;
        } 
        else {
          return a.key() > b.key() ? 1 : -1;
        }
      }
      //otherwise sort by the longest cable first
      return b.temperatures.length - a.temperatures.length;
    });
    let cableLines: JSX.Element[] = [];
    const centerX = 290;
    const minCableX = 80; //the farthest left position
    const maxCableX = 502; //the farthest right position
    const spacingX = (maxCableX - minCableX) / (cables.length + 1);
    const containerTopY = binShape === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM ? 80 : 58; //the highest the cable can be drawn
    const containerBottomY = binShape === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM ? 1155 : 1170; // the lowest the cable can be drawn

    /**
     * variables used for the cable grain line
     */
    //this array will store the path coordinates to build the curved grain estimate using the day/night cycle top node stuff
    let cableGrainPathPoints: GrainNodePoint[] = [];
    let cableGrainStartY = 0;
    let cableGrainEndY = 0;

    let leftCables = 0; //starts at 0 because the first left will be the middle
    let rightCables = 1;

    sortedCables.forEach((cable, cableIndex) => {
      //determine x for cable when the total is odd
      let cablePos = centerX;
      if (cableIndex % 2 === 0) {
        cablePos = cablePos - spacingX * leftCables;
        leftCables++;
      } else {
        cablePos = cablePos + spacingX * rightCables;
        rightCables++;
      }

      //if an even number of cables offset them to the left by half of the spacing
      if (sortedCables.length % 2 === 0) {
        cablePos = cablePos - spacingX / 2;
      }

      //after the x position is determined determine the y positions based on the x and the width and angle of the hopper and headspace of the svg
      //determine top y (assuming angle is about 30 degrees)
      let topY = Math.abs(centerX - cablePos) * Math.tan(20 * (Math.PI / 180)) + containerTopY;

      //determine bottom y if the bin has a hopper
      let bottomY = containerBottomY;
      if (binShape === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM) {
        bottomY = containerBottomY - Math.abs(centerX - cablePos) * Math.tan(40 * (Math.PI / 180));
      }

      //determine the colour of the line based on if the cable is measuring humidity
      let cableCol = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_PERCENT).colour();
      if (cable.humidities.length === 0 || avg(cable.humidities) === 0) {
        cableCol = describeMeasurement(quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE).colour();
      }

      //go through the temperatures to build a node for each one and place it on the line
      let numNodes = cable.temperatures.length;
      let topNode = cable.topNode;

      const minNodeY = topY; // the top of the cable
      const maxNodeY = bottomY; //the bottom of the cable
      const filledToY = (fillPercentage * (minNodeY - maxNodeY)) / 100 + maxNodeY;
      const nodeSpacingY = (maxNodeY - minNodeY) / (cable.temperatures.length + 1); //removing the +1 here would place the nodes starting at the bottom of the cable line rather than from the center

      //let nodeHeatMap: React.SVGProps<SVGEllipseElement>[] = [];
      let nodeHeatMapData: EllipseData[] = [];
      //let nodes: React.SVGProps<SVGCircleElement | SVGRectElement | SVGElement>[] = [];
      let nodesData: NodeData[] = [];

      //let temps: React.SVGProps<SVGTextElement>[] = [];
      let tempData: TextData[] = []
      //let hums: React.SVGProps<SVGTextElement>[] = [];
      let humData: TextData[] = [];
      //let nodeClickers: React.SVGProps<SVGCircleElement>[] = [];
      let nodeClickData: CircleData[] = [];

      cable.temperatures.forEach((temp, index) => {
        //since the array goes from the top down set the nodeNumber we are currently working with to be the number of nodes we have left
        //ie if we have 2 nodes left to draw we are on node 2
        let nodeNumber = numNodes;
        let displayTemp = temp;

        //if the node is below the top node, if there is no top node assume no nodes in grain
        if (nodeNumber <= topNode) {
          //check if the node is hot (this is if we want to set the bin border colour based on any node in the grain)
          if (!nodeWarning && temp > highTemp) {
            setNodeWarning(true);
          }
        }

        //convert to F if that is what is in the user settings
        if (user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT) {
          displayTemp = temp * 1.8 + 32;
        }

        //determine how high to draw the node on the cable
        const nodeY = nodeSpacingY * (index + 1) + minNodeY;

        //if we are using the auto top nodes only show the heatmap for nodes at or below the top node and it is not an ecluded node
        if (!cable.excludedNodes.includes(nodeNumber - 1)){
          if (cable.topNode > 0) {
            if (nodeNumber <= cable.topNode) {
              nodeHeatMapData.push({
                key: index,
                xCenter: cablePos,
                yCenter: nodeY,
                xRadius: "120",
                yRadius: "60",
                fillColour: getGrainGradient(temp)
              })
            }
          } else if (nodeY > filledToY) {
            nodeHeatMapData.push({
              key: index,
              xCenter: cablePos,
              yCenter: nodeY,
              xRadius: "120",
              yRadius: "60",
              fillColour: getGrainGradient(temp)
            })
          }
        }
        const displayWidth = 90;
        const displayHeight = 45;
        const cornerRad = 10;
        //add the boxes to display the temps in fulscreen view
        // nodes.push(
        //   showTempHum ? (
        //     <rect
        //       key={"node" + index}
        //       x={cablePos - displayWidth / 2}
        //       y={nodeY - displayHeight / 2}
        //       width={displayWidth}
        //       height={displayHeight}
        //       rx={cornerRad}
        //       className={classes.cableNode}></rect>
        //   ) : (
        //     <g key={"node" + index}>
        //       {nodeNumber === topNode && topNodeHat(cablePos, nodeY)}
        //       <circle cx={cablePos} cy={nodeY} className={nodeClass(temp)} r="16"></circle>
        //     </g>
        //   )
        // );
        //if showTempHum is true will render as a box otherwise will render as a circle which is why the x,y, and radius values will be different
        nodesData.push({
          key: "node" + index,
          x: showTempHum ? cablePos - displayWidth / 2 : cablePos,
          y: showTempHum ? nodeY - displayHeight / 2 : nodeY,
          height: displayHeight,
          width: displayWidth,
          borderRad: showTempHum ? cornerRad : "16",
          nodeNumber: nodeNumber,
          nodeTemp: temp,
          excluded: cable.excludedNodes.includes(nodeNumber - 1)
        })
        tempData.push({
          key: "node" + index + "temp",
          x: cablePos,
          y: nodeY + 10,
          stroke: "blue",
          value: displayTemp.toFixed(1) +"°",
          filter: "drop-shadow( 4px 4px 10px rgba(0, 0, 0, .7))"
        })
        humData.push({
          key: "node" + index + "hum",
          x: cablePos,
          y: nodeY + 10,
          stroke: "green",
          value: ExtractMoisture(props.grainType, temp, cable.humidities[index] ?? 0).toFixed(1)+"%",
        })
        !showTempHum &&
          nodeClickData.push({
            key: "node"+index,
            centerX: cablePos,
            centerY: nodeY,
            radius: "50",
            onClick: () => {
              if (cable.key() !== "" && cableNodeClicked) {
                cableNodeClicked(cable, nodeNumber);
              }
            }
          })

        //if the current node is the top node of the cable use its x and y to add to the cable grain path
        if (nodeNumber === topNode) {
          //need to determin the correct control points

          //if it is an even cable prepend it to the string, if it is odd append it
          if (cableIndex % 2 === 0) {
            cableGrainStartY = nodeY - nodeSpacingY / 2;
            cableGrainPathPoints.unshift({
              x: cablePos,
              y: nodeY - nodeSpacingY / 2
            });
            //cableGrainPathPoints = (" L " + cablePos + " " + (nodeY - nodeSpacingY/2)) + cableGrainPathPoints
          } else {
            cableGrainEndY = nodeY - nodeSpacingY / 2;
            cableGrainPathPoints.push({
              x: cablePos,
              y: nodeY - nodeSpacingY / 2
            });
            //cableGrainPathPoints = cableGrainPathPoints + (" L " + cablePos + " " + (nodeY - nodeSpacingY/2))
          }
        }
        //decrement the number of nodes left
        numNodes--;
      });

      cableLines.push(
        <g key={"cable" + cableIndex} id={"cable " + cableIndex} data-name={"cable " + cableIndex}>
          {nodeHeatMapData.map(e => (
            <ellipse
              key={e.key}
              cx={e.xCenter}
              cy={e.yCenter}
              rx={e.xRadius}
              ry={e.yRadius}
              fill={e.fillColour}
            />
          ))}
          <line
            stroke={cableCol}
            x1={cablePos}
            y1={topY}
            x2={cablePos}
            y2={bottomY}
            className={classes.cableLine}
          />
          {nodesData.map(e => {
            if (showTempHum) {
              return (
              <rect
                key={e.key}
                x={e.x}
                y={e.y}
                width={e.width}
                height={e.height}
                rx={e.borderRad}
                className={e.excluded ? classes.excludedNode : classes.cableNode}/>
              )
            } else {
              return (
                <g key={e.key}>
                  {e.nodeNumber === topNode && !e.excluded && topNodeHat(cablePos, e.y)}
                  <circle cx={cablePos} cy={e.y} className={e.excluded ? classes.excludedNode : nodeClass(e.nodeTemp, e.nodeNumber <= topNode)} r="16"></circle> 
                </g>
              )
            }
          })}
          {showTempHum && (extraDetails === "temps" ? renderText(tempData) : renderText(humData))}
          {nodeClickData.map((e) => (
            <circle
              key={e.key}
              onClick={e.onClick}
              cx={e.centerX}
              cy={e.centerY}
              className={classes.clickableArea}
              r={e.radius}
            />
          ))}
        </g>
      );
    });
    //for single cable bins the endY would still be 0 so just make it the startY to draw a straight line across the bin
    if (cableGrainEndY === 0) {
      cableGrainEndY = cableGrainStartY;
    }
    if (cableEstimate && cables.length > 0 && cableGrainPathPoints.length === cables.length) {
      let cablePath = cableGrainEstimate(
        cableGrainPathPoints,
        spacingX,
        cableGrainStartY,
        cableGrainEndY
      );
      cableLines.unshift(
        <g key={"cableGrainLvl"} id={"cableGrainLvl"} data-name={"cableGrainLvl"}>
          <path d={cablePath} className={classes.cableGrainEstimate} />
        </g>
      );
      if (inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC) {
        cableLines.unshift(cableEstimateInventory(cablePath));
      }
    }

    return cableLines;
  };

  //inventory fill for under the top node line
  const cableEstimateInventory = (estimatePath: string) => {
    let dPath = estimatePath;
    //use the cable estimate path for the fist section to draw
    if (binShape === pond.BinShape.BIN_SHAPE_FLAT_BOTTOM) {
      //if the bin is a flat bottom draw the line down to the bottom corner across and then up
      dPath = dPath + " L 527.8, 1170.3 L 53.9, 1170.3 z";
    } else if (binShape === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM) {
      //if the bin is a hopper draw down to the corner of the container then do the hopper curve then to the other container corner and then up
      dPath =
        dPath +
        " L 528,964.32 L 365.921,1123.724 C 320,1165 262,1165 216.079,1123.734 L 54,964.32 z";
    }
    return (
      <path key={"InventoryFill"} className={classes.grainInventory} d={dPath} id="cableFill" />
    );
  };

  //inventory fill for the hopper cone
  const grainHopperInventory = () => {
    const hopperTop = 964.93;
    const hopperBottom = 1155;
    //the hopper is about 18 percent of the bin svg
    const hopperPercent = fillPercentage > 18 ? 100 : (fillPercentage / 18) * 100;
    const hopperfill = (hopperPercent * (hopperTop - hopperBottom)) / 100 + hopperBottom;
    const hopperRad = 237;
    const xOffset = (hopperPercent / 100) * hopperRad;
    const startX = 291 - xOffset;
    const endX = 291 + xOffset;
    return (
      <path
        className={classes.grainInventory}
        d={
          "m " +
          startX +
          "," +
          hopperfill +
          " L 216.079,1123.734 C 262,1165 320,1165 365.921,1123.734 L " +
          endX +
          "," +
          hopperfill +
          " z"
        }
        id="hopper"
      />
    );
  };

  //inventory fill for the main container of both flat and hopper bins
  const grainContainerInventory = (hopperOffset?: boolean) => {
    const inventoryBottom = inventoryBottomVal();
    const inventoryTop = inventoryTopVal();
    //let maxY = maxInventoryY();
    const grainFillTo = (fillPercentage * (inventoryTop - inventoryBottom)) / 100 + inventoryBottom;
    let controlY = grainFillTo - 60;
    let x1 = 54;
    let x2 = 528;
    let yBottom = hopperOffset ? inventoryBottom - 190.063 : inventoryBottom;
    return (
      <path
        className={classes.grainInventory}
        id={"grainLevel"}
        d={
          "M " +
          x1 +
          " " +
          yBottom +
          " V " +
          grainFillTo +
          " C " +
          x1 +
          " " +
          controlY +
          ", " +
          x2 +
          " " +
          controlY +
          ", " +
          x2 +
          " " +
          grainFillTo +
          " V " +
          yBottom +
          " Z "
        }
      />
    );
  };

  const inventoryLayer = () => {
    //use the manual/adjustable inventory layer
    if (binShape === pond.BinShape.BIN_SHAPE_FLAT_BOTTOM) {
      return grainContainerInventory();
    } else if (binShape === pond.BinShape.BIN_SHAPE_HOPPER_BOTTOM) {
      return (
        <React.Fragment>
          {fillPercentage > 18 && grainContainerInventory(true)}
          {grainHopperInventory()}
        </React.Fragment>
      );
    }
  };

  const hopperSVG = () => {
    return (
      <g id="g24">
        <path
          fill={borderColor()}
          d="m 301.62573,2.389772 c -6.76183,-3.15302934 -14.48963,-3.15302934 -21.00996,0 l -280.61577,133.154858 v 869.02337 0 c 0,0 0,0 0.24149378,0 l 278.68381622,252.7275 c 6.76183,6.306 17.14606,6.306 24.14938,0 l 277.71784,-251.9999 c 0.48299,-0.2425 0.72448,-0.7276 1.20747,-0.9701 v 0 -868.78087 z"
          id="border"
        />
        <path
          fill="url(#outerShellGradient)"
          d="m 300.96847,20.297314 c -6.34357,-3.063085 -13.59337,-3.063085 -19.71039,0 l -263.25808,129.356466 v 844.23357 0 c 0,0 0,0 0.226556,0 l 261.445644,245.51805 c 6.34357,6.1261 16.08548,6.1261 22.6556,0 l 260.53942,-244.81108 c 0.45311,-0.23571 0.67967,-0.70697 1.13278,-0.94259 v 0 -843.99795 z"
          id="outerShell"
        />
        <path
          fill="url(#innerShellGradient)"
          d="m 409.13869,88.201438 c -74.45256,-37.601917 -160.94891,-37.601917 -235.40147,0 l -137.73722,69.365032 v 821.40349 0 c 0,0 0,0 0,0 l 174.30657,167.92394 c 45.76642,44.1415 115.40146,44.1415 161.16788,0 l 173.64964,-167.22341 c 0.43795,-0.23344 0.65693,-0.70053 0.87591,-0.93417 v 0 -821.16985 l -137.51824,-69.365032 z"
          id="innerShell"
        />
        <path
          className={classes.container}
          d="m 400.32684,106.51307 c -69.30828,-35.35076 -149.3454,-35.35076 -218.65368,0 l -127.67316,65.35237 v 793.29873 0 -0.23258 l 162.00543,158.84571 c 42.4862,41.6303 107.07379,41.6303 149.77456,0 47.99803,-57.5062 162.22001,-158.84571 162.22001,-158.84571 v 0 -793.06615 z"
          id="container"
        />

        {fillPercentage > 0 &&
        (inventoryControl !== pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC ||
          !inventoryControl) ? (
          inventoryLayer()
        ) : (
          <path
            className={classes.bottom}
            d="m 54,964.93159 L 216.079,1123.734 C 258.584,1165.414 256.80,1165.414 365.921,1123.734 L 528,964.932 z"
            id="hopper"
          />
        )}
        <path
          className={headspaceClass()}
          d="m 181.82986,106.67809 -127.82986,65.18735 v 0 c 0,0 474,0 474,0 v 0 c 0,0 -127.61538,-65.41933 -127.61538,-65.41933 -69.27693,-35.261481 -149.27783,-35.261481 -218.55476,0 z"
          id="headspace"
        />
      </g>
    );
  };

  const flatBottomSVG = () => {
    return (
      <g id="flatBottomBin">
        <path
          fill={borderColor()}
          d="m 280.59642,2.3587839 -280.59642,134.4253061 v 1077.02571 0 c 3.7012719,26.6822 132.54555,48.1902 291,48.1902 158.45445,0 287.39876,-21.508 291,-48.1902 v 0 -1077.02571 l -280.39636,-134.4253061 c -6.7023,-3.1450452 -14.40495,-3.1450452 -21.00722,0 z"
          id="border"
        />
        <path
          fill="url(#outerShellGradient)"
          d="m 281.23994,20.291497 -263.23994,130.590673 v 1046.30233 0 c 3.472327,25.921 124.34685,46.8155 273,46.8155 148.65315,0 269.62152,-20.8945 273,-46.8155 v 0 -1046.30233 l -263.05225,-130.590673 c -6.28773,-3.055329 -13.51392,-3.055329 -19.70781,0 z"
          id="outerShell"
        />
        <path
          fill="url(#innerShellGradient)"
          d="m 252.70462,41.634256 -216.70462,111.909164 v 1054.50588 c 30.309608,20.5364 132.94484,35.6164 255,35.6164 122.05516,0 224.69039,-15.08 255,-35.6164 v -1054.50588 l -216.70462,-111.909164 c -24.22954,-12.500491 -52.36122,-12.500491 -76.59076,0 z"
          id="innerShell"
        />
        <path
          className={classes.container}
          d="m 187.30687,84.199999 -133.30687,69.900001 v 1017 0 c 2.970376,23.7999 107.92366,42.9999 237,42.9999 129.07634,0 234.02962,-19.2 237,-42.9999 v 0 -1017 l -133.21686,-70.000001 c -65.70832,-34.499999 -141.94797,-34.499999 -207.65629,0 z"
          id="container"
        />

        {fillPercentage > 0 &&
          (inventoryControl !== pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC ||
            !inventoryControl) &&
          inventoryLayer()}
        <ellipse
          className={fillPercentage > 0 ? classes.grainInventory : classes.bottom}
          cx="290.87698"
          cy="1170.2799"
          rx="237.00191"
          ry="43.718052"
          id="bottom"
        />
        <path
          className={headspaceClass()}
          d="m 187.29264,84.203148 -133.24203,69.896852 c 0,0 319.83487,-22.49899 473.94939,0 l -133.15206,-69.996847 c -65.67637,-34.498446 -141.87893,-34.498446 -207.5553,0 z"
          id="headspace"
        />
      </g>
    );
  };

  return (
    <React.Fragment>
      {showTempHum && (
        <Box
          display="flex"
          alignContent="center"
          justifyContent="center"
          textAlign="center"
          //visibility={showTempHum ? "visible" : "hidden"}
          className={!isFullScreen ? classes.smallToggle : classes.fullToggle}>
          <ButtonGroup 
            buttons={[
              {
                title: isFullScreen ? "Temperature" : "Temp",
                function: () => setExtraDetails("temps")
              },
              {
                title: grainType ? (isFullScreen ? "Moisture" : "EMC") : isFullScreen ? "Humidity" : "Hum",
                function: () => setExtraDetails("hums")
              }
            ]}
            toggle
          />
          {/* <StyledToggleButtonGroup
            value={extraDetails}
            exclusive
            size="small"
            aria-label="Bin Mode">
            <StyledToggle
              value={"temps"}
              aria-label="Node Temperature"
              onClick={() => setExtraDetails("temps")}>
              {isFullScreen ? "Temperature" : "Temp"}
            </StyledToggle>
            <StyledToggle
              onClick={() => setExtraDetails("hums")}
              value={"hums"}
              aria-label="Node Humidity">
              {grainType ? (isFullScreen ? "Moisture" : "EMC") : isFullScreen ? "Humidity" : "Hum"}
            </StyledToggle>
          </StyledToggleButtonGroup> */}
        </Box>
      )}
      <Box
        height={height}
        minHeight={hasMinHeight ? "300px" : "none"}
        width={height * BIN_ASPECT_RATIO}
        minWidth={hasMinHeight ? 300 * BIN_ASPECT_RATIO : "none"}>
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 582 1262" height="100%" width="100%">
          <defs>{gradients()}</defs>
          <title>Bin</title>
          <g id="Bin" data-name="Bin">
            {binShape === pond.BinShape.BIN_SHAPE_FLAT_BOTTOM ? flatBottomSVG() : hopperSVG()}
            <g id="BinInterior" data-name="BinInterior">
              {cables && multiCables()}
              {lidarEstimate && lidarGrainEstimate()}
            </g>
          </g>
        </svg>
      </Box>
    </React.Fragment>
  );
}
