"use strict";

import {
  chartExportOptions,
  createTitleForHeatmap,
  createSubtitleForHeatmap,
} from "./chartHelpers.mjs";

/**
 * Format the response from SensorThings API to make it suitable for use in a heatmap
 * @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API
 * @returns {Array} Array of formatted observations suitable for use in a heatmap
 */
const formatSensorThingsApiResponseForHeatMap = function (obsArray) {
  if (!obsArray) return;

  return obsArray.map((obs) => {
    // Get the date/time string; first element in input array; remove trailing "Z"
    const obsDateTimeInput = obs[0].slice(0, -1);
    // Get the "date" part of an observation
    const obsDateInput = obs[0].slice(0, 10);
    // Create Date objects
    const obsDateTime = new Date(obsDateTimeInput);
    const obsDate = new Date(obsDateInput);
    // x-axis -> timestamp; will be the same for observations from the same date
    const timestamp = Date.parse(obsDate);
    // y-axis -> hourOfDay
    const hourOfDay = obsDateTime.getHours();
    // value -> the observation's value; second element in input array
    const value = obs[1];
    return [timestamp, hourOfDay, value];
  });
};

/**
 * Calculate the minimum and maximum values for a heatmap's color axis
 * @param {Array} formattedObsArrHeatmap Response from SensorThings API formatted for use in a heatmap
 * @returns {Object} An object containing the minimum and maximum values
 */
const calculateMinMaxValuesForHeatmapColorAxis = function (
  formattedObsArrHeatmap
) {
  // The observation value is the third element in array
  const obsValueArr = formattedObsArrHeatmap.map((obs) => obs[2]);

  // Extract integer part
  const minValue = Math.trunc(Math.min(...obsValueArr));
  const maxValue = Math.trunc(Math.max(...obsValueArr));

  // Calculate the closest multiple of 5
  const minObsValue =
    minValue > 0 ? minValue - (minValue % 5) : minValue + (minValue % 5);
  const maxObsValue = maxValue + (5 - (maxValue % 5));

  return { minObsValue, maxObsValue };
};

/**
 * Draw a heatmap using Highcharts library
 * @param {Array} formattedObsArrayForHeatmap Response from SensorThings API formatted for use in a heatmap
 * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties
 * @returns {undefined} undefined
 */
const drawHeatMapHighcharts = function (
  formattedObsArrayForHeatmap,
  extractedFormattedDatastreamProperties
) {
  // Arrays of datastream properties
  const {
    datastreamDescriptionsArr,
    datastreamNamesArr,
    phenomenonNamesArr,
    unitOfMeasurementSymbolsArr,
  } = extractedFormattedDatastreamProperties;

  const [DATASTREAM_DESCRIPTION] = datastreamDescriptionsArr;
  const [DATASTREAM_NAME] = datastreamNamesArr;
  const [PHENOMENON_NAME] = phenomenonNamesArr;
  const [PHENOMENON_SYMBOL] = unitOfMeasurementSymbolsArr;

  const TEXT_CHART_TITLE = createTitleForHeatmap(phenomenonNamesArr);

  const TEXT_CHART_SUBTITLE = createSubtitleForHeatmap(datastreamNamesArr);

  const {
    minObsValue: MINIMUM_VALUE_COLOR_AXIS,
    maxObsValue: MAXIMUM_VALUE_COLOR_AXIS,
  } = calculateMinMaxValuesForHeatmapColorAxis(formattedObsArrayForHeatmap);

  Highcharts.chart("chart-heatmap", {
    chart: {
      type: "heatmap",
      zoomType: "x",
    },

    boost: {
      useGPUTranslations: true,
    },

    title: {
      text: TEXT_CHART_TITLE,
      align: "center",
      x: 40,
    },

    subtitle: {
      text: TEXT_CHART_SUBTITLE,
      align: "center",
      x: 40,
    },

    xAxis: {
      type: "datetime",
      // min: Date.UTC(2017, 0, 1),
      // max: Date.UTC(2017, 11, 31, 23, 59, 59),
      labels: {
        align: "left",
        x: 5,
        y: 14,
        format: "{value:%B}", // long month
      },
      showLastLabel: false,
      tickLength: 16,
    },

    yAxis: {
      title: {
        text: null,
      },
      labels: {
        format: "{value}:00",
      },
      minPadding: 0,
      maxPadding: 0,
      startOnTick: false,
      endOnTick: false,
      tickPositions: [0, 3, 6, 9, 12, 15, 18, 21, 24],
      tickWidth: 1,
      min: 0,
      max: 23,
      reversed: true,
    },

    colorAxis: {
      stops: [
        [0, "#3060cf"],
        [0.5, "#fffbbc"],
        [0.9, "#c4463a"],
        [1, "#c4463a"],
      ],
      min: MINIMUM_VALUE_COLOR_AXIS,
      max: MAXIMUM_VALUE_COLOR_AXIS,
      startOnTick: false,
      endOnTick: false,
      labels: {
        format: `{value}${PHENOMENON_SYMBOL}`,
      },
    },

    exporting: chartExportOptions,

    tooltip: {
      formatter() {
        const headerString = `${PHENOMENON_NAME}<br/>`;

        // Check whether the point value is null or not; this will determine the string that we'll render
        const pointString =
          this.point.value === null
            ? `${Highcharts.dateFormat("%e %b, %Y", this.point.x)} ${
                this.point.y
              }:00:00 <b>null</b>`
            : `${Highcharts.dateFormat("%e %b, %Y", this.point.x)} ${
                this.point.y
              }:00:00 <b>${this.point.value.toFixed(
                2
              )} ${PHENOMENON_SYMBOL}</b>`;

        return headerString + pointString;
      },
    },

    series: [
      {
        data: formattedObsArrayForHeatmap,
        boostThreshold: 100,
        borderWidth: 0,
        nullColor: "#525252",
        colsize: 24 * 36e5, // one day
        turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
      },
    ],
  });
};

export { formatSensorThingsApiResponseForHeatMap, drawHeatMapHighcharts };
