"use strict";

import {
  BASE_URL,
  QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.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 {
  formatSensorThingsApiResponseForHeatMap,
  drawHeatMapHighcharts,
} from "./src_modules/chartHeatmap.mjs";

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

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

/**
 * Use the `vanillaDropDown` library to style the first level drop down list
 *
 * @returns {undefined}
 */
const styleLevelOneDropDown = 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 second level drop down list
 *
 * @returns {undefined}
 */
const styleLevelTwoDropDown = 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 level drop down list
 *
 * @returns {undefined}
 */
const styleLevelThreeDropDown = 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 fourth level drop down list
 *
 * @returns {undefined}
 */
const styleLevelFourDropDown = 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 () {
  styleLevelOneDropDown();
  styleLevelTwoDropDown();
  styleLevelThreeDropDown();
  styleLevelFourDropDown();
};

/**
 * Get the selected option(s) from a dropdown list
 *
 * @param {String} selectorStr A CSS selector string representing the dropdown list
 * @returns {Array} An array of string(s) representing the value(s) of the selected `<option>` elements
 */
const getSelectedOptionsFromDropDownList = function (selectorStr) {
  // Array to store our final result
  const selectedOptionsArr = [];

  // Select all the matching <option> elements as a NodeList
  const optionElements = document.querySelectorAll(`${selectorStr} option`);

  optionElements.forEach((optionEl) => {
    if (optionEl.selected) {
      selectedOptionsArr.push(optionEl.value);
    }
  });

  return selectedOptionsArr;
};

/**
 * Process the selected option(s) from a buildings & data points dropdown list.
 * This is currently the first dropdown list in the UI.
 *
 * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
 * @returns {Array} An array of string(s) representing the processed value(s) of the selected buildings & data points option(s)
 */
const processSelectionsFromBuildingDataPointOptions = function (
  selectedOptionsArr
) {
  // Array to store our final result
  const selectedOptionsBuildingDataPointArr = [];

  selectedOptionsArr.forEach((optionStr) => {
    if (optionStr.includes("/")) {
      // Case 1: <option> element's value CONTAINS a "/" character
      // We wish to create a string like this `Bau 101/VL`

      // Split the <option> element's value into two substrings
      const optionsStrPartOne = optionStr.slice(0, 3);
      const optionsStrPartTwo = optionStr.slice(3);

      // Create a new string for the first substring
      const optionsStrPartOneNew = `Bau ${optionsStrPartOne}`;

      // Create a new combined string
      const optionsStrNew = optionsStrPartOneNew + optionsStrPartTwo;

      selectedOptionsBuildingDataPointArr.push(optionsStrNew);
    } else {
      // Case 2: <option> element's value DOES NOT CONTAIN a "/" character
      // We wish to create a string like this `Other/Außentemp`
      selectedOptionsBuildingDataPointArr.push(`Other/${optionStr}`);
    }
  });

  return selectedOptionsBuildingDataPointArr;
};

/**
 * Split an option element's value (a string) using a forward slash character ("/") as the delimiter
 *
 * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
 * @returns {Array} An array made up of resulting strings after splitting
 */
const splitOptionsTextDelimitedBySlash = function (selectedOptionsArr) {
  return selectedOptionsArr.map((selectedOption) => selectedOption.split("/"));
};

/**
 * Get the values from the currently selected options in ALL the drop down lists
 * @returns {Array} An array containing four arrays, where each array contains the values of the selected options
 */
const getSelectedOptionsFromAllDropDownLists = function () {
  const selectedBuildingDataPointOptionsArr =
    processSelectionsFromBuildingDataPointOptions(
      getSelectedOptionsFromDropDownList("#drop-down--bldg-data-point")
    );

  // Separate the building ID from the data point
  const selectedBuildingDataPointOptionsSplitArr =
    splitOptionsTextDelimitedBySlash(selectedBuildingDataPointOptionsArr);

  const selectedAggregationOptionsArr = getSelectedOptionsFromDropDownList(
    "#drop-down--aggregation-type"
  );

  const selectedSamplingRateArr = getSelectedOptionsFromDropDownList(
    "#drop-down--sampling-rate"
  );

  const selectedChartTypeArr = getSelectedOptionsFromDropDownList(
    "#drop-down--chart-type"
  );

  // Ensure that all the options have at least one selection

  if (
    selectedBuildingDataPointOptionsSplitArr.length === 0 ||
    selectedAggregationOptionsArr.length === 0 ||
    selectedSamplingRateArr.length === 0 ||
    selectedChartTypeArr.length === 0
  )
    return;

  return [
    selectedBuildingDataPointOptionsSplitArr,
    selectedAggregationOptionsArr,
    selectedSamplingRateArr,
    selectedChartTypeArr,
  ];
};

/**
 * Get the abbreviated form of building IDs, phenomenon names and sensor sampling rates
 * @param {String} buildingFullForm A string representation of the full form of a building ID
 * @param {String} phenomenonFullForm A string representation of the full form of a phenomenon name
 * @param {String} samplingRateFullForm A string representation of the full form of a sensor's sampling rate
 * @returns {Array} An array of abbreviated strings
 */
const getBuildingSensorSamplingRateAbbreviation = function (
  buildingFullForm,
  phenomenonFullForm,
  samplingRateFullForm
) {
  const fullFormToAbbreviationMapping = {
    buildings: {
      "Bau 101": "101",
      "Bau 102": "102",
      "Bau 107": "107",
      "Bau 112": "112, 118",
      "Bau 125": "125",
      "Bau 225": "225",
      "Other": "weather_station_521",
    },
    phenomenon: {
      VL: "vl",
      RL: "rl",
      dT: "dT",

      Durchfluss: "flow",
      Leistung: "power",
      Energie: "energy",
      Energie_VERBR: "energy_verb",
      Außentemp: "outside_temp",
    },
    samplingRate: {
      "15 min": "15min",
      "60 min": "60min",
    },
  };

  if (
    fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm] === undefined
  )
    throw new Error(
      "The provided building ID is not valid or is not supported by this function"
    );

  if (
    fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm] ===
    undefined
  )
    throw new Error(
      "The provided data point is not valid or is not supported by this function"
    );

  if (
    fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm] ===
    undefined
  )
    throw new Error(
      "The provided sampling rate is not valid or is not supported by this function"
    );

  const buildingAbbrev =
    fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm];

  const phenomenonAbbrev =
    fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm];

  const samplingRateAbbrev =
    fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm];

  return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev];
};

