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

import {
   Allocation,
   ObjectType,
   OnDropCallbackArgs,
   OnShowContextMenuCallbackArgs,
   OnShowTooltipCallbackArgs,
   Resource,
   RowDragModes,
   SaveAsPDFOptions,
   TargetPositions,
   ViewType,
   VisualType,
   VSWidget,
   VSWidgetCallbackArgs,
   VSWidgetOptions,
} from 'nwaf_rab';

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

const setDisabled = (elem: HTMLElement, newValue: boolean): void => {
   if (newValue)
      elem.setAttribute("disabled", "");
   else
      elem.removeAttribute("disabled");
};

class SampleApp {
   private _vsWidget!: VSWidget;
   private _pdfOptionsDialog!: PDFOptionsDialog;
   private _editResourceDialog!: EditResourceDialog;
   private _pdfOptions!: SaveAsPDFOptions;
   private _objectMaps!: Record<string, Record<string, any[]>>;

   /**
    * Initializes the application and the VS Widget.
    */
   public 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! as Element).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.
    */
   private instantiateVSWidget (): void {
      this._vsWidget?.destroy();

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

      // define initial options
      const vsWidgetOptions: VSWidgetOptions = {
         licenseKey: (window as any).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: string[] = ["canSelect", "onSelectionChanged", "onClicked", "onDoubleClicked",
                           "canDrag", "onDrag", "onDragStart", "onDrop",
                           "onCollapseStateChanged", "onCurveCollapseStateChanged",
                           "onShowContextMenu", "onCloseContextMenu",
                           "onShowTooltip", "onTableCellDefinitionWidthChanged",
                           "onTimeAreaViewParametersChanged", "onVerticalScrollOffsetChanged"];
      
      callbackNames.forEach(callbackName => {
         vsWidget.option(callbackName, (callbackArgs: VSWidgetCallbackArgs) => {
            // 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")! as HTMLElement).style.backgroundColor = "tomato";
   }

   /**
    * Instantiates the PDF options dialog.
    */
   private instantiatePDFOptionsDialog (): void {
      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.
    */
   private instantiateEditResourceDialog (): void {
      this._editResourceDialog = new EditResourceDialog(document.getElementById("EditResourceDialogContainer")!);
   }

   /**
    * Handles each callback that was triggered by the VS Widget.
    *
    * @param callbackName Name of the callback.
    * @param callbackArgs Original callbackArgs object.
    */
   private onCallbackTriggered (callbackName: string, callbackArgs: VSWidgetCallbackArgs): void {
      // copy callbackArgs object and delete properties that cannot be put to the console
      const callbackArgsToLog = {...callbackArgs};
      delete (callbackArgsToLog as any).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": {
            // only as an example
            const onShowTooltipCallbackArgs = callbackArgs as OnShowTooltipCallbackArgs;
            onShowTooltipCallbackArgs.innerHTML = `${popupText} ID: ${onShowTooltipCallbackArgs.object ? `ID: ${onShowTooltipCallbackArgs.object!.ID}` : ""}${(onShowTooltipCallbackArgs.visualType === VisualType.Curve) ? `<br/>${onShowTooltipCallbackArgs.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 text Text to display in the popup.
    * @param onSecondLine If true, then show popup in second line.
    */
   private showSimplePopup (text: string, onSecondLine: boolean = false): void {
      // 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.
    */
   private handleOnDrop (callbackArgs: VSWidgetCallbackArgs, popupText: string): void {
      const onDropCallbackArgs = callbackArgs as OnDropCallbackArgs;

      const dropFunc = (_onDropCallbackArgs: OnDropCallbackArgs) => {
         // 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)
         onDropCallbackArgs.promise = new Promise<void>((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, onDropCallbackArgs);
            }, 3000);
         });
      }
      else {
         dropFunc.call(this, onDropCallbackArgs);
      }
   }

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

      const onShowContextMenuCallbackArgs = callbackArgs as OnShowContextMenuCallbackArgs;

      // 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: any[] | undefined;
      let contextMenuDoneFunction: ((cmd: string) => void) | undefined;

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

         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: string) => {
               if (cmd === 'change_color') {
                  const copyOfAllocation = {...allocation} as Allocation;
                  copyOfAllocation.Entries = allocation.Entries!.map((entry: any) => ({...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 ((onShowContextMenuCallbackArgs.objectType === ObjectType.Resource) && (onShowContextMenuCallbackArgs.visualType === VisualType.Row)) {
         const resource = onShowContextMenuCallbackArgs.object as Resource;

         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: string) => {
            if (cmd === 'edit_resource_code') {
               const copyOfResource = {...resource} as Resource;
               const dialogPromise = this._editResourceDialog.showModal(copyOfResource);
               dialogPromise?.then(() => {
                  vsWidget.updateResources([copyOfResource]);
                  vsWidget.render();
               });
            }
         };
      }

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

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

   /**
    * Hides a context menu.
    */
   private handleOnCloseContextMenu (): void {
      // 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
    */
   private loadData (sampleData: SampleData): void {
      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 => {
         const objectArray = (sampleData as any)[objectArrayName];
         if (Array.isArray(objectArray) && (objectArray.length > 0)) {
            (vsWidget as any)[`add${objectArrayName}`](objectArray);

            // in order to have fast access by ID for each object type later, maps are filled
            this._objectMaps[objectArrayName] = Object.fromEntries(Object.entries(objectArray).map(([_key, value]: [string, any]) => [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.
    */
   private registerWindowResizeEventHandler (): void {
      // 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")! as HTMLElement);
         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();
   }

   private handleSwitchAllocationRowsButton (): void {
      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.
    */
   private registerToolbarClicks (): void {
      // --- 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") as ViewType];
         const optionName = `topViewAreaVisibleIn${viewName}View`;
         const newOptionValue = !vsWidget.option(optionName);
         vsWidget.option(optionName, newOptionValue);

         // handle state of toolbar button
         (event.currentTarget as HTMLElement).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 as HTMLElement).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 as HTMLElement).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") as 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 as HTMLElement).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 as HTMLElement).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 as HTMLElement).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 as HTMLElement).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 as HTMLSelectElement).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 as HTMLSelectElement).value);
      });

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

         // switch to compact or normal layout
         const toCompactLayout = !(event.currentTarget as HTMLElement).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 as HTMLElement).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 as HTMLElement).classList.toggle("toolbar-button-highlighted");
      });

      document.querySelector("#LoadSampleData1")!.addEventListener("click", _event => {
         const currentLocale = (document.querySelector("#Locale")! as HTMLInputElement).value;
         const currentTimeZone = (document.querySelector("#TimeZone")! as HTMLInputElement).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")! as HTMLInputElement).value;
         const currentTimeZone = (document.querySelector("#TimeZone")! as HTMLInputElement).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 as HTMLSelectElement).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: PDFOptionsDialogResult) => {
            this._pdfOptions = params.pdfOptions;

            if (params.saveButtonClicked) {
               // show banner
               const h1Element = (document.querySelector("h1")! as HTMLElement);
               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: string) => {
                              this.showSimplePopup(`saveAsPDF has failed: ${errorMessage}`);
                           })
                           .finally(finalFunc);
                     }
                     else
                        finalFunc();
                  }
                  catch (error: any) {
                     // 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();
