"use strict";

/**
 * Licensed Materials - Property of IBM
 * IBM Cognos Products: Cognos Analytics
 * Copyright IBM Corp. 2015, 2018
 * US Government Users Restricted Rights -
 * Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */
define(['doT', 'q', 'jquery', 'underscore', 'bi/glass/app/ContentView', 'bi/commons/utils/BidiUtil', 'bacontentnav/utils/WidgetNavigator', 'bi/admin/nls/StringResource', 'bi/admin/common/actions/AddInputRow', 'text!bi/admin/common/ui/listview/templates/ListViewTemplate.html', 'text!bi/admin/common/ui/listview/templates/EmptyListViewTemplate.html', 'datatables', 'bi/commons/utils/Utils', 'bi/glass/utils/ClassFactory'], function (dot, Q, $, _, ContentView, BidiUtil, WidgetNavigator, StringResource, AddInputRow, ListViewTemplate, EmptyListViewTemplate, datatables, Utils, ClassFactory) {
  'use strict'; //NOSONAR: meant to be strict

  var ListView = ContentView.extend({
    aSelectedRows: [],
    multiSelect: true,
    touchMultiSelectEnabled: false,
    init: function init(options) {
      ListView.inherited('init', this, arguments);

      if (options !== undefined) {
        this.accessibleLabel = options.accessibleLabel || "";
        this.formatContent = options.formatContent || true;
      } else {
        this.accessibleLabel = "";
        this.formatContent = true;
      }

      $.extend(this, options);
      this.aSelectedRows = [];
    },
    createColumnObject: function createColumnObject(columnSpec, ColumnModule) {
      return new ColumnModule($.extend(columnSpec, {
        'listControl': this,
        'glassContext': this.glassContext
      }));
    },
    isMultitouchActive: function isMultitouchActive() {
      return this.multiSelect;
    },
    _createColumns: function _createColumns() {
      return Promise.all(_.map(this.dataAdaptor.getColumnSpecs(), function (colSpec, index) {
        if (colSpec.type || colSpec.module) {
          var path = colSpec.module || 'bi/content_apps/common/ui/list_columns/' + colSpec.type;
          return ClassFactory.instantiate(path, _.extend({}, colSpec, {
            'listControl': this,
            'glassContext': this.glassContext
          })).then(function (col) {
            delete col.type;
            return col.getSpec().then(function (spec) {
              _.extend(col, spec);

              col.aTargets = [index];
              col.bSortable = !!colSpec.sortable;

              if (colSpec.width) {
                col.sWidth = colSpec.width;
              }

              return col;
            });
          }.bind(this));
        } else {
          return null;
        }
      }.bind(this)));
    },
    setFocus: function setFocus() {
      var $firstFocusEl = this.$el.find("td[tabindex='0']");

      if ($firstFocusEl.length === 0) {
        return false;
      } else {
        $firstFocusEl.focus();
        return true;
      }
    },
    clearShowWorking: function clearShowWorking() {
      this.$el.find("tbody tr[role='row']").remove();
      this.showWorking();
    },
    showWorking: function showWorking() {
      var loadingAnimation = Utils.getLoadingAnimation(1);
      var $container = $('<div class="emptyTableContent bi-admin-working"></div>');
      $container.append(loadingAnimation);
      this.$el.append($container);
    },
    hideWorking: function hideWorking() {
      var workingEl = this.$el.find(".bi-admin-working");
      $(workingEl).remove();
    },
    render: function render() {
      this.showWorking();
      return Promise.all([this._createColumns(), this.dataAdaptor.getRows()]).then(function (resp) {
        this.$el.empty();
        var columns = resp[0];
        var rows = resp[1].rows;

        if (this.dataAdaptor.checkboxSelection && this.contentView.getSearchTerm) {
          var text = this.contentView.getSearchTerm().toLowerCase();
          rows = _.filter(rows, function (row) {
            return (row['defaultName'] || "").toLowerCase().indexOf(text) !== -1;
          });
        }

        var sHtml;
        this.hideWorking();

        if (rows.length > 0) {
          sHtml = dot.template(ListViewTemplate)({
            sortable: this.dataAdaptor.supportSorting,
            accessibleLabel: this.accessibleLabel,
            tableCaption: StringResource.get('listViewTableCaption'),
            columns: columns
          });
          this.$el.append(sHtml);
          return this._renderTable(columns, rows).then(function () {
            this.widgetKeyController = new WidgetNavigator({
              $el: this.$el.find(".listControl"),
              focusClass: "contentListFocusable",
              fCallBack: function fCallBack() {}
            });
          }.bind(this));
        } else {
          sHtml = '<div class="bi-admin-empty-activities-list-bee wft_bee"></div><div class="bi-admin-empty-activities-list-text">' + StringResource.get('noEntries') + '</div>';
          this.$el.append(sHtml);
        }
      }.bind(this));
    },
    resize: function resize() {
      var sh = this._calcYBound();

      var sb = this.$el.find('.dataTables_scrollBody');

      if (sb) {
        sb.height(sh);
      }
    },
    reload: function reload(append) {
      var deferred = Q.defer();
      this.showWorking();

      if (!this._dTable) {
        this.render().done(function () {
          this.hideWorking();
          deferred.resolve();
        }.bind(this));
      } else {
        if (this._reloading) {
          return;
        }

        this.waitForReload = true;
        this._reloading = true;

        if (!append) {
          this._dTable.clear();

          this._dTable.draw();
        }

        this.dataAdaptor.getRows().done(function (result) {
          var rows = result.rows;

          if (!append && rows.length === 0) {
            var emptyHTML = '<div class="emptyTableContent"><div class="bi-admin-empty-activities-list-bee wft_bee"></div><div class="bi-admin-empty-activities-list-text">' + StringResource.get('noEntries') + '</div></div>';
            this.$el.append(emptyHTML);
          } else {
            this.$el.find(".emptyTableContent").remove();

            this._dTable.rows.add(result.rows).draw(false);

            this._setTextDirection();

            this._clearRows(true);

            this.resize();
          }

          this._reloading = false;
          this.hideWorking();
          setTimeout(function () {
            delete this.waitForReload;
          }.bind(this), 1000);
          deferred.resolve();
        }.bind(this));
      }

      return deferred.promise;
    },
    _sortListener: function _sortListener() {},
    filter: function filter(text, propertyName) {
      if (this.dataAdaptor.checkboxSelection) {
        this._clearRows(true);
      }

      this.removeEmptyTableMessage();
      var rows = this.dataAdaptor.getFilteredRows();

      if (this._dTable && rows) {
        propertyName = propertyName || 'defaultName';
        text = text.toLowerCase();

        var filteredRows = _.filter(rows, function (row) {
          return (row[propertyName] || "").toLowerCase().indexOf(text) !== -1;
        });

        this._dTable.rows().remove();

        this._dTable.rows.add(filteredRows).draw(false);

        if (filteredRows.length < 1) {
          this._noResults();
        } else {
          this._setTextDirection();
        }
      }
    },
    _renderTable: function _renderTable(columns, rows) {
      //NOSONAR
      var deferred = Q.defer();
      var xbound = this.$el.width();
      var datatableSettings = {
        'data': rows,
        'sScrollY': this._calcYBound(),
        'sScrollX': xbound,
        'sDom': 'rtS',
        'bServerSide': false,
        'bFilter': false,
        'bInfo': false,
        'bAutoWidth': false,
        'bPaginate': false,
        'bDeferRender': true,
        'orderClasses': false,
        'bSort': this.dataAdaptor.isServerSorting(),
        'asStripeClasses': [''],
        'aoColumnDefs': columns,
        'fnInitComplete': function (oSettings) {
          this.isInitialized = true;
          var tabEl = $(oSettings.nScrollHead).find("table");
          this._scrollNode = this.$el.find('.dataTables_scrollBody');
          var lab = tabEl.attr("aria-label");
          tabEl.removeAttr("aria-label");
          tabEl.attr("aria-labelledby", "adminTab_tableHeaderLab1 adminTab_tableHeaderLab2 ");
          var hiddenDiv1 = $('<div style="position: absolute; left: -999px" aria-hidden="true" id="adminTab_tableHeaderLab1">' + lab + '</div>');
          var hiddenDiv2 = $('<div style="position: absolute; left: -999px" aria-hidden="true" id="adminTab_tableHeaderLab2">' + StringResource.get('listControlColumns') + '</div>');
          tabEl.parent().append(hiddenDiv1);
          tabEl.parent().append(hiddenDiv2);

          this._scrollNode.bind('scroll', this._onScroll.bind(this));

          deferred.resolve();
        }.bind(this),
        'fnDrawCallback': function (oSettings) {
          this._drawCallback(oSettings);
        }.bind(this)
      };
      var defaultOrder = this.dataAdaptor.sortOrder;

      if (datatableSettings.bSort && defaultOrder) {
        datatableSettings.aaSorting = defaultOrder;
      }

      var $listTable = this.$el.find('table.listControl.bi-admin-table-list'); // cache all indices the selected tr elements

      var aSelectedIndices = this._cachedSelectedRows();

      this._dTable = $listTable.DataTable(datatableSettings);
      $listTable.attr("role", "application");

      this._reApplySelections(aSelectedIndices); // process row events


      this._dTable.on('click', 'tr', this.handleClick.bind(this));

      this._dTable.on('keydown', 'tr', this._handlekeydown.bind(this));

      this._dTable.on('hold', this.handleHoldEvent.bind(this));

      var self = this; // Locate checkbox header (if supported)

      if (this.dataAdaptor.checkboxSelection) {
        this._renderSelectAllCheckBox();
      } // Customize datatable's sorting behavior


      if (this.dataAdaptor.supportSorting && !datatableSettings.bSort) {
        var $headers = this.$el.find("thead th");
        $headers.each(function () {
          var $h = $(this);
          var index = $h.attr('colindex') * 1;
          var col = columns[index];

          if (col && col.sortable) {
            $h.removeClass('sorting_disabled');

            if (defaultOrder && index === defaultOrder[0]) {
              $h.addClass('sorting_' + defaultOrder[1]);
            }

            $h.on('primaryaction', function () {
              var asc = $h.hasClass('sorting_asc');
              var nextOrder = asc ? 'desc' : 'asc';
              $headers.removeClass('sorting_asc sorting_desc');
              $h.addClass('sorting_' + nextOrder);
              self.dataAdaptor.setOrder(index, nextOrder);
              self.reload();
            });
          }
        });
      }

      this.$el.find('div.dataTables_scrollBody').on('scroll', function () {
        if ($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight && self.dataAdaptor.hasMore()) {
          self.reload(true);
        }
      });

      this._setTextDirection();

      return deferred.promise;
    },
    _isClickableCol: function _isClickableCol(evt) {
      var toElem = evt.relatedTarget || evt.toElement || evt.target;
      return $(toElem).hasClass('nameColumnDiv');
    },
    _handlekeydown: function _handlekeydown(evt) {
      if (evt.which === 13 || evt.which === 32) {
        this.handleClick(evt);
      }
    },
    handleClick: function handleClick(evt) {
      //NOSONAR
      var trNode = this._findRowNode(evt.currentTarget);

      if ($(evt.currentTarget).hasClass("ellipsesButton")) {
        if (this.aSelectedRows.length === 0) {
          this._selectRow(trNode);
        } else if (this.aSelectedRows.length === 1) {
          var match = false;

          _.each(this.aSelectedRows, function (oRow) {
            if ($(oRow)[0].rowIndex === trNode.rowIndex) {
              match = true;
            }
          });

          if (!match) {
            this._clearRows(true);

            this._selectRow(trNode);
          }
        }

        if (this._isClickableCol(evt)) {
          this._handleSingleSelect(trNode, evt);
        } else {
          this._handleContextMenu(evt);
        }

        return false;
      } // click still fired by browser for hold events on iPad, this is to catch and nullify the click


      if (evt.type === 'click' && this.multiSelectEvent && this.multiSelectEvent.type === "hold") {
        return;
      }

      if (evt.which === 3 && (evt.shiftKey || evt.ctrlKey || evt.metaKey || $(trNode).hasClass('selected'))) {
        return false;
      } // process <Shift>click


      if (evt.shiftKey && this.multiSelect) {
        // get last selected row
        var oLastSelectedRow = _.last(this.aSelectedRows);

        var iLastSelectedRowIndex = 0;

        if (oLastSelectedRow) {
          iLastSelectedRowIndex = oLastSelectedRow.rowIndex;
        } // clear all rows of 'selected' class


        this._clearRows(true);

        var iEndRow = Math.max(iLastSelectedRowIndex, trNode.rowIndex);
        var iStartRow = Math.min(iLastSelectedRowIndex, trNode.rowIndex); // Get filtered table rows

        var aNodes = this._dTable.$('tr', {
          "filter": "applied"
        });

        var i;

        for (i = iStartRow; i <= iEndRow; i = i + 1) {
          this._handleMultiSelect($(aNodes[i - 1]), evt.target);
        }
      } // process <Ctrl>click
      else if ((evt.ctrlKey || evt.metaKey) && this.multiSelect || evt.type === 'tap' && this.touchMultiSelectEnabled || this.dataAdaptor.checkboxSelection && evt.currentTarget.classList.contains('admin-checkbox')) {
        this._handleMultiSelect(trNode, evt.target);
      } // process normal mouse click
      else if (this._handleSingleSelect(trNode, evt) === false) {
        return false;
      }
    },
    _setTextDirection: function _setTextDirection() {
      var $divName = this.$el.find('div.nameColumnDiv');
      $divName.each(function () {
        $(this).attr('dir', BidiUtil.resolveBaseTextDir(this.innerHTML));
      });
    },
    _noResults: function _noResults() {
      var emptyHTML = '<div class="emptyTableContent"><div class="bi-admin-empty-activities-list-bee wft_bee"></div><div class="bi-admin-empty-activities-list-text">' + StringResource.get('noEntries') + '</div></div>';
      this.$el.append(emptyHTML);
    },
    _handleMultiSelect: function _handleMultiSelect(trNode, target, fromCheckbox) {
      // toggle 'selected' class of current row
      if (!($(trNode).hasClass('selected') && $(target).hasClass('forceRowSelection'))) {
        $(trNode).toggleClass('selected');

        if (this.dataAdaptor.checkboxSelection && !fromCheckbox) {
          var $checkbox = $(trNode).find('.admin-checkbox');
          $checkbox.prop('checked', !$checkbox.prop('checked'));
        }
      } // maintain the array of selected rows


      var iIndex = this.aSelectedRows.indexOf(trNode);

      if (iIndex > -1) {
        if (!$(target).hasClass('forceRowSelection')) {
          this._updateSelectedRows('-', iIndex);
        }
      } else {
        this._updateSelectedRows('+', trNode);
      }
    },
    _handleSingleSelect: function _handleSingleSelect(trNode, evt) {
      if (this.aSelectedRows.length === 1 && trNode.rowIndex === this.aSelectedRows[0].rowIndex) {
        if (this.deselectCallback) {
          this.deselectCallback();
        }

        $(trNode).removeClass('selected');

        this._checkRow(trNode, true);

        this._updateSelectedRows('c');
      }

      this._selectSingleRow(trNode, evt);
    },
    getActionPayload: function getActionPayload() {
      //method required by content apps column class...need to return a payload I guess
      var deferred = Q.defer(); //don't want the content apps column to do a thing....

      deferred.reject();
      return deferred.promise;
    },
    _selectSingleRow: function _selectSingleRow(trNode, evt) {
      this._clearRows(true);

      this._selectRow(trNode); //trigger event when row is highlighted


      this.$el.trigger('com.ibm.admin.listItemHighlighted', this.getSelectedObjects()[0]);

      if (this.singleSelectCallback && this._isClickableCol(evt)) {
        this.singleSelectCallback(this.getSelectedObjects()[0]);
      }
    },
    _handleContextMenu: function _handleContextMenu(evt) {
      var position = {};

      if (evt.pageX === undefined || evt.gesture === undefined || evt.gesture.center === undefined || evt.gesture.center.pageX === undefined) {
        position = $(evt.target).offset();
      } else {
        position.left = evt.pageX || evt.gesture.center.pageX;
        position.top = evt.pageY || evt.gesture.center.pageY;
      }

      var data = {
        'position': {
          "pageX": position.left,
          "pageY": position.top
        },
        'selectedObject': this.getSelectedObjects()
      };

      if (this.contextMenuCallback) {
        this.contextMenuCallback(data);
      }
    },
    handleHoldEvent: function handleHoldEvent(evt) {
      this._clearRows(true);

      this.touchMultiSelectEnabled = true;

      var node = this._findRowNode(evt.target.parentNode);

      $(node).addClass('selected');

      this._updateSelectedRows('+', node);

      this.multiSelectEvent = evt;
    },
    _findRowNode: function _findRowNode(node) {
      while (node.nodeName.toLowerCase() !== 'tr') {
        node = node.parentNode;
      }

      return node;
    },

    /**
    Clear all selected rows in dataTable
    **/
    _clearRows: function _clearRows(isFromAdmin) {
      if (isFromAdmin) {
        if (this.dataAdaptor.checkboxSelection) {
          var $checkboxHeader = this.$el.find('thead th .admin-header-checkbox input');
          $checkboxHeader.prop('checked', false);
        }

        _.each(this.aSelectedRows, function (oRow) {
          $(oRow).removeClass('selected');

          this._checkRow(oRow, false);
        }.bind(this));

        this._updateSelectedRows('c'); // clear button and counter for multi-select on touch devices


        this.touchMultiSelectEnabled = false;
      }
    },

    /**
    	Returns an array of objects that are being represented by the selected rows
    **/
    getSelectedObjects: function getSelectedObjects() {
      var i;
      var rowObjects = [];

      for (i = 0; i < this.aSelectedRows.length; i += 1) {
        var obj = this._dTable.row(this.aSelectedRows[i]).data();

        rowObjects.push(obj);
      }

      return rowObjects;
    },

    /**
    Select a row in dataTable
    **/
    _selectRow: function _selectRow(trNode) {
      $(trNode).addClass('selected');

      this._updateSelectedRows('+', trNode);

      if (this.dataAdaptor.checkboxSelection) {
        var $checkbox = $(trNode).find('.admin-checkbox');
        $checkbox.prop('checked', true);
      }
    },
    _formatContentHelper: function _formatContentHelper(oSettings, i, rowData) {
      //add in the role of gridcell to the td's
      if (rowData.anCells) {
        for (var cellIndex = 0; cellIndex < rowData.anCells.length; cellIndex += 1) {
          var $cell = $(rowData.anCells[cellIndex]);
          var scope = oSettings.aoColumns[cellIndex].scope;

          if (scope === 'row') {
            $cell.attr("role", "rowheader");
          } else {
            $cell.attr("role", "gridcell");
          }

          $cell.attr("tabindex", "-1");
        }
      }

      this._processColumnWeights(oSettings);

      if (this.formatContent && rowData.anCells && rowData.anCells[i]) {
        oSettings.aoColumns[i].formatContent(rowData.anCells[i]);
      }
    },
    _drawCallback: function _drawCallback(oSettings) {
      for (var i = 0; i < oSettings.aoColumns.length; i++) {
        if (oSettings.aoColumns[i].formatContent) {
          oSettings.aoData.forEach(this._formatContentHelper.bind(this, oSettings, i));
        }
      }

      if (this.widgetKeyController) {
        this.widgetKeyController.setInitialTabIndex();
      }
    },
    _processColumnWeights: function _processColumnWeights(oSettings) {
      var weightSum = 0;
      var percentSum = 0;
      oSettings.aoColumns.forEach(function (column) {
        if (column.weight) {
          weightSum += column.weight;
        } else if (column.sWidth && column.sWidth.slice(-1) === '%') {
          percentSum += parseInt(column.sWidth.slice(0, -1), 10);
        }
      });
      oSettings.aoColumns.forEach(function (column) {
        if (column.weight) {
          column.sWidth = Math.floor(column.weight / weightSum * (100 - percentSum)) + '%';
        }
      });
    },
    _calcYBound: function _calcYBound() {
      var headerHeight = this.$el.find('th').height();

      if (this.$el.height() - headerHeight < headerHeight) {
        return "70%";
      } else {
        return this.$el.height() - headerHeight;
      }
    },
    addInput: function addInput(type, pid, accountExplorer, listAdaptor) {
      //running multiple instances of AddInputRow simultaneously leads to unnecessary bugs,
      //therefore, only manage one instance at a time, instances will terminate when finished
      if (this.activeInputForm === null) {
        this.activeInputForm = new AddInputRow({
          'oListControl': this,
          'glassContext': this.glassContext,
          'data': this._dTable.rows().data(),
          'accountExplorer': accountExplorer,
          'listAdaptor': listAdaptor
        });
        this.activeInputForm.execute(type, pid);
      }
    },
    insertToTable: function insertToTable(rowObj) {
      this._dTable.rows.add([rowObj]).draw(true);
    },
    removeEmptyTableMessage: function removeEmptyTableMessage() {
      this.$el.find('.emptyTableContent').remove();
    },
    adjustScrollForPageDown: function adjustScrollForPageDown() {
      var $sb = $(this.$el.find('.dataTables_scrollBody'));
      $sb.scrollTop($sb.scrollTop() + 50);
    },
    _clearTable: function _clearTable() {
      this._dTable.clear().draw();
    },
    setPaging: function setPaging(isPaged) {
      this.pagingOn = isPaged;
    },
    _onScroll: function _onScroll(event) {
      if (!this.scrollIgnore && !this.waitForReload) {
        var $target = $(event.target); // If we've scrolled to the bottom

        if ($target.scrollTop() + $target.innerHeight() >= $target[0].scrollHeight - 20) {
          $(this).trigger("nextPage", event);
          this.scrollIgnore = true;
          setTimeout(function () {
            delete this.scrollIgnore;
          }.bind(this), 500);
        } else if ($target.scrollTop() === 0) {
          $(this).trigger("previousPage", event);
          this.scrollIgnore = true;
          setTimeout(function () {
            delete this.scrollIgnore;
          }.bind(this), 500);
        }
      }
    },
    _isShortTable: function _isShortTable() {
      var listContainer = this.$el.find('.ca-listContainer');
      return listContainer.height() < 200;
    },
    _updateSelectedRows: function _updateSelectedRows(action, options) {
      switch (action) {
        case '-':
          var removeAtIndex = this.aSelectedRows.indexOf(options);

          if (removeAtIndex !== -1) {
            this.aSelectedRows.splice(removeAtIndex, 1);
          }

          break;

        case '+':
          this.aSelectedRows.push(options);
          break;

        case 'c':
          this.aSelectedRows = [];
          break;

        case '=':
          this.aSelectedRows = options;
          break;

        default:
          break;
      }

      if (this.onSelectionChange) {
        this.onSelectionChange();
      }
    },
    _cachedSelectedRows: function _cachedSelectedRows() {
      // cache all indices the selected tr elements
      var aSelectedIndices = [];

      if (this._dTable && this.aSelectedRows.length > 0) {
        var selectedRows = this._dTable.$('tr.selected');

        for (var index = 0; index < selectedRows.length; ++index) {
          aSelectedIndices.push($(selectedRows[index]).index());
        }
      }

      return aSelectedIndices;
    },
    _reApplySelections: function _reApplySelections(aSelectedIndices) {
      this.aSelectedRows = []; // reapply previous selections

      for (var index = 0; index < aSelectedIndices.length; ++index) {
        var trNode = this._dTable.$('tr')[aSelectedIndices[index]];

        trNode.classList.add('selected');

        this._checkRow(trNode, true);

        this._updateSelectedRows('+', trNode);
      }
    },
    _renderSelectAllCheckBox: function _renderSelectAllCheckBox() {
      var $checkboxHeader = this.$el.find('thead th .admin-header-checkbox:not(:has(input))');
      $checkboxHeader.html('<input role="checkbox" tabindex="-1" type="checkbox" class="admin-clickable admin-checkbox"></input>');
      $checkboxHeader.find('input').click(this._toggleSelectAll.bind(this));
    },
    _toggleSelectAll: function _toggleSelectAll(evt) {
      if (evt.currentTarget.checked) {
        var rows = this._dTable.$('tr:not(.selected)');

        for (var index = 0; index < rows.length; ++index) {
          this._selectRow(rows[index]);
        }
      } else {
        this._clearRows(true);
      }
    },
    _deselectRow: function _deselectRow(trNode) {
      if (this.deselectCallback) {
        this.deselectCallback();
      }

      $(trNode).removeClass('selected');

      this._checkRow(trNode, false);

      this._updateSelectedRows('-', trNode);
    },
    _checkRow: function _checkRow(trNode, check) {
      if (this.dataAdaptor.checkboxSelection) {
        var $checkbox = $(trNode).find('.admin-checkbox');
        $checkbox.prop('checked', check);
      }
    }
  });
  return ListView;
});