/**
 * Get the abbreviated form for the currently selected options in ALL the drop down lists
 *
 * @param {Array} allSelectedOptionsArr An array containing four arrays, where each array contains the values of the selected options
 * @returns {Array} An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s)
 */
const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
  allSelectedOptionsArr
) {
  // Note: The sampling rate array is the third array, therefore we skip one element
  const [selectedBuildingDataPointOptionsSplitArr, , selectedSamplingRateArr] =
    allSelectedOptionsArr;

  // The building is the first element
  const selectedBuildingsArr = selectedBuildingDataPointOptionsSplitArr.map(
    (selectedBuildingDataPoint) => selectedBuildingDataPoint[0]
  );

  // The data point is the second element
  const selectedDataPointsArr = selectedBuildingDataPointOptionsSplitArr.map(
    (selectedBuildingDataPoint) => selectedBuildingDataPoint[1]
  );

  // Assume that the buildings and data points arrays have equal length
  // use one of the arrays for looping
  if (selectedBuildingsArr.length !== selectedDataPointsArr.length)
    throw new Error(
      "The buildings array and data points array have different lengths"
    );

  return selectedBuildingsArr.map((selectedBuilding, i) =>
    getBuildingSensorSamplingRateAbbreviation(
      selectedBuilding,
      selectedDataPointsArr[i],
      ...selectedSamplingRateArr
    )
  );
};

/**
 * Callback function for chart selection using drop down list
 * @returns {undefined}
 */
const selectChartTypeFromDropDown = async function () {
  try {
    const selectedOptions = getSelectedOptionsFromDropDownLists();

    if (selectedOptions === undefined) return;

    const selectedOptionsAbbreviationsArr =
      getBuildingSensorSamplingRateAbbreviation(...selectedOptions);

    const selectedChartType = document.querySelector(
      "#drop-down--chart-type"
    ).value;

    if (selectedChartType === "--Select--") return;

    // Display the loading indicator
    showLoadingSpinner();

    // The `getMetadataPlusObservationsFromSingleOrMultipleDatastreams` function expects a nested array structure
    const abbreviationsNestedArr = [selectedOptionsAbbreviationsArr];

    const observationsPlusMetadata =
      await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
        BASE_URL,
        QUERY_PARAMS_COMBINED,
        abbreviationsNestedArr
      );

    // Extract the combined arrays for observations and metadata
    const [observationsNestedArr, metadataNestedArr] = observationsPlusMetadata;

    // Create formatted array(s) for observations - line chart
    const formattedObsLineChartNestedArr = observationsNestedArr.map(
      (observationsArr) =>
        formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
    );

    // Create formatted array(s) for observations - heatmap
    const formattedObsHeatMapNestedArr = observationsNestedArr.map(
      (observationsArr) =>
        formatSensorThingsApiResponseForHeatMap(observationsArr)
    );

    // Create formatted array(s) for metadata - same for both chart types
    const formattedMetadataArr = metadataNestedArr.map((metadataObj) =>
      formatDatastreamMetadataForChart(metadataObj)
    );

    // Extract the formatted metadata properties
    const extractedFormattedDatastreamProperties =
      extractPropertiesFromFormattedDatastreamMetadata(
        formattedMetadataArr,
        false
      );

    if (selectedChartType === "Line") {
      drawLineChartHighcharts(
        formattedObsLineChartNestedArr,
        extractedFormattedDatastreamProperties
      );
    } else if (selectedChartType === "Heatmap") {
      // First need to extract the formatted observations from the nested array
      // Heatmap only needs one set of formatted observation values
      drawHeatMapHighcharts(
        ...formattedObsHeatMapNestedArr,
        extractedFormattedDatastreamProperties
      );
    }
  } catch (err) {
    console.error(err);
  } finally {
    // Hide the loading indicator
    hideLoadingSpinner();
  }
};

document.addEventListener("DOMContentLoaded", afterDocumentLoads);

document
  .querySelector("#drop-down--chart-type")
  .addEventListener("change", selectChartTypeFromDropDown);
