import sampleData1 from './SampleData1.mjs';
import sampleData2 from './SampleData2.mjs';
import PDFOptionsDialog from './PDFOptionsDialog.mjs';
import EditResourceDialog from './EditResourceDialog.mjs';
import { default as d3_contextMenu } from 'd3-context-menu';

import {
   VSWidget,
   ObjectType,
   RowDragModes,
   TargetPositions,
   ViewType,
   VisualType,
} from 'nwaf_rab';

const viewTypeToNameMap = {
   [ViewType.Activities]: "Activities",
   [ViewType.Resources]: "Resources",
   [ViewType.Loads]: "Loads",
   [ViewType.SkilledResources]: "SkilledResources",
};

let setDisabled = (elem, newValue) => {
   if (newValue)
      elem.setAttribute("disabled", "");
   else
      elem.removeAttribute("disabled");
};

class SampleApp {
   /**
    * Initializes the application and the VS Widget.
    */
   constructor () {
      // ATTENTION: Following code unfortunately is necessary for using the d3-context-menu with touch devices!
      document.addEventListener("contextmenu", e => {
         if ((e.pointerType === "touch") && e.target.closest(".d3-context-menu")) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
         }
      }, true);

      // instantiate VS Widget and handle resize events
      this.instantiateVSWidget();

      // instantiate the PDF options dialog and handles the save callback
      this.instantiatePDFOptionsDialog();

      // instantiate the edit resource dialog
      this.instantiateEditResourceDialog();

      // initialize toolbar
      this.registerToolbarClicks();

      // register handler for resize events of Window object
      this.registerWindowResizeEventHandler();

      const vsWidget = this._vsWidget;

      // add version number of widget to the title
      document.getElementById("title").innerText += ` ${vsWidget.option("version")}`;

      // load sample data (from variable sampleData1)
      this._objectMaps = {};
      this.loadData(sampleData1);

      vsWidget.fitTimeAreaIntoView(new Date(2025, 0, 6), new Date(2025, 0, 18));

