import * as d3 from "d3";
import React, { useCallback, useEffect, useRef } from "react";

const VolatilityGraph = (props) => {
  const ref = useRef();
  const myClass = props.chartClass; // this can be passed in so multiple instances on same page
  let svg = null;
  let chartData = props.data;
  const minHeight = 280;
  const minWidth = 0;
  let janAlready = {};

  const initiateChart = useCallback(() => {
    svg = d3.select(ref.current);
    const parentElement = svg.node().parentElement;
    const width = Math.max(parentElement.clientWidth, minWidth);
    const height = Math.max(parentElement.clientHeight, minHeight);

    svg.attr("width", width).attr("height", height);

    const margins = { left: 20, right: 30, top: 20, bottom: 40 };
    const chartWidth = width - margins.left - margins.right;
    const chartHeight = height - margins.top - margins.bottom;

    chartData.map((m) => (m.datetime = new Date(m.datetime)));
    let xTicks = [];
    chartData = chartData.sort((a, b) => d3.ascending(a.datetime, b.datetime));
    chartData.forEach((d) => xTicks.push(d.datetime));
    const xScale = d3.scaleBand().domain(xTicks).range([0, chartWidth]);
    const xScaleLinear = d3
      .scaleLinear()
      .domain([0, xTicks.length - 1])
      .range([0, chartWidth]);

    const percentileLines = [9, 16, 20, 23, 25, 40];
    const percentileRange =
      percentileLines[percentileLines.length - 1] - percentileLines[0];
    const thresholdScale = d3
      .scaleThreshold()
      .range(props.percentileColors || [])
      .domain(percentileLines);

    let yExtent = d3.extent(chartData, (d) => +d.close);
    if (yExtent[0] > percentileLines[0]) {
      yExtent[0] = percentileLines[0];
    }
    if (yExtent[1] < percentileLines[percentileLines.length - 1]) {
      yExtent[1] = percentileLines[percentileLines.length - 1];
    }
    const yScale = d3.scaleLinear().domain(yExtent).range([chartHeight, 0]);

    let scaleGradientStops = [],
      positionPercent = 0,
      currentValue = percentileLines[0];
    percentileLines.forEach((d) => {
      positionPercent = ((d - currentValue) / percentileRange) * 100;
      scaleGradientStops.push({
        offset: positionPercent + "%",
        color: thresholdScale(d),
      });
    });

    svg
      .select(".lineGradient")
      .attr("id", "lineGradient")
      .attr("gradientUnits", "userSpaceOnUse")
      .attr("x1", 0)
      .attr("y1", chartHeight)
      .attr("x2", 0)
      .attr("y2", 0)
      .selectAll("stop")
      .data(scaleGradientStops)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);

    const line = d3
      .line()
      .x((d) => xScale(d.datetime))
      .y((d) => yScale(d.close));

    svg
      .select(".xAxis")
      .call(
        d3.axisBottom(xScaleLinear).tickSizeOuter(0).tickFormat(getTickFormat)
      )
      .attr(
        "transform",
        "translate(" + margins.left + "," + (margins.top + chartHeight) + ")"
      );

    svg.selectAll(".xAxis line").attr("display", "none");

    svg.selectAll(".xAxis path").attr("display", "none");
    svg
      .selectAll(".xAxis text")
      .attr("transform", "translate(-10,11) rotate(-40)");

    svg
      .select(".yAxis")
      .call(d3.axisRight(yScale).tickSizeOuter(0).ticks(8))
      .attr(
        "transform",
        "translate(" + (width - margins.right) + "," + margins.top + ")"
      );

    svg.selectAll(".yAxis line").attr("x1", 3);

    svg
      .selectAll(".axis path")
      .style("stroke", "#141227")
      .style("stroke-width", 2)
      .style("stroke-linecap", "square");

    svg
      .select(".volatilityPath")
      .attr("d", line(chartData))
      .attr("fill", "none")
      .attr("stroke", "url(#lineGradient)")
      .attr("stroke-width", 2)
      .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

    //quadrant group join
    const lineGroup = svg
      .selectAll(".lineGroup" + myClass)
      .data(percentileLines)
      .join(function (group) {
        const enter = group.append("g").attr("class", "lineGroup" + myClass);
        enter.append("line").attr("class", "thresholdLine");
        enter.append("rect").attr("class", "thresholdRect");
        enter.append("text").attr("class", "thresholdLabel");
        return enter;
      });

    lineGroup
      .select(".thresholdLine")
      .attr("x1", margins.left)
      .attr("x2", margins.left + chartWidth)
      .attr("y1", (d) => yScale(d) + margins.top)
      .attr("y2", (d) => yScale(d) + margins.top)
      .attr("stroke", (d) => thresholdScale(d))
      .attr("stroke-width", 1);

    lineGroup
      .select(".thresholdRect")
      .attr("y", (d) => yScale(d))
      .attr("width", margins.right - 3)
      .attr("height", 12)
      .attr("rx", 5)
      .attr("ry", 5)
      .attr("fill", (d) => thresholdScale(d))
      .attr(
        "transform",
        "translate(" +
          (margins.left + chartWidth - 3) +
          "," +
          (margins.top - 6) +
          ")"
      );

    lineGroup
      .select(".thresholdLabel")
      .attr("y", (d) => yScale(d) + 10)
      .attr("font-size", 10)
      .attr("text-anchor", "middle")
      .text((d) => (d / 100) * 100)
      .attr("fill", "white")
      .attr(
        "transform",
        "translate(" +
          (margins.left + chartWidth - 3 + (margins.right - 3) / 2) +
          "," +
          (margins.top - 6) +
          ")"
      );

    function getTickFormat(d) {
      console.log(xTicks[d]);
      const currentTicks = xTicks;
      if (xTicks[d] === undefined) {
        const decimal = Number("0." + String(d).split(".")[1]);
        const secondsPerDay = 86400;
        const startTime = new Date("01/01/2000 00:00");
        const hourSpan = d3.timeHour.count(
          currentTicks[0],
          currentTicks[currentTicks.length - 1]
        );
        return d3.timeFormat(hourSpan > 2 ? "%H:00" : "%H:%M")(
          d3.timeSecond.offset(startTime, decimal * secondsPerDay)
        );
      } else {
        let formatString = "";
        const daySpan = d3.timeDay.count(
          currentTicks[0],
          currentTicks[currentTicks.length - 1]
        );
        if (daySpan < 365) {
          //under a year, day then month
          formatString = "%d %b";
          if (
            xTicks[d].getMonth() === 0 &&
            janAlready[xTicks[d].getFullYear()] === undefined
          ) {
            janAlready[xTicks[d].getFullYear()] = true;
            formatString = "%Y";
          }
        } else {
          //over a year - month only
          formatString = "%b";
          if (
            xTicks[d].getMonth() === 0 &&
            janAlready[xTicks[d].getFullYear()] === undefined
          ) {
            janAlready[xTicks[d].getFullYear()] = true;
            formatString = "%Y";
          }
        }
        return d3.timeFormat(formatString)(xTicks[d]);
      }
    }
  });

  useEffect(() => {
    //copied this from another project
    initiateChart();
  }, [initiateChart, props.data]);

  return (
    <svg ref={ref}>
      <defs>
        <linearGradient className={"lineGradient"}></linearGradient>
      </defs>
      <g className={"axis xAxis"}></g>
      <g className={"axis yAxis"}></g>
      <rect className={"volatilityGradientRect"}></rect>
      <path className={"volatilityPath"}></path>
    </svg>
  );
};

VolatilityGraph.propTypes = {};

export default VolatilityGraph;
