"use strict";

import {
  BASE_URL,
  QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs";

import { calculateVorlaufMinusRuecklaufTemperature } from "./src_modules/calculateTemperatureDiff.mjs";

import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./src_modules/fetchData.mjs";

import {
  formatDatastreamMetadataForChart,
  extractPropertiesFromFormattedDatastreamMetadata,
} from "./src_modules/fetchedDataProcessing.mjs";

import {
  formatSensorThingsApiResponseForLineOrColumnChart,
  drawLineChartHighcharts,
} from "./src_modules/chartLine.mjs";

import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";

import {
  showLoadingSpinner,
  hideLoadingSpinner,
  disableDrawChartButton,
  enableDrawChartButton,
} from "./src_modules/loadingIndicator.mjs";

import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";

import {
  extractObservationsWithinDatesInterval,
  extractUniqueCalendarDatesFromTimestamp,
} from "./src_modules/aggregateHelpers.mjs";

import {
  splitMultipleOptionsTextDelimitedBySlash,
  getSelectedOptionsFromAllDropDownLists,
  checkIfSelectedOptionsContainTemperatureDifference,
  deleteTemperatureDifferenceOptions,
  extractTemperatureDifferenceOptions,
  extractBuildingPlusSamplingRate,
  checkIfSelectedBuildingDataPointsOptionsAreValid,
  checkIfSelectedAggregationOptionsAreValid,
  getAbbreviationsForSelectedOptionsFromAllDropDownLists,
} from "./src_modules/dropDownListHelpers.mjs";

import {
  drawHeatmapBasedOnSelectedOptions,
  drawScatterPlotFromChartSelection,
  drawLineChartBasedOnSelectedAggregationOptions,
  drawColumnChartBasedOnSelectedAggregationOptions,
} from "./src_modules/dropDownListProcessing.mjs";

/**
 * Use the `vanillaDropDown` library to style the buildings & data points drop down list
 *
 * @returns {undefined}
 */
const styleBuildingsDataPointsDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`; supports the selection of multiple options
  new vanillaSelectBox("#drop-down--bldg-data-point", {
    "disableSelectAll": true,
    "maxSelect": 5,
    "placeHolder": "--Select--",
    "search": false,
  });
};

/**
 * Use the `vanillaDropDown` library to style the aggregation type drop down list
 *
 * @returns {undefined}
 */
const styleAggregationDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--aggregation-type", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
};

/**
 * Use the `vanillaDropDown` library to style the third sampling rate down list
 *
 * @returns {undefined}
 */
const styleSamplingRateDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--sampling-rate", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
};

/**
 * Use the `vanillaDropDown` library to style the chart type drop down list
 *
 * @returns {undefined}
 */
const styleChartTypeDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--chart-type", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
};

/**
 * Callback function that wraps the logic of populating the linked drop down lists.
 * Will run on `DOMContentLoaded` event
 *
 * @returns {undefined}
 */
const afterDocumentLoads = function () {
  styleBuildingsDataPointsDropDown();
  styleAggregationDropDown();
  styleSamplingRateDropDown();
  styleChartTypeDropDown();
};

/**
 * Callback function that draws a chart using options from the drop-down list that
 * have been selected by a user.
 * Will be run when the user clicks a button
 *
 * @async
 * @returns {undefined} undefined
 */
const drawChartUsingSelectedOptions = async function () {
  try {
    const selectedOptionsAllDropDownLists =
      getSelectedOptionsFromAllDropDownLists();

    // Note: The chart type amd aggregation type + duration are the first and
    // third elements respectively, we have ignored the second and fourth elements
    const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
      selectedOptionsAllDropDownLists;

    // Create an array of aggregation type and duration
    const selectedAggregationTypeDurationSplitNestedArr =
      splitMultipleOptionsTextDelimitedBySlash(
        selectedAggregationTypeDurationArr
      );

    // Separate the aggregation type and the aggregation duration strings
    const [selectedAggregationTypeDurationSplitArr] =
      selectedAggregationTypeDurationSplitNestedArr;

    const [selectedAggregationType, selectedAggregationDuration] =
      selectedAggregationTypeDurationSplitArr;

    // Array of building(s) + data point(s) + sampling rate
    const selectedBuildingsDataPointsSamplingRateAbbrevNestedArr =
      getAbbreviationsForSelectedOptionsFromAllDropDownLists(
        selectedOptionsAllDropDownLists
      );

    // The values of these references is either equal to `true` or an error will be thrown
    // We would like the errors to be thrown at this point before we begin performing any asynchronous tasks
    const selectedAggregationOptionsAreValid =
      checkIfSelectedAggregationOptionsAreValid(
        selectedChartTypeArr,
        selectedAggregationType,
        selectedAggregationDuration
      );

    const selectedBuildingDataPointsOptionsAreValid =
      checkIfSelectedBuildingDataPointsOptionsAreValid(
        selectedChartTypeArr,
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
      );

    // Check whether we have dT (temperature difference), if so, delete these options,
    // then compute abbreviations for raw observations
    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
      checkIfSelectedOptionsContainTemperatureDifference(
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
      )
        ? deleteTemperatureDifferenceOptions(
            selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
          )
        : selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;

    // Check if we have dT (temperature difference)
    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
      checkIfSelectedOptionsContainTemperatureDifference(
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
      )
        ? extractTemperatureDifferenceOptions(
            selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
          )
        : [];

    // Display the loading indicator
    showLoadingSpinner();

    // Disable the 'draw chart' button
    disableDrawChartButton();

    // Fetch the observations + metadata / raw observations
    const observationsRawPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
        ? [[], []]
        : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
            selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
          );

    // Fetch the observations + metadata / temperature difference (dT)
    const observationsTempDiffPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
        ? [[], []]
        : await calculateVorlaufMinusRuecklaufTemperature(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
            extractBuildingPlusSamplingRate(
              selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
            )
          );

    // If there is an error in fetching metadata + observations (Case 1: raw observations)
    // the returned array will have this structure: [[undefined, undefined], undefined]
    // Note that the second element is not an array as we would expect but is a
    // a single `undefined` value
    if (typeof observationsRawPlusMetadataArr[0][0] === "undefined") {
      throw new Error(
        `There was a problem in fetching metadata and observations`
      );
    }

    // If there is an error in fetching metadata + observations (Case 2: temperature difference, dT)
    // a single `undefined` value instead of an array (as we would expect) will be returned
    if (typeof observationsTempDiffPlusMetadataArr === "undefined")
      throw new Error(
        `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations`
      );

    // Extract the combined arrays for observations and metadata / raw observations
    const [observationsRawNestedArr, metadataRawNestedArr] =
      observationsRawPlusMetadataArr;

    // Extract the combined arrays for observations and metadata / temperature difference (dT)
    const [observationsTempDiffNestedArr, metadataTempDiffNestedArr] =
      observationsTempDiffPlusMetadataArr;

    // Create a combined array of observations and metadata
    const observationsPlusMetadataCombined = [
      [...observationsRawNestedArr, ...observationsTempDiffNestedArr],
      [...metadataRawNestedArr, ...metadataTempDiffNestedArr],
    ];

    const [observationsComboNestedArr, metadataComboNestedArr] =
      observationsPlusMetadataCombined;

    // Create formatted array(s) for metadata - used by ALL chart types
    const formattedMetadataNestedArr = metadataComboNestedArr.map(
      (metadataObj) => formatDatastreamMetadataForChart(metadataObj)
    );

    // Extract the formatted metadata properties for the raw observations - used by ALL chart types
    const rawObservationsExtractedFormattedDatastreamProperties =
      extractPropertiesFromFormattedDatastreamMetadata(
        formattedMetadataNestedArr,
        false
      );

    // The formatted abbreviations array is nested
    const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
      selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;

    // Extract the formatted sampling rate string - used by ALL chart types
    const [, , selectedSamplingRateAbbrev] =
      selectedBuildingsDataPointsSamplingRateAbbrevArr;

    // User-specified start date and end date for aggregation - used by MULTIPLE chart types
    const aggregationStartDate = "2020-01-01";
    const aggregationEndDate = "2020-12-31";

    // Create final array of observations- used by MULTIPLE chart types
    // If we are performing aggregation, it was envisioned that the user would
    // select observations that fall within a start and end date
    const observationsComboNestedFinalArr =
      selectedAggregationType === "None (raw data)"
        ? observationsComboNestedArr
        : observationsComboNestedArr.map((observationsArr) =>
            extractObservationsWithinDatesInterval(
              observationsArr,
              selectedSamplingRateAbbrev,
              aggregationStartDate,
              aggregationEndDate
            )
          );

    // Unique calendar dates - used by MULTIPLE chart types
    const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
      (observationsArr) =>
        extractUniqueCalendarDatesFromTimestamp(observationsArr)
    );

    for (const selectedChartType of selectedChartTypeArr) {
      switch (selectedChartType) {
        // We are interested in raw observations ONLY
        case "Heatmap":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawHeatmapBasedOnSelectedOptions(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          break;

        // We are interested in raw observations ONLY
        case "Scatter Plot":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawScatterPlotFromChartSelection(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          break;

        // We are interested in BOTH raw observations and aggregated observations
        case "Line":
          // Raw observations
          if (selectedAggregationType === "None (raw data)") {
            // Create formatted array(s) for observations
            const formattedRawObservationsLineChartNestedArr =
              observationsComboNestedFinalArr.map((observationsArr) =>
                formatSensorThingsApiResponseForLineOrColumnChart(
                  observationsArr
                )
              );

            drawLineChartHighcharts(
              formattedRawObservationsLineChartNestedArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          // Aggregated observations
          else {
            drawLineChartBasedOnSelectedAggregationOptions(
              selectedAggregationType,
              selectedAggregationDuration,
              observationsComboNestedFinalArr,
              selectedSamplingRateAbbrev,
              uniqueCalendarDatesNestedArr,
              formattedMetadataNestedArr
            );
          }
          break;

        // We are interested in BOTH raw observations and aggregated observations
        case "Column":
          // Raw observations
          if (selectedAggregationType === "None (raw data)") {
            // Create formatted array(s) for observations
            const formattedRawObservationsColumnChartNestedArr =
              observationsComboNestedFinalArr.map((observationsArr) =>
                formatSensorThingsApiResponseForLineOrColumnChart(
                  observationsArr
                )
              );

            drawColumnChartHighcharts(
              formattedRawObservationsColumnChartNestedArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          // Aggregated observations
          else {
            drawColumnChartBasedOnSelectedAggregationOptions(
              selectedAggregationType,
              selectedAggregationDuration,
              observationsComboNestedFinalArr,
              selectedSamplingRateAbbrev,
              uniqueCalendarDatesNestedArr,
              formattedMetadataNestedArr
            );
          }
          break;

        default:
          throw new Error("None of the chart type options were selected");
      }
    }
  } catch (err) {
    console.error(err);

    // Display a dialog window with the error message
    alert(err);
  } finally {
    // Enable the 'draw chart' button
    enableDrawChartButton();

    // Hide the loading indicator
    hideLoadingSpinner();
  }
};

document.addEventListener("DOMContentLoaded", afterDocumentLoads);

document
  .querySelector("#btn-draw-chart")
  .addEventListener("click", drawChartUsingSelectedOptions);