      // disable buttons that make no sense when the resources view is open
      setDisabled(document.querySelector("#ScrollToActivity"), true);
      setDisabled(document.querySelector("#ScrollToEntity"), true);
   }

   /**
    * Instantiates the VS Widget and handles resize events.
    */
   instantiateVSWidget () {
      this._vsWidget?.destroy();

      // get DOM element that will be used for the VS Widget
      const vsWidgetDiv = document.querySelector("#VSWidget");

      // define initial options
      const vsWidgetOptions = {
         licenseKey: window.VSW_LICENSE_KEY
      };

      // instantiate widget and save reference to widget object into member variable _vsWidget
      const vsWidget = this._vsWidget = new VSWidget(vsWidgetDiv, vsWidgetOptions);

      // set callback handlers for all callbacks for logging purposes (see debugger console for output)
      const callbackNames = ["canSelect", "onSelectionChanged", "onClicked", "onDoubleClicked",
                           "canDrag", "onDrag", "onDragStart", "onDrop",
                           "onCollapseStateChanged", "onCurveCollapseStateChanged",
                           "onShowContextMenu", "onCloseContextMenu",
                           "onShowTooltip", "onTableCellDefinitionWidthChanged",
                           "onTimeAreaViewParametersChanged", "onVerticalScrollOffsetChanged"];

      callbackNames.forEach(callbackName => {
         vsWidget.option(callbackName, callbackArgs => {
            // call handler for all callbacks
            this.onCallbackTriggered(callbackName, callbackArgs);
         });
      });

      // try to find out, whether the local time zone is away from UTC-1, UTC, or UTC+1
      if (Math.abs(new Date(2000, 0, 1, 0, 0, 0).valueOf() - Date.UTC(2000, 0, 1, 0, 0, 0)) > 60 * 60 * 1000)
         document.querySelector("#HintsOnTimeZones").style.backgroundColor = "tomato";
   }

   /**
    * Instantiates the PDF options dialog.
    */
   instantiatePDFOptionsDialog () {
      this._pdfOptionsDialog = new PDFOptionsDialog(document.getElementById("PDFOptionsDialogContainer"));

      this._pdfOptions = {
         topHTML: "<div style='display:flex;flex-direction:row;height:100%;'>" +
                     "<div style='width:15%;text-align:left'></div>" +
                     "<div style='display:flex;flex-direction:column;width:70%'>" +
                        "<div style='text-align:center;font-size:14px;font-weight:bold'>Title</div>" +
                        "<div style='text-align:center;'>Subtitle</div>" +
                     "</div>" +
                     "<div style='width:15%;background-image:url(images/boyumIT_Bring-logo-RGB-color-onWhite.svg);background-position:right;background-size:contain;background-repeat:no-repeat;'></div>" +
                  "</div>",
         bottomHTML: "<div style='display:flex;flex-direction:row;height:100%;font-size:10px;'>" +
                        "<div style='width:20%;text-align:left'></div>" +
                        "<div style='width:60%;text-align:center'>{{#PageNo}}/{{#PageCount}}</div>" +
                        "<div style='width:20%;text-align:right'>{{#Date}}</div>" +
                     "</div>"
      };
   }

   /**
    * Instantiates the edit resource dialog.
    */
   instantiateEditResourceDialog () {
      this._editResourceDialog = new EditResourceDialog(document.getElementById("EditResourceDialogContainer"));
   }

   /**
    * Handles each callback that was triggered by the VS Widget.
    *
    * @param {string} callbackName Name of the callback.
    * @param {Object} callbackArgs Original callbackArgs object.
    */
   onCallbackTriggered (callbackName, callbackArgs) {
      // copy callbackArgs object and delete properties that cannot be put to the console
      const callbackArgsToLog = {...callbackArgs};
      delete callbackArgsToLog.event;

      // log the callback including arguments to the console
      // eslint-disable-next-line no-console
      console.log(`Callback '${callbackName}'(${JSON.stringify(callbackArgsToLog)}) was triggered.\n`);

      const popupText = `<i class="fas fa-envelope"></i> Callback '${callbackName}' was triggered!`;
      if (callbackName !== "onShowTooltip") { // exclude onShowTooltip because of its frequency
         // show callback name in a popup
         this.showSimplePopup(popupText);
      }

      // handle some callbacks for this application
      switch (callbackName) {
         case "onShowTooltip": {
            const vsWidget = this._vsWidget;

            if (callbackArgs.tooltipTemplate?.HTMLFormat?.includes("{{?TTT}}")) {
               // especially for SampleData2 (feature sample)
               let text = callbackArgs.object._CustomField1;
               text = text.split("°").map((item) => {
                  let listElementType, attributes = "";
                  if (item[0] === "§") {
                     listElementType = "span";
                     item = item.slice(1);
                  }
                  else {
                     listElementType = "li";
                     attributes = ` style="margin-left: 17px;"`;
                  }
                  item = item.replace(/#(.*)#/g, '<a href="$1" target="_blank">$1</a>');
                  return `<${listElementType}${attributes}>${item}</${listElementType}>`;
               }).join("");
               const applicationVariablesMap = vsWidget.option("applicationVariablesMap") ?? {};
               applicationVariablesMap.TTT = text;
               vsWidget.option("applicationVariablesMap", applicationVariablesMap);
            }
            else
               // only as an example
               callbackArgs.innerHTML = `${popupText} (${callbackArgs.object ? `ID: ${callbackArgs.object.ID}` : ""}${(callbackArgs.visualType === VisualType.Curve) ? `<br/>${callbackArgs.date}` : ""})`;
            break;
         }

         case "onDrop":
            this.handleOnDrop(callbackArgs, popupText);
            break;

         case "onShowContextMenu":
            this.handleOnShowContextMenu(callbackArgs);
            break;

         case "onCloseContextMenu":
            this.handleOnCloseContextMenu();
            break;
      }
   }

   /**
    * Shows a simple popup for a short time.
    * 
    * @param {string} text Text to display in the popup.
    * @param {boolean} onSecondLine If true, then show popup in second line.
    */
   showSimplePopup (text, onSecondLine) {
      // show the text in a temporary popup.
      const popupDiv = document.createElement("div");
      popupDiv.innerHTML = text;
      popupDiv.className = `popuptext popuptext-fadein${onSecondLine ? " popuptext-secondline" : ""}`;
      document.querySelector("#messages").appendChild(popupDiv);

      // remove the popup after 2 seconds.
      window.setTimeout(() => {
         popupDiv.remove();
      }, 2000);
   }

   /**
    * Handles dropped objects.
    */
   handleOnDrop (callbackArgs, popupText) {
      const dropFunc = function (/* callbackArgs */) {
         // The functionality is highly specific to applications, so no functionality implemented here!
         this.showSimplePopup("<i class=\"fas fa-exclamation-triangle\"></i> No business logic implemented for callback onDrop!", true);
      };

      if (document.querySelector("#SwitchPromise").classList.contains("toolbar-button-highlighted")) {
         // example for using a Promise object (e.g. for asynchronous requests to application server)
         callbackArgs.promise = new Promise((resolve/*, reject*/) => {
            this.showSimplePopup(`${popupText} (<i class="fas fa-hourglass-half"></i> Promise started with 3 seconds delay)`);
            setTimeout(() => {
               this.showSimplePopup(`${popupText} (<i class="fas fa-hourglass-half"></i> Promise now resolved)`);
               resolve();
               dropFunc.call(this, callbackArgs);
            }, 3000);
         });
      }
      else {
         dropFunc.call(this, callbackArgs);
      }
   }

   /**
    * Shows or hides a context menu.
    *
    * @param {Object} callbackArgs If given, then show an appropriate context menu. Else hide the current one.
    */
   handleOnShowContextMenu (callbackArgs) {
      const vsWidget = this._vsWidget;

      // example of handling a context menu (using d3-context-menu, see https://github.com/patorjk/d3-context-menu)
      // close eventually opened context menu
      this.handleOnCloseContextMenu();

      let contextMenuItems;
      let contextMenuDoneFunction;

      // check if triggered on an allocation object
      if (callbackArgs.objectType === ObjectType.Allocation) {
         const allocation = callbackArgs.object;

         if (allocation.Entries?.[0]) {
            // show a context menu
            contextMenuItems = [
               { title: '<div>Change color of last entry</div>', action: () => contextMenuDoneFunction('change_color') },
               { title: '<span style="color: darkred;"><i class="fas fa-exclamation-triangle"></i> Context menus are not part of VSW!</span>' }
            ];

            // handle action chosen by user
            contextMenuDoneFunction = cmd => {
               if (cmd === 'change_color') {
                  const copyOfAllocation = {...allocation};
                  copyOfAllocation.Entries = allocation.Entries.map(entry => ({...entry}));
                  const entryIndex = allocation.Entries.length - 1;
                  if (entryIndex >= 0) {
                     const colors = ["lime", "deeppink", "deepskyblue"];
                     const colorIndex = (colors.indexOf(copyOfAllocation.Entries[entryIndex].Color) + 1) % colors.length;
                     copyOfAllocation.Entries[entryIndex].Color = colors[colorIndex];
                     vsWidget.updateAllocations([copyOfAllocation]);
                     vsWidget.render();
                  }
               }
            };
         }
      }
      // check if triggered on an resource object
      else if ((callbackArgs.objectType === ObjectType.Resource) && (callbackArgs.visualType === VisualType.Row)) {
         const resource = callbackArgs.object;

         contextMenuItems = [
            { title: '<div>Edit application specific resource properties for grouping...</div>', action: () => contextMenuDoneFunction('edit_resource_code') },
            { title: '<span style="color: darkred;"><i class="fas fa-exclamation-triangle"></i> Context menus are not part of VSW!</span>' }
         ];

         contextMenuDoneFunction = cmd => {
            if (cmd === 'edit_resource_code') {
               const copyOfResource = {...resource};
               const dialogPromise = this._editResourceDialog.showModal(copyOfResource);
               dialogPromise.then(() => {
                  vsWidget.updateResources([copyOfResource]);
                  vsWidget.render();
               });
            }
         };
      }

      if (contextMenuDoneFunction && contextMenuItems) {
         const promise = new Promise((resolve/*, reject*/) => {
            // show a context menu
            d3_contextMenu(contextMenuItems, {
               onClose: resolve, // inform VSW about closing the context menu
            })(callbackArgs.event);
         });

         // inform VSW about closing the context menu later (see above)
         callbackArgs.promise = promise;
      }
   }

   /**
    * Hides a context menu.
    */
   handleOnCloseContextMenu () {
      // hide context menu if open
      d3_contextMenu("close");
   }

   /**
    * Loads the data that is hosted in the variable "currentSampleData" into the VS Widget.
    *
    * @param sampleData The sample data to load
    */
   loadData (sampleData) {
      const vsWidget = this._vsWidget;

      vsWidget.option(sampleData.options ?? {});

      // make all objects from the sample data known to the VS widget.
      // Note: Due to enhance the loading performance, the objects should be added in the order defined in Widget chapter of IDD.
      const objectArrayNames = ["Symbols", "DateLines", "TooltipTemplates", "TableRowDefinitions", "Calendars", "PeriodHighlighters", "Curves", "Skills",
         "Resources", "Activities", "Allocations", "Links", "Entities", "HierarchySupplementaryDefinitions"];
      
      objectArrayNames.forEach(objectArrayName => {
         if (Array.isArray(sampleData[objectArrayName])) {
            vsWidget[`add${objectArrayName}`](sampleData[objectArrayName]);

            // in order to have fast access by ID for each object type later, maps are filled
            this._objectMaps[objectArrayName] = Object.fromEntries(Object.entries(sampleData[objectArrayName]).map(([_key, value]) => [value.ID, value])) || {};
         }
         else
            this._objectMaps[objectArrayName] = {};
      });

      // do not forget to call this method after each modification to the object model!
      vsWidget.render();
   }

   /**
    * Registers a handler for resize events of Window object.
    * Note: The VS Widget resizes itself automatically to fill the DIV element it is hosted in.
    */
   registerWindowResizeEventHandler () {

      // handle resizes of the DOM element that hosts the VS Widget (in this case fills the space below the ribbon)
      const resizeFunc = () => {
         const contentDiv = document.querySelector("#content");
         contentDiv.style.height = `${document.body.clientHeight - contentDiv.getBoundingClientRect().top}px`;
      };

      // register for resize events of body object
      new ResizeObserver(resizeFunc).observe(document.body);

      // process first resize (otherwise height is 0)
      resizeFunc();
   }

   handleSwitchAllocationRowsButton () {
      const vsWidget = this._vsWidget;

      const switchAllocationRowsClassList = document.querySelector("#SwitchAllocationRows").classList;

      if (vsWidget.option("viewType") === ViewType.Resources) {
         if (vsWidget.option("allocationRowsVisibleInResourcesView")) {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
            switchAllocationRowsClassList.add("toolbar-button-highlighted");
         }
         else {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
         }
      }
      if (vsWidget.option("viewType") === ViewType.SkilledResources) {
         if (vsWidget.option("allocationRowsVisibleInSkilledResourcesView")) {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
            switchAllocationRowsClassList.add("toolbar-button-highlighted");
         }
         else {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
         }
      }
      else if (vsWidget.option("viewType") === ViewType.Activities) {
         if (vsWidget.option("allocationRowsVisibleInActivitiesView")) {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
            switchAllocationRowsClassList.add("toolbar-button-highlighted");
         }
         else {
            switchAllocationRowsClassList.remove("toolbar-button-highlighted");
         }
      }   
      else if (vsWidget.option("viewType") === ViewType.Loads) {
         switchAllocationRowsClassList.remove("toolbar-button-highlighted");
      }   
   }
   
   /**
    * Registers click event handlers for each toolbar button.
    */
   registerToolbarClicks () {
      // --- toolbar buttons for switching the views ---

      document.querySelector("#ShowActView").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("viewType", ViewType.Activities);

         // handle states of toolbar buttons
         document.querySelector("#ShowActView").classList.add("toolbar-button-highlighted");
         document.querySelector("#ShowResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowSkilledResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowLoadsView").classList.remove("toolbar-button-highlighted");

         const loadSampleData2Highlighted = document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted");

         document.querySelector("#ShowEntitiesTable").classList.toggle("toolbar-button-highlighted",
            vsWidget.option("entitiesTableVisibleInActivitiesView") && !loadSampleData2Highlighted);
         setDisabled(document.querySelector("#ShowEntitiesTable"), !!loadSampleData2Highlighted);
         setDisabled(document.querySelector("#ShowTopViewArea"), !!loadSampleData2Highlighted);
         document.querySelector("#ShowTopViewArea").classList.toggle("toolbar-button-highlighted",
         vsWidget.option("topViewAreaVisibleInActivitiesView") && !loadSampleData2Highlighted);
         setDisabled(document.querySelector("#ShowWorldView"), false);
         setDisabled(document.querySelector("#ScrollToResource"), true);
         setDisabled(document.querySelector("#ScrollToAllocation"), true);
         setDisabled(document.querySelector("#ScrollToActivity"), !!loadSampleData2Highlighted);
         setDisabled(document.querySelector("#ScrollToEntity"), !!loadSampleData2Highlighted || !vsWidget.option("entitiesTableVisibleInActivitiesView"));
         setDisabled(document.querySelector("#SwitchVerticalRowDragging"), false);
         setDisabled(document.querySelector("#SwitchAllocationRows"), false);
         setDisabled(document.querySelector("#Grouping"), true);

         this.handleSwitchAllocationRowsButton();

         vsWidget.option("defaultAllocationTableRowDefinitionID", "AllocationInActivitiesViewTRD");
      });

      document.querySelector("#ShowResView").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("viewType", ViewType.Resources);

         // handle states of toolbar buttons
         document.querySelector("#ShowResView").classList.add("toolbar-button-highlighted");
         document.querySelector("#ShowSkilledResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowActView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowLoadsView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowEntitiesTable").classList.toggle("toolbar-button-highlighted",
            vsWidget.option("entitiesTableVisibleInResourcesView") && !document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowEntitiesTable"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowTopViewArea"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         document.querySelector("#ShowTopViewArea").classList.toggle("toolbar-button-highlighted",
            vsWidget.option("topViewAreaVisibleInResourcesView") && !document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowWorldView"), false);
         setDisabled(document.querySelector("#ScrollToResource"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ScrollToAllocation"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ScrollToActivity"), true);
         setDisabled(document.querySelector("#ScrollToEntity"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted") || !vsWidget.option("entitiesTableVisibleInResourcesView"));
         setDisabled(document.querySelector("#SwitchVerticalRowDragging"), false);
         setDisabled(document.querySelector("#SwitchAllocationRows"), false);
         setDisabled(document.querySelector("#Grouping"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));

         this.handleSwitchAllocationRowsButton();

         vsWidget.option("defaultAllocationTableRowDefinitionID", "AllocationInResourcesViewTRD");
      });

      document.querySelector("#ShowSkilledResView").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("viewType", ViewType.SkilledResources);

         // handle states of toolbar buttons
         document.querySelector("#ShowResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowSkilledResView").classList.add("toolbar-button-highlighted");
         document.querySelector("#ShowActView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowLoadsView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowEntitiesTable").classList.toggle("toolbar-button-highlighted",
            vsWidget.option("entitiesTableVisibleInSkilledResourcesView") && !document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowEntitiesTable"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowTopViewArea"), true);
         document.querySelector("#ShowTopViewArea").classList.remove("toolbar-button-highlighted");
         setDisabled(document.querySelector("#ShowWorldView"), false);
         setDisabled(document.querySelector("#ScrollToResource"), true);
         setDisabled(document.querySelector("#ScrollToAllocation"), true);
         setDisabled(document.querySelector("#ScrollToActivity"), true);
         setDisabled(document.querySelector("#ScrollToEntity"), true);
         setDisabled(document.querySelector("#SwitchVerticalRowDragging"), true);
         setDisabled(document.querySelector("#SwitchAllocationRows"), false);
         setDisabled(document.querySelector("#Grouping"), true);

         this.handleSwitchAllocationRowsButton();

         vsWidget.option("defaultAllocationTableRowDefinitionID", "AllocationInResourcesViewTRD");
      });

      document.querySelector("#ShowLoadsView").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("viewType", ViewType.Loads);

         // handle states of toolbar buttons
         document.querySelector("#ShowResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowSkilledResView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowActView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowLoadsView").classList.add("toolbar-button-highlighted");
         document.querySelector("#ShowEntitiesTable").classList.remove("toolbar-button-highlighted");
         setDisabled(document.querySelector("#ShowEntitiesTable"), true);
         setDisabled(document.querySelector("#ShowTopViewArea"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         document.querySelector("#ShowTopViewArea").classList.toggle("toolbar-button-highlighted",
            vsWidget.option("topViewAreaVisibleInLoadsView") && !document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ShowWorldView"), true);
         setDisabled(document.querySelector("#ScrollToResource"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));
         setDisabled(document.querySelector("#ScrollToAllocation"), true);
         setDisabled(document.querySelector("#ScrollToActivity"), true);
         setDisabled(document.querySelector("#ScrollToEntity"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted") || !vsWidget.option("entitiesTableVisibleInResourcesView"));
         setDisabled(document.querySelector("#SwitchVerticalRowDragging"), false);

         // hide world view
         vsWidget.option("worldViewVisible", false);

         // handle state of toolbar button
         document.querySelector("#ShowWorldView").classList.remove("toolbar-button-highlighted");
         
         setDisabled(document.querySelector("#SwitchAllocationRows"), true);
         setDisabled(document.querySelector("#Grouping"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"));

         if (document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted"))
            vsWidget.scrollToObject(ObjectType.Resource, "R40", TargetPositions.Top, false);

         this.handleSwitchAllocationRowsButton();
      });

      document.querySelector("#ShowTopViewArea").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         // show top body view area
         const viewName = viewTypeToNameMap[vsWidget.option("viewType")];
         const optionName = `topViewAreaVisibleIn${viewName}View`;
         const newOptionValue = !vsWidget.option(optionName);
         vsWidget.option(optionName, newOptionValue);

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#ShowWorldView").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         // show world view
         vsWidget.option("worldViewVisible", !vsWidget.option("worldViewVisible"));

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      // --- toolbar buttons for some typical actions ---

      document.querySelector("#FitIntoView").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.fitTimeAreaIntoView();
      });

      document.querySelector("#SetTimeResolution").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.setTimeResolutionForView("hours", 12);
      });

      document.querySelector("#ScrollToDate").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.scrollToDate(new Date(2025, /*starting with 0 for January*/ 0, 13));
      });

      document.querySelector("#ScrollToActivity").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.scrollToObject(ObjectType.Activity, "A3");
      });

      document.querySelector("#ScrollToResource").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.scrollToObject(ObjectType.Resource, "R3", 16, true);
      });

      document.querySelector("#ScrollToEntity").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.scrollToObject(ObjectType.Entity, "E3");
      });

      document.querySelector("#ScrollToAllocation").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         vsWidget.scrollToObject(ObjectType.Allocation, "0_0");
      });

      // --- toolbar buttons for some options ---

      document.querySelector("#SwitchNonworkingTimeVisible").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("nonWorkingTimeVisible", !vsWidget.option("nonWorkingTimeVisible"));

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#ShowEntitiesTable").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         // show entities table for the currently visible view
         const viewName = viewTypeToNameMap[vsWidget.option("viewType")];
         if (viewName !== "Loads") {
            const optionName = `entitiesTableVisibleIn${viewName}View`;
            const newOptionValue = !vsWidget.option(optionName);
            vsWidget.option(optionName, newOptionValue);

            // handle state of toolbar button
            event.currentTarget.classList.toggle("toolbar-button-highlighted", newOptionValue);

            setDisabled(document.querySelector("#ScrollToEntity"), document.querySelector("#LoadSampleData2").classList.contains("toolbar-button-highlighted") || !newOptionValue);
         }
      });

      document.querySelector("#SwitchMultipleSelection").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("multipleSelectionEnabled", !vsWidget.option("multipleSelectionEnabled"));

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#SwitchMultipleBarDragging").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("multipleBarDraggingEnabled", !vsWidget.option("multipleBarDraggingEnabled"));

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#SwitchVerticalRowDragging").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         const rowDraggingEnabled = !!vsWidget.option("defaultActivityAllowedRowDragModes");
         if (rowDraggingEnabled) {
            vsWidget.option("defaultActivityAllowedRowDragModes", RowDragModes.None);
            vsWidget.option("defaultResourceAllowedRowDragModes", RowDragModes.None);
            vsWidget.option("defaultEntityAllowedRowDragModes", RowDragModes.DragOutside);
         }
         else {
            vsWidget.option("defaultActivityAllowedRowDragModes", RowDragModes.DragVertically);
            vsWidget.option("defaultResourceAllowedRowDragModes", RowDragModes.DragVertically);
            vsWidget.option("defaultEntityAllowedRowDragModes", RowDragModes.DragOutside | RowDragModes.DragVertically);
         }

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#SwitchAllocationRows").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         if (vsWidget.option("viewType") === ViewType.Resources) {
            vsWidget.option("allocationRowsVisibleInResourcesView", !vsWidget.option("allocationRowsVisibleInResourcesView"));
            this.handleSwitchAllocationRowsButton();
         }
         else if (vsWidget.option("viewType") === ViewType.Activities) {
            vsWidget.option("allocationRowsVisibleInActivitiesView", !vsWidget.option("allocationRowsVisibleInActivitiesView"));
            this.handleSwitchAllocationRowsButton();
         }
         else if (vsWidget.option("viewType") === ViewType.SkilledResources) {
            vsWidget.option("allocationRowsVisibleInSkilledResourcesView", !vsWidget.option("allocationRowsVisibleInSkilledResourcesView"));
            this.handleSwitchAllocationRowsButton();
         }
      });

      document.querySelector("#Grouping").addEventListener("change", event => {
         const vsWidget = this._vsWidget;

         switch (event.currentTarget.value) {
            case "none":
               vsWidget.option("resourceHierarchySupplementaryDefinitionID", null);
               vsWidget.option("resourceHierarchySupplementaryDefinitionIDInLoadsView", null);
               break;
            case "1criterion":
               vsWidget.option("resourceHierarchySupplementaryDefinitionID", "criterion1");
               vsWidget.option("resourceHierarchySupplementaryDefinitionIDInLoadsView", "criterion1");
               break;
            case "2criteria":
               vsWidget.option("resourceHierarchySupplementaryDefinitionID", "criterion2");
               vsWidget.option("resourceHierarchySupplementaryDefinitionIDInLoadsView", "criterion2");
               break;
            default:
               vsWidget.option("resourceHierarchySupplementaryDefinitionID", null);
               vsWidget.option("resourceHierarchySupplementaryDefinitionIDInLoadsView", null);
         }
      });

      document.querySelector("#Locale").addEventListener("change", event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("locale", event.currentTarget.value);
      });

      document.querySelector("#SwitchCompactLayout").addEventListener("click", event => {
         const vsWidget = this._vsWidget;

         // switch to compact or normal layout
         const toCompactLayout = !event.currentTarget.classList.contains("toolbar-button-highlighted");

         // hint: setting these options by using an object has the advantage of an internal update suspension for more performance!
         vsWidget.option({
            "topBarSymbolsVisible": !toCompactLayout,
            "topRowMarginInTimeArea": toCompactLayout ? 2 : 10,
            "bottomRowMarginInTimeArea": toCompactLayout ? 2 : 10,
            "subRowDistanceInTimeArea": toCompactLayout ? 2 : 5,
            "defaultActivityMinimumRowHeight": toCompactLayout ? 22 : 42,
            "defaultAllocationMinimumRowHeight": toCompactLayout ? 22 : 42,
            "defaultResourceMinimumRowHeight": toCompactLayout ? 22 : 42,
            "defaultEntityMinimumRowHeight": toCompactLayout ? 22 : 42,
            "defaultAllocationBarHeight": toCompactLayout ? 16 : 22,
            "defaultActivityBarHeight": toCompactLayout ? 16 : 22
         });

         // handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      // --- toolbar buttons for some special actions ---

      document.querySelector("#SwitchPromise").addEventListener("click", event => {
         // only handle state of toolbar button
         event.currentTarget.classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#LoadSampleData1").addEventListener("click", _event => {
         const currentLocale = document.querySelector("#Locale").value;
         const currentTimeZone = document.querySelector("#TimeZone").value;

         this.instantiateVSWidget();
         this.loadData(sampleData1);

         const vsWidget = this._vsWidget;

         vsWidget.option({
            "locale": currentLocale,
            "timeZone": currentTimeZone
         });

         vsWidget.fitTimeAreaIntoView(new Date(2025, 0, 6), new Date(2025, 0, 18));

         document.querySelector("#LoadSampleData1").classList.add("toolbar-button-highlighted");
         document.querySelector("#LoadSampleData2").classList.remove("toolbar-button-highlighted");
         document.querySelector("#SwitchCompactLayout").classList.remove("toolbar-button-highlighted");
         setDisabled(document.querySelector("#ShowSkilledResView"), false);
         document.querySelector("#ShowWorldView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowTopViewArea").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowResView").dispatchEvent(new MouseEvent("click"));
         setDisabled(document.querySelector("#SwitchAllocationRows"), false);
         setDisabled(document.querySelector("#ShowEntitiesTable"), false);
         setDisabled(document.querySelector("#Grouping"), false);
      });

      document.querySelector("#LoadSampleData2").addEventListener("click", _event => {
         const currentLocale = document.querySelector("#Locale").value;
         const currentTimeZone = document.querySelector("#TimeZone").value;

         this.instantiateVSWidget();
         this.loadData(sampleData2);

         const vsWidget = this._vsWidget;

         vsWidget.option({
            "locale": currentLocale,
            "timeZone": currentTimeZone
         });

         document.querySelector("#LoadSampleData1").classList.remove("toolbar-button-highlighted");
         document.querySelector("#LoadSampleData2").classList.add("toolbar-button-highlighted");
         document.querySelector("#SwitchCompactLayout").classList.remove("toolbar-button-highlighted");
         setDisabled(document.querySelector("#ShowSkilledResView"), true);
         document.querySelector("#ShowWorldView").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowTopViewArea").classList.remove("toolbar-button-highlighted");
         document.querySelector("#ShowResView").dispatchEvent(new MouseEvent("click"));
         document.querySelector("#SwitchAllocationRows").classList.remove("toolbar-button-highlighted");
         setDisabled(document.querySelector("#SwitchAllocationRows"), false);
         setDisabled(document.querySelector("#ShowEntitiesTable"), true);
         setDisabled(document.querySelector("#Grouping"), true);
      });

      document.querySelector("#HintsOnContextMenu").addEventListener("click", _event => {
         alert("Please use the context menu implemented as a working sample on allocation bars!");
      });

      document.querySelector("#HintsOnTimeZones").addEventListener("click", _event => {
         //alert("You may see shifted working times inside this application, when you look onto it outside a time zone of UTC +/- 1 hour!\n\n" +
         //   "This is no issue of VSW, but the way we currently have defined the date values in the data sets.");
         alert("The dates in this sample are designed for use in a time zone of UTC +/- 1 hour! "
            + "If your local time zone is a different one or if you have selected a different time zone using the combo box, "
            + "then the scheduling of the bars and the positioning of the working/non-working times may seem strange. For instance, "
            + "activities may be scheduled for a night, or the working times shown in the calendar grid "
            + "may be on weekends or in the middle of the night. All this is not an issue of VSW, but it is due to the orientation "
            + "of the dates to a time zone of UTC +/- 1 hour.");
      });

      document.querySelector("#TimeZone").addEventListener("change", event => {
         const vsWidget = this._vsWidget;

         vsWidget.option("timeZone", event.currentTarget.value || undefined);
      });

      document.querySelector("#SaveAsPDF").addEventListener("click", _event => {
         if (document.location.protocol === "file:")
            alert("Attention: Since you have started this Sample App from the file system,\nimages included by using relative URLs will not be exported to the PDF file because of browser restrictions! (In this case, please also ignore corresponding JavaScript errors of the browser!)");

         // show PDF options dialog
         const dialogPromise = this._pdfOptionsDialog.showModal(this._pdfOptions);

         dialogPromise.then((params) => {
            this._pdfOptions = params.pdfOptions;

            if (params.saveButtonClicked) {
               // show banner
               const h1Element = document.querySelector("h1");
               h1Element.style.backgroundColor = "tomato";
               const headline = h1Element.innerText;
               h1Element.innerText = "Saving as PDF...";
               setDisabled(document.querySelector("#SaveAsPDF"), true);

               window.setTimeout(() => {
                  const finalFunc = () => {
                     // remove banner
                     h1Element.innerText = headline;
                     h1Element.style.backgroundColor = "white";
                     setDisabled(document.querySelector("#SaveAsPDF"), false);
                  };
                  try {
                     const vsWidget = this._vsWidget;

                     const prom = vsWidget.saveAsPDF(`VSW_${new Date().toISOString().replace(/(\.\d+)Z/, "").replace(/:/g, ".")}.pdf`, params.pdfOptions);
                     if (prom) {
                        prom
                           // executed asynchronously
                           .catch(errorMessage => {
                              this.showSimplePopup(`saveAsPDF has failed: ${errorMessage}`);
                           })
                           .finally(finalFunc);
                     }
                     else
                        finalFunc();
                  }
                  catch (error) {
                     // executed synchronously
                     this.showSimplePopup(`saveAsPDF has failed: ${error.message}`);
                     finalFunc();
                  }
               }, 50);
            }
         }, () => {});
      });

      document.querySelector("#About").addEventListener("click", _event => {
         const vsWidget = this._vsWidget;

         // show about dialog
         vsWidget.about();
      });
   }
}

// initialize the application
new SampleApp();
