import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import { AgCartesianChartOptions } from "ag-charts-enterprise";
import { getArgon, getHelium, getOxygen } from "./data";
import "ag-charts-enterprise";
import clone from "clone";

function randomDelta(min: number, max: number) {
  return Math.random() * (max - min) + min;
}
function randomIndex(length: number) {
  return Math.round(Math.random() * (length - 1));
}

const ChartExample = () => {
  const [options, setOptions] = useState<AgCartesianChartOptions>({
    title: {
      text: "Volume-Pressure Relationship with Confidence Intervals",
    },
    axes: [
      // Note: axis configuration is required only for line series.
      // The bottom axis defaults to 'number' for scatter series.
      { type: "number", position: "left" },
      { type: "number", position: "bottom" },
    ],
    series: [
      {
        data: getOxygen(),
        type: "scatter",
        xKey: "volume",
        yKey: "pressure",
        title: "Oxygen",
        errorBar: {
          xLowerKey: "volumeLower",
          xUpperKey: "volumeUpper",
          yLowerKey: "pressureLower",
          yUpperKey: "pressureUpper",
        },
      },
      {
        data: getHelium(),
        type: "scatter",
        xKey: "volume",
        yKey: "pressure",
        title: "Helium",
        errorBar: {
          xLowerKey: "volumeLower",
          xUpperKey: "volumeUpper",
          yLowerKey: "pressureLower",
          yUpperKey: "pressureUpper",
        },
      },
      {
        data: getArgon(),
        type: "scatter",
        xKey: "volume",
        yKey: "pressure",
        title: "Argon",
        errorBar: {
          xLowerKey: "volumeLower",
          xUpperKey: "volumeUpper",
          yLowerKey: "pressureLower",
          yUpperKey: "pressureUpper",
        },
      },
    ],
  });

  const scatter = () => {
    const nextOptions = clone(options);

    if (nextOptions.series !== undefined) {
      for (const series of nextOptions.series) {
        series.type = "scatter";
      }
    }

    setOptions(nextOptions);
  };

  const line = () => {
    const nextOptions = clone(options);

    if (nextOptions.series !== undefined) {
      for (const series of nextOptions.series) {
        series.type = "line";
      }
    }

    setOptions(nextOptions);
  };

  const resetData = () => {
    const nextOptions = clone(options);

    if (nextOptions.series !== undefined) {
      nextOptions.series[0].data = getOxygen();
      nextOptions.series[1].data = getHelium();
      nextOptions.series[2].data = getArgon();
    }

    setOptions(nextOptions);
  };

  const randomiseData = () => {
    const nextOptions = clone(options);

    if (nextOptions.series !== undefined) {
      const fn = (datum: Datum): Datum => {
        const volumeDelta = randomDelta(-0.1, 0.1);
        const pressureDelta = randomDelta(-1, 1);
        return {
          volume: datum.volume + volumeDelta,
          volumeLower: datum.volumeLower + volumeDelta,
          volumeUpper: datum.volumeUpper + volumeDelta,
          pressure: datum.pressure + pressureDelta,
          pressureLower: datum.pressureLower + pressureDelta,
          pressureUpper: datum.pressureUpper + pressureDelta,
        };
      };
      nextOptions.series[0].data = nextOptions.series[0].data?.map(fn);
      nextOptions.series[1].data = nextOptions.series[1].data?.map(fn);
      nextOptions.series[2].data = nextOptions.series[2].data?.map(fn);
    }

    setOptions(nextOptions);
  };

  const randomiseErrors = () => {
    const nextOptions = clone(options);

    if (nextOptions.series !== undefined) {
      const fn = (datum: Datum): Datum => {
        return {
          volume: datum.volume,
          volumeLower: datum.volume - randomDelta(0.05, 0.1),
          volumeUpper: datum.volume + randomDelta(0.05, 0.1),
          pressure: datum.pressure,
          pressureLower: datum.pressure - randomDelta(0.5, 2),
          pressureUpper: datum.pressure + randomDelta(0.5, 2),
        };
      };
      nextOptions.series[0].data = nextOptions.series[0].data?.map(fn);
      nextOptions.series[1].data = nextOptions.series[1].data?.map(fn);
      nextOptions.series[2].data = nextOptions.series[2].data?.map(fn);
    }

    setOptions(nextOptions);
  };

  const removeRandomElem = () => {
    const { series } = options;
    if (series !== undefined) {
      const meta: {
        seriesIndex: number;
        datumIndex: number;
      }[] = [];
      for (let seriesIndex = 0; seriesIndex < series.length; seriesIndex++) {
        const { data } = series[seriesIndex];
        for (
          let datumIndex = 0;
          datumIndex < (data?.length ?? 0);
          datumIndex++
        ) {
          meta.push({ seriesIndex, datumIndex });
        }
      }
      const { seriesIndex, datumIndex } = meta[randomIndex(meta.length)];
      series[seriesIndex].data?.splice(datumIndex, 1);
    }
  };

  return (
    <Fragment>
      <div className="toolbar">
        <button onClick={scatter}>Scatter</button>
        <button onClick={line}>Line</button>
        <button onClick={resetData}>Reset Data</button>
        <button onClick={randomiseData}>Randomise Data</button>
        <button onClick={randomiseErrors}>Randomise Errors</button>
        <button onClick={removeRandomElem}>Remove Random Point</button>
      </div>
      <AgCharts options={options as any} />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);

/** DARK MODE START **/
import { AgCharts as __chartAPI } from "ag-charts-community";

let darkmode =
  (localStorage["documentation:darkmode"] ||
    String(matchMedia("(prefers-color-scheme: dark)").matches)) === "true";

const isAgThemeOrUndefined = (theme) => {
  return (
    theme == null || (typeof theme === "string" && theme.startsWith("ag-"))
  );
};

const getDarkmodeTheme = (theme = "ag-default", preset) => {
  const baseTheme =
    preset === "price-volume" ? "ag-financial" : theme.replace(/-dark$/, "");
  return darkmode ? baseTheme + "-dark" : baseTheme;
};

__chartAPI.optionsMutationFn = function update(options, preset) {
  const nextOptions = { ...options };
  const theme = options.theme;
  if (isAgThemeOrUndefined(theme)) {
    nextOptions.theme = getDarkmodeTheme(theme, preset);
  } else if (
    typeof theme === "object" &&
    isAgThemeOrUndefined(theme.baseTheme)
  ) {
    nextOptions.theme = {
      ...options.theme,
      baseTheme: getDarkmodeTheme(theme.baseTheme, preset),
    };
  }
  return nextOptions;
};

const applyDarkmode = () => {
  document.documentElement.setAttribute("data-dark-mode", darkmode);
  const charts = document.querySelectorAll("[data-ag-charts]");
  charts.forEach((element) => {
    const chart = __chartAPI.getInstance(element.parentElement);
    if (chart == null) return;
    // This is just needed to trigger the theme update
    chart.update(chart.getOptions());
  });
  return charts.length !== 0;
};

if (!applyDarkmode()) {
  /* React defers updates. Rather than try and hook into the API, just wait until the darkmode is applied. */
  const observer = new MutationObserver(() => {
    if (applyDarkmode()) {
      observer.disconnect();
    }
  });
  observer.observe(document.body, {
    attributes: true,
    childList: true,
    subtree: true,
  });
}
window.addEventListener("message", (event) => {
  if (event.data && event.data.type === "color-scheme-change") {
    darkmode = event.data.darkmode;
    applyDarkmode();
  }
});
/** DARK MODE END **/
