"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";

const buildingsAvailableSensorsArr = [
  ["--Select--", "", ""],

  ["Bau 101", "Vorlauftemperatur", "15 min"],
  ["Bau 101", "Vorlauftemperatur", "60 min"],
  ["Bau 101", "Rücklauftemperatur", "15 min"],
  ["Bau 101", "Rücklauftemperatur", "60 min"],

  ["Bau 102", "Vorlauftemperatur", "15 min"],
  ["Bau 102", "Vorlauftemperatur", "60 min"],
  ["Bau 102", "Rücklauftemperatur", "15 min"],
  ["Bau 102", "Rücklauftemperatur", "60 min"],

  ["Bau 107", "Vorlauftemperatur", "15 min"],
  ["Bau 107", "Vorlauftemperatur", "60 min"],
  ["Bau 107", "Rücklauftemperatur", "15 min"],
  ["Bau 107", "Rücklauftemperatur", "60 min"],

  ["Bau 112", "Vorlauftemperatur", "15 min"],
  ["Bau 112", "Vorlauftemperatur", "60 min"],
  ["Bau 112", "Rücklauftemperatur", "15 min"],
  ["Bau 112", "Rücklauftemperatur", "60 min"],

  ["Bau 125", "Vorlauftemperatur", "15 min"],
  ["Bau 125", "Vorlauftemperatur", "60 min"],
  ["Bau 125", "Rücklauftemperatur", "15 min"],
  ["Bau 125", "Rücklauftemperatur", "60 min"],

  ["Bau 225", "Vorlauftemperatur", "15 min"],
  ["Bau 225", "Vorlauftemperatur", "60 min"],
  ["Bau 225", "Rücklauftemperatur", "15 min"],
  ["Bau 225", "Rücklauftemperatur", "60 min"],
  ["Bau 225", "Durchfluss", "15 min"],
  ["Bau 225", "Durchfluss", "60 min"],
  ["Bau 225", "Leistung", "15 min"],
  ["Bau 225", "Leistung", "60 min"],
  ["Bau 225", "Energie", "15 min"],
  ["Bau 225", "Energie", "60 min"],
  ["Bau 225", "Energie_VERBR", "15 min"],
  ["Bau 225", "Energie_VERBR", "60 min"],
];

/**
 * Get the unique values from an array
 * @param {Array} dataArr Input array
 * @param {Number} index Integer representing a zero-based index of the columns in the array
 * @returns {Array} An array of unique options
 */
const getUniqueValues = function (dataArr, index) {
  const uniqueOptions = new Set();

  dataArr.forEach((row) => uniqueOptions.add(row[index]));

  return [...uniqueOptions];
};

/**
 * Populate the HTML elements that make up a drop down list
 * @param {Object} element HTMLElement object of a drop down list element
 * @param {Array} uniqueValuesArr An array of unique values
 * @returns {undefined}
 */
const populateDropDown = function (element, uniqueValuesArr) {
  element.innerHTML = "";

  uniqueValuesArr.forEach((item) => {
    const option = document.createElement("option");
    option.textContent = item;
    element.appendChild(option);
  });
};

/**
 * Filter an array using filter strings of variable length
 * @param {Array} dataArr Input array
 * @param {Array} filtersAsArray An array of filter strings
 * @returns {Array} An array that contains the filter result
 */
const filterArray = function (dataArr, filtersAsArray) {
  return dataArr.filter((row) =>
    filtersAsArray.every((filterItem, i) => filterItem === row[i])
  );
};

/**
 * Create a drop down list in the HTML document
 * @param {Array} dataArr Input array
 * @param {Array} filtersAsArray An array of strings tp be used as filters
 * @param {Object} targetElement HTMLElement object of a drop down list element
 * @returns {undefined}
 */
const makeDropDown = function (dataArr, filtersAsArray, targetElement) {
  const filteredArr = filterArray(dataArr, filtersAsArray);

  const uniqueList = getUniqueValues(filteredArr, filtersAsArray.length);

  getUniqueValues(dataArr);

  populateDropDown(targetElement, uniqueList);
};

/**
 * Use the `makeDropDown` function to create the first two levels of the linked drop down lists
 * @returns {undefined}
 */
const applyDropDown = function () {
  const selectLevel1Value = document.querySelector("#drop-down--bldg").value;
  const selectLevel2 = document.querySelector("#drop-down--sensor");
  makeDropDown(buildingsAvailableSensorsArr, [selectLevel1Value], selectLevel2);

  applyDropDown2();
};

/**
 * Use the `makeDropDown` function to create the third level of the linked drop down lists
 * @returns {undefined}
 */
const applyDropDown2 = function () {
  const selectLevel1Value = document.querySelector("#drop-down--bldg").value;
  const selectLevel2Value = document.querySelector("#drop-down--sensor").value;
  const selectLevel3 = document.querySelector("#drop-down--sampling-rate");
  makeDropDown(
    buildingsAvailableSensorsArr,
    [selectLevel1Value, selectLevel2Value],
    selectLevel3
  );
};

/**
 * Use the `populateDropDown` function to populate the first level drop down
 * @returns {undefined}
 */
const populateFirstLevelDropDown = function () {
  const el = document.querySelector("#drop-down--bldg");
  const uniqueList = getUniqueValues(buildingsAvailableSensorsArr, 0);
  populateDropDown(el, uniqueList);
};

document
  .querySelector("#drop-down--bldg")
  .addEventListener("change", applyDropDown);
document
  .querySelector("#drop-down--sensor")
  .addEventListener("change", applyDropDown2);

// These functions run after "DOMContentLoaded" event
populateFirstLevelDropDown();
applyDropDown();

/**
 * Get the values from the currently selected options in the linked drop dpwn lists
 * @returns {Array} An array containing the values of the selected options
 */
const getSelectedOptionsFromDropDownLists = function () {
  const selectedBuilding = document.querySelector("#drop-down--bldg").value;
  const selectedSensor = document.querySelector("#drop-down--sensor").value;
  const selectedSamplingRate = document.querySelector(
    "#drop-down--sampling-rate"
  ).value;

  if (
    selectedBuilding === "--Select--" ||
    selectedSensor === "" ||
    selectedSamplingRate === ""
  )
    return;

  return [selectedBuilding, selectedSensor, selectedSamplingRate];
};

/**
 * 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",
    },
    phenomenon: {
      Vorlauftemperatur: "vl",
      Rücklauftemperatur: "rl",
      Durchfluss: "flow",
      Leistung: "power",
      Energie: "energy",
      Energie_VERBR: "energy_verb",
    },
    samplingRate: {
      "15 min": "15min",
      "60 min": "60min",
    },
  };

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

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

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

  return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev];
};

/**
 * 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
  .querySelector("#drop-down--chart-type")
  .addEventListener("change", selectChartTypeFromDropDown);
