'use strict';

var utils = require('./utils.js');
var stringResources = require('./stringResources.js');
var parameterValidators = require('./parameterValidators.js');
var parameters = require('./parameters.js');
var enums = require('./enums.js');
var eventEmitter = require('./event-emitter.js');

var defaultOptions = {};
var Events = {
  PARAMETERS_READY: "pa.parametersReady",
  ERROR: "pa.Error"
};
class ParametersArea {
  // #region fields
  options;
  viewerOptions;
  element;
  $element;
  eventEmitter;
  controller;
  notificationService;
  parameters;
  editors;
  parameterEditors;
  initialParameterValues;
  recentParameterValues;
  processedParameterValues;
  _parametersContainer;
  _parametersWrapper;
  $errorMessage;
  $previewButton;
  parameterContainerTemplate;
  parametersAreaVisible;
  parametersAreaNecessary;
  enableAccessibility;
  loadingCount;
  initialized;
  // #endregion
  // #region constructor
  constructor(element, options, viewerOptions) {
    this.options = $.extend({}, defaultOptions, options, viewerOptions);
    this.element = element;
    this.eventEmitter = new eventEmitter.EventEmitter();
    this.editors = {};
    this.controller = this.options.controller;
    if (!this.controller) {
      throw "No controller (telerikReporting.reportViewerController) has been specified.";
    }
    this.notificationService = options.notificationService;
    if (!this.notificationService) {
      throw "No notificationService (telerikReporting.NotificationService) has been specified.";
    }
    this.parameterEditors = parameters.ParameterEditors;
    if (this.options.parameterEditors && this.options.parameterEditors.length > 0) {
      this.parameterEditors = [].concat(this.options.parameterEditors, parameters.ParameterEditors);
    }
    this.recentParameterValues;
    this.parameters;
    this.initialParameterValues;
    this.processedParameterValues = void 0;
    this.$element = $(this.element);
    this._parametersContainer = this.element?.querySelector(".trv-parameters-area-content");
    this._parametersWrapper = this.element?.querySelector(".trv-parameters-wrapper");
    this.$errorMessage = this.$element.find(".trv-error-message");
    this.$previewButton = this.$element.find(".trv-parameters-area-preview-button");
    this.$previewButton.text(stringResources.stringResources[this.$previewButton.text()]);
    this.$previewButton.attr("aria-label", stringResources.stringResources[this.$previewButton.attr("aria-label")]);
    this.$previewButton.on("click", (e) => {
      e.preventDefault();
      if (!this.controller.hasInvalidParameter(this.parameters)) {
        this.applyParameters();
      }
    });
    this.parameterContainerTemplate = this.options.templates["trv-parameter"];
    this.parametersAreaVisible = this.options.parametersAreaVisible !== false;
    this.enableAccessibility = this.options.enableAccessibility;
    this.loadingCount = 0;
    this.parametersAreaNecessary = false;
    this.initialized = false;
    this.init();
  }
  // #endregion
  // #region methods
  init() {
    if (this.initialized) {
      return;
    }
    this._attachEvents();
    this.replaceStringResources(this.$element);
  }
  replaceStringResources($paramsArea) {
    var $previewButton = $paramsArea.find(".trv-parameters-area-preview-button");
    if (!$previewButton) {
      return;
    }
    $previewButton.attr("aria-label", stringResources.stringResources[$previewButton.attr("aria-label")]);
    $previewButton.text(stringResources.stringResources[$previewButton.text()]);
  }
  toggleElementDisplay(el, shouldHide) {
    el.style.display = shouldHide ? "none" : "";
  }
  createParameterContainer() {
    return $(this.parameterContainerTemplate);
  }
  createParameterUI(parameter) {
    var $container = this.createParameterContainer();
    var $editorPlaceholder = $container.find(".trv-parameter-value");
    var $title = $container.find(".trv-parameter-title");
    var $error = $container.find(".trv-parameter-error");
    var $errorMessage = $container.find(".trv-parameter-error-message");
    var $useDefaultValueCheckbox = $container.find(".trv-parameter-use-default input");
    var editorsTypes = this.options.parameters && this.options.parameters.editors ? this.options.parameters.editors : null;
    var editorFactory = this.selectParameterEditorFactory(parameter, editorsTypes);
    var parameterText = utils.escapeHtml(parameter.text);
    var isHiddenParameter = !parameter.isVisible;
    if (isHiddenParameter) {
      parameterText += " [<b>hidden</b>]";
    }
    $title.html(parameterText).attr("title", parameterText);
    $errorMessage.text(parameter.Error);
    (parameter.Error ? $.fn.show : $.fn.hide).call($error);
    var editor = editorFactory.createEditor(
      $editorPlaceholder,
      {
        templates: this.options.templates,
        parameterChanged: (parameter2, newValue) => {
          var invalidParameterLI = document.querySelector(".trv-pages-area .trv-error-message ul li." + parameter2.name);
          try {
            newValue = parameterValidators.ParameterValidators.validate(parameter2, newValue);
            $error.hide();
            if (invalidParameterLI) {
              this.toggleElementDisplay(invalidParameterLI, true);
            }
            this.onParameterChanged(parameter2, newValue);
          } catch (error) {
            if (invalidParameterLI) {
              this.toggleElementDisplay(invalidParameterLI, false);
            }
            parameter2.Error = error;
            parameter2.value = [];
            $errorMessage.text(error);
            $error.show();
            this.enablePreviewButton(false);
          } finally {
            this.setAccessibilityErrorState(parameter2);
          }
        },
        enableAccessibility: this.enableAccessibility
      }
    );
    this.editors[parameter.id] = editor;
    editor.beginEdit(parameter);
    if (this.enableAccessibility && !isHiddenParameter) {
      editor.addAccessibility(parameter);
    }
    if ($useDefaultValueCheckbox.length > 0) {
      $useDefaultValueCheckbox.on("click", (event) => {
        var useDefaultValue = $(event.currentTarget).is(":checked");
        if (useDefaultValue) {
          delete this.recentParameterValues[parameter.id];
          delete this.initialParameterValues[parameter.id];
          this.invalidateChildParameters(parameter);
          this.updateParameters(this.onLoadParametersSuccess.bind(this));
        } else {
          this.recentParameterValues[parameter.id] = parameter.value;
          this.initialParameterValues[parameter.id] = parameter.value;
        }
        editor.enable(!useDefaultValue);
        this.trigger(Events.PARAMETERS_READY, this.recentParameterValues);
      });
      var hasInitialValues = this.initialParameterValues !== null;
      if (hasInitialValues) {
        if (!(parameter.id in this.initialParameterValues)) {
          $useDefaultValueCheckbox.prop("checked", true);
          editor.enable(false);
        }
      } else if (isHiddenParameter) {
        $useDefaultValueCheckbox.prop("checked", true);
        editor.enable(false);
      }
    }
    return $container;
  }
  setAccessibilityErrorState(parameter) {
    var editor = this.editors[parameter.id];
    if (!editor || !this.enableAccessibility) {
      return;
    }
    editor.setAccessibilityErrorState(parameter);
  }
  enablePreviewButton(enabled) {
    if (enabled) {
      this.$previewButton.prop("disabled", false);
      this.$previewButton.removeClass("k-disabled");
    } else {
      this.$previewButton.prop("disabled", true);
      this.$previewButton.addClass("k-disabled");
    }
  }
  selectParameterEditorFactory(parameter, editorsType) {
    var factory = Array.from(this.parameterEditors).find((editor) => {
      return editor.match(parameter, editorsType);
    });
    return factory;
  }
  showError(error) {
    this.$errorMessage.text(error);
    (error ? $.fn.addClass : $.fn.removeClass).call(this.$element, "trv-error");
  }
  showPreviewButton(parameters) {
    (this.controller.allParametersAutoRefresh(parameters) && this.controller.autoRunEnabled ? $.fn.removeClass : $.fn.addClass).call(this.$element, "preview");
  }
  clearEditors() {
    Object.entries(this.editors).forEach(([key, editor]) => {
      if (typeof editor.destroy === "function") {
        editor.destroy();
      }
    });
    this.editors = {};
  }
  fill(newParameters) {
    this.recentParameterValues = {};
    this.processedParameterValues = {};
    this.parameters = newParameters || [];
    this.clearEditors();
    var $parameterContainer;
    var $tempContainer = $("<div></div>");
    Array.from(this.parameters).forEach((parameter) => {
      try {
        parameter.value = parameterValidators.ParameterValidators.validate(parameter, parameter.value);
      } catch (e) {
        parameter.Error = parameter.Error || e;
      }
      var hasError = Boolean(parameter.Error);
      var hasValue = !hasError;
      if (hasValue) {
        this.recentParameterValues[parameter.id] = parameter.value;
      } else {
        parameter.Error = stringResources.stringResources.invalidParameter;
      }
      if (parameter.availableValues) {
        this.processedParameterValues[parameter.id] = { valueMember: parameter.value, displayMember: parameter.label, availableValues: parameter.availableValues, multivalue: parameter.multivalue, allowNull: parameter.allowNull };
      } else {
        this.processedParameterValues[parameter.id] = parameter.value;
      }
      if (parameter.isVisible || this.options.showHiddenParameters) {
        $parameterContainer = this.createParameterUI(parameter);
        if ($parameterContainer) {
          $tempContainer.append($parameterContainer);
        }
      }
    });
    if (this.initialParameterValues !== void 0) {
      if (null === this.initialParameterValues) {
        this.initialParameterValues = {};
        Array.from(this.parameters).forEach((parameter) => {
          if (parameter.isVisible) {
            this.initialParameterValues[parameter.id] = parameter.value;
          } else {
            delete this.recentParameterValues[parameter.id];
          }
        });
      } else {
        Array.from(this.parameters).forEach((parameter) => {
          if (!(parameter.id in this.initialParameterValues)) {
            delete this.recentParameterValues[parameter.id];
          }
        });
      }
    }
    if (this._parametersWrapper) {
      kendo.destroy(this._parametersWrapper);
      this._parametersWrapper.innerHTML = "";
      if (this.parameters.length > 0) {
        this._parametersWrapper.append(...$tempContainer.children().get());
        if (this.enableAccessibility) {
          this._parametersWrapper.setAttribute("aria-label", "Parameters area. Contains " + this.parameters.length + " parameters.");
        }
      }
    }
    this.showPreviewButton(this.parameters);
    var allValid = !this.controller.hasInvalidParameter(this.parameters);
    this.enablePreviewButton(allValid);
    this.applyProcessedParameters();
  }
  applyParameters() {
    this.controller.setParameters($.extend({}, this.recentParameterValues));
    this.controller.previewReport(false);
  }
  applyProcessedParameters() {
    this.controller.setProcessedParameterValues($.extend({}, this.processedParameterValues));
  }
  tryRefreshReport() {
    this.trigger(Events.PARAMETERS_READY, this.recentParameterValues);
    if (this.controller.allParametersValidForAutoRefresh(this.parameters, this.recentParameterValues) && this.controller.autoRunEnabled) {
      this.applyParameters();
    }
  }
  invalidateChildParameters(parameter) {
    if (parameter.childParameters) {
      Array.from(parameter.childParameters).forEach((parameterId) => {
        var childParameter = this.getParameterById(parameterId);
        if (childParameter) {
          this.invalidateChildParameters(childParameter);
        }
        delete this.recentParameterValues[parameterId];
        this.resetPendingParameterChange(parameterId);
      });
    }
  }
  resetPendingParameterChange(parameterId) {
    if (this.editors) {
      var editor = this.editors[parameterId];
      if (editor && typeof editor.clearPendingChange === "function") {
        editor.clearPendingChange();
      }
    }
  }
  onParameterChanged(parameter, newValue) {
    delete parameter["Error"];
    parameter.value = newValue;
    this.recentParameterValues[parameter.id] = newValue;
    if (this.initialParameterValues !== void 0) {
      if (parameter.id in this.initialParameterValues) {
        this.recentParameterValues[parameter.id] = newValue;
      }
    } else {
      this.recentParameterValues[parameter.id] = newValue;
    }
    this.invalidateChildParameters(parameter);
    if (parameter.childParameters) {
      this.updateParameters(this.tryRefreshReport.bind(this));
    } else {
      var allValid = !this.controller.hasInvalidParameter(this.parameters);
      this.enablePreviewButton(allValid);
      if (allValid) {
        var errorMsg = document.querySelector(".trv-pages-area .trv-error-message");
        if (this.$previewButton.is(":visible") && errorMsg) {
          errorMsg.textContent = stringResources.stringResources.tryReportPreview;
        }
        this.tryRefreshReport();
      }
    }
  }
  getParameterById(parameterId) {
    if (this.parameters) {
      for (var i = 0; i < this.parameters.length; i++) {
        var p = this.parameters[i];
        if (p.id === parameterId) {
          return p;
        }
      }
    }
    return null;
  }
  setParametersAreaVisibility(visible) {
    this.controller.setParametersAreaVisible({ visible });
  }
  hasVisibleParameters(params) {
    return (params || []).some((parameter) => {
      return parameter.isVisible;
    });
  }
  beginLoad() {
    this.loadingCount++;
    this.$element.addClass("trv-loading");
  }
  endLoad() {
    if (this.loadingCount > 0) {
      if (0 === --this.loadingCount) {
        this.$element.removeClass("trv-loading");
      }
    }
  }
  onLoadParametersComplete(params, successAction) {
    this.showParametersAreaIfNecessary(params);
    this.fill(params);
    this.showError("");
    if (this.parametersAreaNecessary && this.parametersAreaVisible) {
      this.showParametersArea(true);
      if (this.enableAccessibility) {
        this.setSplitbarAccessibilityAttributes();
      }
    }
    this.notificationService.updateUIInternal();
    if (typeof successAction === "function") {
      successAction();
    }
    this.endLoad();
  }
  showParametersAreaIfNecessary(params) {
    this.parametersAreaNecessary = this.hasVisibleParameters(params) || !this.controller.autoRunEnabled;
    this.showParametersArea(this.parametersAreaVisible && this.parametersAreaNecessary);
  }
  updateParameters(successAction) {
    this.acceptParameters(this.controller.loadParameters(this.recentParameterValues), successAction);
  }
  acceptParameters(controllerLoadParametersPromise, successAction) {
    this.beginLoad();
    controllerLoadParametersPromise.then((parameters) => {
      this.onLoadParametersComplete(parameters, successAction);
    }).catch((error) => {
      this.endLoad();
      this.clear();
      if (!this.$element.hasClass("k-hidden")) {
        this.showError(error);
      }
      this.trigger(Events.ERROR, error);
    });
  }
  onLoadParametersSuccess() {
    if (this.initialParameterValues === null) {
      this.initialParameterValues = $.extend({}, this.recentParameterValues);
    }
    this.trigger(Events.PARAMETERS_READY, this.recentParameterValues);
  }
  showParametersArea(show) {
    var splitter = $("#" + this.options.viewerSelector + "-parameters-splitter").getKendoSplitter();
    if (!splitter) {
      return;
    }
    var sibling = this.$element.prev(".k-splitbar");
    if (this.options.parametersAreaPosition === enums.ParametersAreaPositions.TOP || this.options.parametersAreaPosition === enums.ParametersAreaPositions.LEFT) {
      sibling = this.$element.next(".k-splitbar");
    }
    (this.parametersAreaNecessary ? $.fn.removeClass : $.fn.addClass).call(sibling, "k-hidden");
    splitter.toggle(".trv-parameters-area", show);
  }
  setSplitbarAccessibilityAttributes() {
    var splitbar = this.$element.prev();
    var tabIndex = this.$element.find(".trv-parameters-area-content").attr("tabIndex");
    if (this.options.parametersAreaPosition === enums.ParametersAreaPositions.TOP || this.options.parametersAreaPosition === enums.ParametersAreaPositions.LEFT) {
      splitbar = this.$element.next();
    }
    splitbar.attr("aria-label", stringResources.stringResources.ariaLabelParametersAreaSplitter);
    splitbar.attr("tabIndex", tabIndex);
  }
  _attachEvents() {
    this.controller.on("reloadParameters", (controllerLoadParametersPromise) => {
      this.showError();
      if (this._parametersWrapper) {
        kendo.destroy(this._parametersWrapper);
        this._parametersWrapper.innerHTML = "";
      }
      this.acceptParameters(controllerLoadParametersPromise, this.onLoadParametersSuccess.bind(this));
    }).onAsync("beforeLoadReport", async () => {
      this.loadingCount = 0;
      this.beginLoad();
    }).onAsync("reportLoadComplete", async (reportInfo) => {
      this.showParametersAreaIfNecessary(this.parameters);
      this.showPreviewButton(this.parameters);
    }).on("error", () => this.endLoad()).on("pageReady", () => this.endLoad());
    this.notificationService.getParametersAreaState((event, args) => {
      var parametersAreaNecessary = false;
      if (this.parameters) {
        parametersAreaNecessary = this.hasVisibleParameters(this.parameters) || !this.controller.autoRunEnabled;
      }
      args.enabled = parametersAreaNecessary;
      args.visible = this.parametersAreaVisible;
    }).setParametersAreaVisible((event, args) => {
      this.parametersAreaVisible = args.visible;
      this.showParametersArea(args.visible && (this.hasVisibleParameters(this.parameters) || !this.controller.autoRunEnabled));
    });
  }
  clear() {
    this.fill([]);
  }
  setParameters(parameterValues) {
    this.initialParameterValues = null === parameterValues ? null : $.extend({}, parameterValues);
  }
  // #endregion
  // #region events
  on(type, handler) {
    this.eventEmitter.on(type, handler);
    return this;
  }
  trigger(type, ...args) {
    return this.eventEmitter.trigger(type, ...args);
  }
  // #endregion
}

exports.ParametersArea = ParametersArea;
