(function (paw, utils) {
  /**
   * Note on furhter dev:
   * If custom content would need to be supported:
   * - datagrid features should be moved to own component
   * - proper datagrid initialization logic should be impl.
   *
   * @param {jQuery} $elt
   */
  function FinderComponent($elt) {
    var self = this;

    this.FinderDef;
    this.finderSettings;
    this.localization;

    this.$elt = $elt;
    this.$filtersHolder = null;
    this.$datagridHolder = null;
    this.$finderFiltersAction = null;

    this.finderDef = null;

    this.filterDefs = [];
    this.pinnedFilters = {};

    this.filtersPopup = null;
    this.datatable = null;

    this.filtersConfig = {};

    this.findOnChange = true;
    this.findDebouncer = utils.async.debouncer(function () {
      self.findDebounced();
    }, 400);
  }

  FinderComponent.prototype.markupDef = function () {
    var markup = [];
    markup.push(
      '<div class="paw-area-v paw-holder paw-nooverflow paw-holder-rel">'
    );
    markup.push(
      '   <div class="paw-col paw-area-h paw-items-middle paw-finder-filters paw-nooverflow">'
    );
    markup.push(
      '      <div class="paw-col-d paw-area-h paw-finder-pinned-filters paw-nooverflow"></div>'
    );
    markup.push('      <div class="paw-col paw-finder-filters-holder">');
    markup.push(
      '         <button id="finder-filters" class="btn btn-primary btn-outline btn-nav">'
    );
    markup.push('            <span class="fa fa-filter"></span>');
    markup.push(
      '            <span class="paw-finder-filters-label">' +
        this.localization.filters +
        "</span>"
    );
    markup.push("         </button>");
    markup.push("      </div>");
    markup.push("   </div>");
    markup.push(
      '   <div class="paw-col-d paw-nooverflow paw-finder-datagrid-holder">'
    );
    markup.push('      <div class="paw-finder-datagrid"></div>');
    markup.push("   </div>");
    markup.push('   <div class="paw-preloader-backdrop paw-preloader-cover">');
    markup.push(
      '      <span class="fa fa-spinner fa-2x paw-preloader paw-preloader-spin"></span>'
    );
    markup.push("   </div>");
    markup.push("</div>");
    return markup;
  };

  FinderComponent.prototype.filterMarkupDef = function (filter) {
    var markup = [];
    markup.push('<div class="paw-col-d paw-finder-filter-slot">');
    if (utils.value.isSet(filter.label)) {
      markup.push('   <div class="paw-text-ellipsis">');
      markup.push("      <span>" + filter.label + "</span>");
      markup.push("   </div>");
    }
    markup.push("   <div>");
    markup.push(filter.markup);
    markup.push("   </div>");
    markup.push("</div>");
    return markup;
  };

  FinderComponent.prototype.datagridMarkupDef = function () {
    var markup = [];

    markup.push(
      '<table id="' +
        this.finderDef.getId() +
        '-datatable" data-grid-id="' +
        this.finderDef.getId() +
        '-datagrid" data-url="' +
        this.finderSettings.datagridUrl +
        '" class="list-table">'
    );
    markup.push("   <thead>");
    markup.push("      <tr>");
    utils.iterable.iterate(this.gridColumns, function (col) {
      var headerAttrs = "";
      if (utils.value.isSet(col.url)) {
        headerAttrs += ' data-url="' + col.url + '"';
      }
      if (utils.value.isSet(col.width)) {
        headerAttrs += ' width="' + col.width + '"';
      }
      markup.push("<th " + headerAttrs + ">");
      markup.push(col.label);
      markup.push("</th>");
    });
    markup.push("      </tr>");
    markup.push("   </thead>");
    markup.push("</div>");
    return markup;
  };

  FinderComponent.prototype.init = function () {
    var self = this;
    this.$filtersHolder = this.$elt.find(".paw-finder-pinned-filters");
    this.$datagridHolder = this.$elt.find(".paw-finder-datagrid");
    this.$finderFiltersAction = this.$elt.find("#finder-filters");

    this.finderDef = new this.FinderDef(
      this,
      this.$filtersHolder,
      this.$datagridHolder,
      this.finderSettings,
      this.localization
    );
    this.gridColumns = this.finderDef.gridColumnsDef();

    self.$datagridHolder.html(self.datagridMarkupDef().join("\n"));
    self.$datagridHolder.css("opacity", 0);

    this.$finderFiltersAction.on("click", function () {
      self.findOnChange = false;
      self.openFiltersPopup();
    });

    utils.async
      .when(utils.async.all([this.finderDef.init()]))
      .then(function () {
        self.finderDef.data = self.finderDef.resetValues();
        self.filterDefs = self.finderDef.filtersDef();

        self.loadFiltersConfig();
        self.createPinnedFilters();

        self.$datagridHolder.css("opacity", "");
        self.$elt.find(".paw-preloader-backdrop").fadeOut();

        return self.initDatagrid();
      })
      .then(function () {
        self.onResize();
      });
  };

  FinderComponent.prototype.destroy = function () {
    this.destroyPinnedFilters();

    if (utils.value.isSet(this.datatable)) {
      this.datatable.destroy();
      this.datatable = null;
    }

    if (utils.value.isFunction(this.finderDef.destroy)) {
      this.finderDef.destroy();
    }

    if (utils.value.isSet(this.filtersPopup)) {
      this.filtersPopup.hide();
    }

    this.$finderFiltersAction.off("click");

    this.$finderFiltersAction.off();
  };

  FinderComponent.prototype.onResize = function () {
    this.datatable.columns.adjust();
    if (this.datatable.fixedHeader) {
      this.datatable.fixedHeader.adjust();
    }
    if (this.datatable.responsive) {
      this.datatable.responsive.recalc();
    }
  };

  FinderComponent.prototype.createFilterFromDef = function (
    filterDef,
    data,
    $elt,
    pinned,
    $filtersHolder
  ) {
    filterDef.pinned = pinned;
    return {
      id: filterDef.id,
      $elt: $elt,
      inputField: filterDef.inputField(
        $elt.find("#" + filterDef.id),
        data,
        $filtersHolder
      ),
      def: filterDef,
    };
  };

  FinderComponent.prototype.createPinnedFilters = function () {
    var self = this;
    var pinnedFiltersCount = 0;
    utils.iterable.iterate(this.filterDefs, function (filterDef) {
      if (!filterDef.pinned) {
        return;
      }
      var $filter = $(self.filterMarkupDef(filterDef).join("\n"));
      self.$filtersHolder.append($filter);
      self.pinnedFilters[filterDef.id] = self.createFilterFromDef(
        filterDef,
        self.finderDef.data,
        $filter,
        true,
        self.$filtersHolder
      );
      pinnedFiltersCount++;
    });

    this.adjustPinnedFiltersWidth(pinnedFiltersCount);
  };

  FinderComponent.prototype.adjustPinnedFiltersWidth = function (
    pinnedFiltersCount
  ) {
    if (pinnedFiltersCount <= 1) {
      return;
    }
    var pinnedFilterMaxW = 100 / pinnedFiltersCount;
    utils.iterable.iterate(this.pinnedFilters, function (pinnedFilter) {
      pinnedFilter.$elt.css("max-width", pinnedFilterMaxW + "%");
    });
  };

  FinderComponent.prototype.destroyPinnedFilters = function () {
    var self = this;
    utils.iterable.iterate(self.pinnedFilters, function (pinnedFilter) {
      pinnedFilter.inputField.remove();
      pinnedFilter.$elt.remove();
      delete self.pinnedFilters[pinnedFilter.id];
    });
  };

  FinderComponent.prototype.openFiltersPopup = function () {
    var self = this;
    this.filtersPopup = new paw.com.Popup("finder-filters-popup", {
      Component: paw.com.finder.FinderFiltersComponent,
      bind: {
        finderDef: self.finderDef,
        filterDefs: self.filterDefs,
        pinnedFilters: self.pinnedFilters,
        filtersConfig: self.filtersConfig,
        data: $.extend(true, {}, self.finderDef.data),
        localization: self.localization,
        onSearch: function (data) {
          self.findOnChange = true;
          $.extend(true, self.finderDef.data, data);
          utils.iterable.iterate(self.filterDefs, function (filterDef) {
            var pinnedFilter = self.pinnedFilters[filterDef.id];
            if (utils.value.isSet(pinnedFilter)) {
              pinnedFilter.inputField.refreshValue();
              return;
            }
          });

          self.filtersPopup.hide();
          self.find();
        },
        onClear: function () {
          utils.iterable.iterate(self.pinnedFilters, function (filter) {
            filter.refreshValue();
          });
          self.storeFiltersConfig();
        },
        onPinned: function (filter) {
          self.storeFiltersConfig();
          self.destroyPinnedFilters();
          self.createPinnedFilters();
        },
        createFilterFromDef: function (
          filterDef,
          data,
          $filter,
          pinned,
          $filtersHolder
        ) {
          return self.createFilterFromDef(
            filterDef,
            data,
            $filter,
            pinned,
            $filtersHolder
          );
        },
      },
      styleClass: "paw-finder-filters-popup",
      title: self.localization.filters,
      onClose: function () {
        self.findOnChange = true;
        self.filtersPopup = null;
      },
    });
    this.filtersPopup.show();
  };

  FinderComponent.prototype.initDatagrid = function () {
    var self = this;
    var $dt = this.$datagridHolder.find("table");
    var d = utils.async.defer();
    this.datatable = $dt.DataTable({
      responsive: false,
      ajax: this.finderDef.ajax
        ? this.finderDef.ajax($dt.attr("data-url"))
        : {
            method: this.finderDef.fetchMethod,
            url: $dt.attr("data-url"),
            data: function (d) {
              if (utils.value.isFunction(self.finderDef.datagridFilters)) {
                self.finderDef.datagridFilters(d);
              }
            },
          },
      order: [[1, "desc"]],
      columns: utils.iterable.map(this.gridColumns, function (colDef) {
        return colDef.def;
      }),
      stateSave: false,
      stateSaveCallback: undefined,
      stateLoadCallback: undefined,
      fixedHeader: true,
      scrollX: true,
      scrollY: this.$datagridHolder.parent().height() + "px",
      scroller: {
        rowHeight: "auto",
        displayBuffer: 0.5,
        loadingIndicator: true,
      },
      initComplete: function () {
        $.fn.dataTable.defaults.initComplete.apply(this, arguments);
        d.resolve();
      },
      fixedColumns: {
        leftColumns: utils.value.isSet(this.finderDef.fixedColumns)
          ? this.finderDef.fixedColumns
          : 1,
      },
    });
    return d.promise;
  };

  FinderComponent.prototype.fetchDatatableData = function () {
    var d = utils.async.defer();
    try {
      this.datatable.ajax.reload(function () {
        d.resolve();
      });
    } catch (err) {
      console.error("Finder error", err);
      d.reject();
    }
    return d.promise;
  };

  FinderComponent.prototype.find = function () {
    if (this.findOnChange) {
      return this.findDebouncer();
    }
  };

  FinderComponent.prototype.findDebounced = function () {
    var self = this;
    return this.fetchDatatableData().then(function () {
      self.storeFiltersConfig();
    });
  };

  FinderComponent.prototype.getFiltersStorageKey = function () {
    return (
      "paw-finder_" +
      this.finderDef.getId() +
      "_" +
      this.finderSettings.user.userId +
      "_" +
      this.finderSettings.user.domain
    );
  };

  FinderComponent.prototype.loadFiltersConfig = function () {
    var self = this;
    var filtersConfig = this.getFiltersConfig();
    if (utils.value.isSet(filtersConfig)) {
      this.filtersConfig = filtersConfig;

      utils.iterable.iterate(this.filterDefs, function (filterDef) {
        var filterConfig = self.filtersConfig[filterDef.id];
        if (!utils.value.isSet(filterConfig)) {
          return;
        }
        if (!filterConfig.ignoreStoreValue) {
          self.finderDef.data[filterConfig.id] = filterConfig.value;
        }
        filterDef.pinned = filterConfig.isPinned;
      });
    }
    return this.filtersConfig;
  };

  FinderComponent.prototype.getFiltersConfig = function () {
    var filtersConfig = localStorage.getItem(this.getFiltersStorageKey());
    if (utils.value.isSet(filtersConfig)) {
      return JSON.parse(filtersConfig);
    }
    return null;
  };

  FinderComponent.prototype.storeFiltersConfig = function () {
    var self = this;
    var filtersConfig = {};
    var removeIds = [];
    utils.iterable.iterate(this.filterDefs, function (filterDef) {
      filtersConfig[filterDef.id] = {
        id: filterDef.id,
        isPinned: filterDef.pinned,
        value: self.finderDef.data[filterDef.id],
      };
    });
    utils.iterable.iterate(removeIds, function (id) {
      delete filtersConfig[id];
    });
    localStorage.setItem(
      this.getFiltersStorageKey(),
      JSON.stringify(filtersConfig)
    );
  };

  paw.com.finder.FinderComponent = FinderComponent;
})(paw, paw.utils);
