/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2019 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
(function() {

/* global Slick: true */

"use strict";

var ui = angular.module('axelor.ui');

ui.controller('GridViewCtrl', GridViewCtrl);

ui.GridViewCtrl = GridViewCtrl;
ui.GridViewCtrl.$inject = ['$scope', '$element'];

function GridViewCtrl($scope, $element) {

  ui.DSViewCtrl('grid', $scope, $element);

  var ds = $scope._dataSource;
  var page = {};

  $scope.dataView = new Slick.Data.DataView();
  $scope.selection = [];

  ds.on('change', function(e, records, page){
    $scope.setItems(records, page);
  });

  var initialized = false;
  var reloadDotted = false;

  $scope.onShow = function(viewPromise) {

    if (!initialized) {

      viewPromise.then(function(){
        var view = $scope.schema,
          params = $scope._viewParams,
          sortBy = view.orderBy,
          pageNum = null;

        if (sortBy) {
          sortBy = sortBy.split(',');
        }
        if (params.options && params.options.mode === 'list') {
          pageNum = params.options.state;
          $scope._routeSearch = params.options.search;
        }

        reloadDotted = params.params && params.params['reload-dotted'];

        $scope.view = view;

        if (view.noFetch) return;

        var opts = ds._filter ? ds._filter : {
          _sortBy: sortBy,
          _pageNum: pageNum
        };

        $scope.filter(opts).then(function(){
          $scope.$broadcast('on:grid-selection-change', $scope.getContext());
          $scope.updateRoute();
        });
      });

      initialized = true;
    } else {
      if (reloadDotted || ($scope._viewTypeLast && $scope._viewTypeLast !== 'grid')) {
        return $scope.reload().then(function() {
          $scope.updateRoute();
        });
      }
      var current = $scope.dataView.getItem(_.first($scope.selection));
      if (current && current.id) {
        $scope.dataView.updateItem(current.id, current);
      }
    }
  };

  $scope.getRouteOptions = function() {
    var pos = 1,
      args = [],
      query = {},
      params = $scope._viewParams;

    if (page && page.limit) {
      pos = (page.from / page.limit) + 1;
    } else if (params.options && params.options.mode === 'list') {
      pos = +(params.options.state);
    }

    pos = pos || 1;
    args = [pos];

    return {
      mode: 'list',
      args: args,
      query: query
    };
  };

  $scope._routeSearch = {};
  $scope.setRouteOptions = function(options) {
    var opts = options || {},
      pos = +(opts.state),
      current = (page.from / page.limit) + 1;

    pos = pos || 1;
    current = current || 1;

    $scope._routeSearch = opts.search;
    if (pos === current) {
      return $scope.updateRoute();
    }

    var params = $scope._viewParams;
    if (params.viewType !== "grid") {
      return $scope.show();
    }

    $scope.filter({
      _pageNum: pos
    });
  };

  $scope.getItem = function(index) {
    return $scope.dataView.getItem(index);
  };

  $scope.getItems = function() {
    return $scope.dataView.getItems();
  };

  $scope.setItems = function(items, pageInfo) {

    var dataView = $scope.dataView;
    var selection = $scope.selection || [];
    var selectionIds = dataView.mapRowsToIds(selection);
    var hasSelected = _.some(items, function (item) { return item.selected; });
    var syncSelection = function () {
      if (dataView.$syncSelection) {
        setTimeout(function(){
          if (hasSelected) {
            dataView.$syncSelection();
          } else {
            dataView.$syncSelection(selection, selectionIds);
          }
        });
      }
    };

    //XXX: clear existing items (bug?)
    if (dataView.getLength()) {
      dataView.beginUpdate();
      dataView.setItems([]);
        dataView.endUpdate();
    }

    dataView.beginUpdate();
      dataView.setItems(items);
      dataView.endUpdate();

    if (pageInfo) {
        page = pageInfo;
    }

    var details = $scope.$details;
    if (details) {
      details.$timeout(function () {
        var record = details.record || {};
        var found = _.findWhere(items, { id: record.id });
        if (found) {
          found.selected = true;
          return;
        }
        details.edit(null);
        syncSelection();
      });
    } else {
      syncSelection();
    }

    $scope.$broadcast('grid:adjust-columns');
  };

  $scope.attr = function (name) {
    if (!$scope.schema || $scope.schema[name] === undefined) {
      return true;
    }
    return $scope.schema[name];
  };

  $scope.canNew = function() {
    return $scope.hasButton('new');
  };

  $scope.canEdit = function() {
    return $scope.hasButton('edit') && $scope.selection.length > 0;
  };

  $scope.canShowDetailsView = function () {
    var params = ($scope._viewParams || {}).params || {};
    return params['details-view'] && !axelor.device.mobile;
  };

  $scope.canShowSave = function () {
    if ($scope.$details) {
      return true;
    }
    return $scope.hasButton('save') && $scope.canEditInline();
  };

  $scope.canSave = function() {
    if ($scope.$details && $scope.$details.canSave()) {
      return true;
    }
    return $scope.hasButton('save') && this.dataView.canSave && this.dataView.canSave();
  };

  $scope.canDelete = function() {
    return $scope.hasButton('delete') && !$scope.canSave() && $scope.selection.length > 0;
  };

  $scope.canArchive = function() {
    return $scope.hasPermission('write')
      && $scope.hasButton('archive')
      && !$scope.canSave()
      && $scope.selection.length > 0;
  };

  $scope.canUnarchive = function() {
    return $scope.canArchive();
  };

  $scope.canEditInline = function() {
    return _.isFunction(this.dataView.canSave);
  };

  $scope.canMassUpdate = function () {
    // this permission is actually calculated from fields marked for mass update
    return $scope.hasPermission('massUpdate', false) || ($scope.schema && $scope.schema.canMassUpdate);
  };

  $scope.canExport = function() {
    return $scope.hasPermission('export');
  };

  $scope.selectFields = function() {
    return _.map($scope.fields, function (field) {
      if (field.jsonField) {
        return field.name + '::' + (field.jsonType || 'text');
      }
      return field.name;
    });
  };

  $scope.filter = function(searchFilter) {

    var fields = $scope.selectFields(),
      options = {};

    function fixPage() {
      var promise = ds.fixPage();
      if (promise) {
        $scope.updateRoute();
        return promise;
      }
    }

    // if criteria is given search using it
    if (searchFilter.criteria || searchFilter._domains) {
      options = {
        filter: searchFilter,
        fields: fields
      };
      if (searchFilter.archived !== undefined) {
        options.archived = searchFilter.archived;
      }
      return ds.search(options).then(fixPage);
    }

    var filter =  {},
      sortBy, pageNum,
      domain = null,
      context = null,
      action = null,
      criteria = {
        operator: 'and'
      };

    for(var name in searchFilter) {
      var value = searchFilter[name];
      if (value !== '') filter[name] = value;
    }

    pageNum = +(filter._pageNum || 0);
    sortBy = filter._sortBy;
    domain = filter._domain;
    context = filter._context;
    action = filter._action;

    delete filter._pageNum;
    delete filter._sortBy;
    delete filter._domain;
    delete filter._context;
    delete filter._action;

    criteria.criteria = _.map(filter, function(value, key) {

      var field = $scope.fields[key] || _.findWhere(($scope.schema||{}).items, { name: key }) || {};
      var type = field.type || 'string';
      var operator = 'like';
      var origValue = value;
      var value2;

      //TODO: implement expression parser

      if (type === 'many-to-one' && !field.jsonField) {
        if (field.targetName) {
          key = key + '.' + field.targetName;
        } else {
          console.warn("Can't search on field: ", key);
        }
      }
      if (field.selection) {
        type = 'selection';
      }

      function stripOperator(val) {
        var match = /(<)(.*)(<)(.*)/.exec(val);
        if (match) {
          operator = 'between';
          value2 = match[2].trim();
          return match[4].trim();
        }
        match = /(<=?|>=?|=)(.*)/.exec(val);
        if (match) {
          operator = match[1];
          return match[2].trim();
        }
        return val;
      }

      function toMoment(val) {
        var format = 'MM/YYYY';
        if (/\d+\/\d+\/\d+/.test(val)) format = 'DD/MM/YYYY';
        if (/\d+\/\d+\/\d+\s+\d+:\d+/.test(val)) format = 'DD/MM/YYYY HH:mm';
        return val ? moment(val, format) : moment();
      }

      function toDate(val) {
        return val ? toMoment(val).format('YYYY-MM-DD') : val;
      }

      function toDateString(val) {
          return moment.utc(val, 'DD/MM/YYYY').toDate().toISOString().split("T")[0];
      }

      switch(type) {
        case 'integer':
        case 'long':
        case 'decimal':
          operator = '=';
          value = stripOperator(value);
          value = +(value) || 0;
          if (value2) value2 = +(value2) || 0;
          break;
        case 'boolean':
          operator = '=';
          value = !/f|n|false|no|0/.test(value);
          break;
        case 'date':
          operator = '=';
          value = stripOperator(value);
          if (value) value = toDateString(value);
          if (value2) value2 = toDateString(value2);
          break;
        case 'time':
          operator = '=';
          break;
        case 'datetime':
          operator = 'between';
          value = stripOperator(value);
          var val = toMoment(value);
          value = (operator == 'between' ? val.startOf('day') : val).toDate().toISOString();
          value2 = (operator == 'between' ? val.endOf('day') : val).toDate().toISOString();
          break;
        case 'enum':
        case 'selection':
          operator = '=';
          break;
      }

      // tag json fields
      if (field.jsonField) {
        key += '::' + (field.jsonType || 'text');
      }

      return {
        fieldName: key,
        operator: operator,
        value: value,
        value2: value2
      };
    });

    domain = domain || $scope._domain;
    context = _.extend({}, $scope._context, context);

    if (domain && $scope.getContext) {
      context = _.extend(context, $scope.getContext());
    }

    context._model = context._model || $scope._model;

    options = {
      filter: criteria,
      fields: fields,
      sortBy: sortBy,
      domain: domain,
      context: context,
      action: action
    };

    if (pageNum) {
      options.offset = (pageNum - 1 ) * ds._page.limit;
    }

    var advance = arguments.length > 1 ? arguments[1] : null;
    if (advance && advance.criteria && advance.criteria.length) {
      if (_.isEmpty(criteria.criteria)) {
        options.filter = advance;
      } else {
        criteria.criteria = [{
          operator: criteria.operator,
          criteria: criteria.criteria
        }, {
          operator: advance.operator,
          criteria: advance.criteria
        }];
        criteria.operator = "and";
      }
    }
    if (advance && advance.archived !== undefined) {
      options.archived = advance.archived;
    }
    return ds.search(options).then(fixPage);
  };

  $scope.pagerText = function() {
    if (page && page.from !== undefined) {
      if (page.total === 0) return null;
      return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
    }
  };

  $scope.pagerIndex = function(fromSelection) {
    var index = page.index,
      record = null;
    if (fromSelection) {
      record = $scope.dataView.getItem(_.first($scope.selection));
      index = ds._data.indexOf(record);
    }
    return index;
  };

  $scope.onNext = function() {
    var fields = $scope.selectFields();
    ds.next(fields).then(function(){
      $scope.updateRoute();
    });
  };

  $scope.onPrev = function() {
    var fields = $scope.selectFields();
    ds.prev(fields).then(function(){
      $scope.updateRoute();
    });
  };

  $scope.onNew = function() {
    page.index = -1;
    if ($scope.$details) {
      $scope.$details.onNew();
      return;
    }
    $scope.switchTo('form', function(viewScope){
      $scope.ajaxStop(function(){
        $scope.$timeout(function(){
          viewScope.$broadcast('on:new');
        });
      });
    });
  };

  $scope.onEdit = function(force) {
    page.index = $scope.pagerIndex(true);
    $scope.switchTo('form', function (formScope) {
      formScope.__canForceEdit = force;
    });
  };

  $scope.$confirmMessage = _t("Do you really want to delete the selected record(s)?");
  $scope.$confirmArchiveMessage = _t("Do you really want to archive the selected record(s)?");
  $scope.$confirmUnarchiveMessage = _t("Do you really want to unarchive the selected record(s)?");

  $scope.onDelete = function() {
    var message = $scope.$confirmMessage;
    var message = _.isFunction(message) ? message() : message;

    axelor.dialogs.confirm(message, function(confirmed){

      if (!confirmed)
        return;

      var selected = _.map($scope.selection, function(index) {
        return $scope.dataView.getItem(index);
      });

      ds.removeAll(selected).success(function(records, page){
        if (records.length === 0 && page.total > 0) {
          $scope.onRefresh();
        }
      });
    });
  };

  $scope._doArchive = function (message, archive) {
    axelor.dialogs.confirm(message, function(confirmed) {
      if (!confirmed) {
        return;
      }

      var selected = _.map($scope.selection, function(index) {
        var item = $scope.dataView.getItem(index);
        return _.extend({}, _.pick(item, 'id', 'version'), { archived: archive });
      });

      ds.saveAll(selected).success(function() {
        $scope.onRefresh();
      });
    });
  };

  $scope.onArchive = function() {
    $scope._doArchive($scope.$confirmArchiveMessage, true);
  };

  $scope.onUnarchive = function() {
    $scope._doArchive($scope.$confirmUnarchiveMessage, false);
  };

  $scope.onRefresh = function() {
    if ($scope.$details) {
      $scope.$details.onNew().then(function () {
        $scope.reload();
      });
    } else {
      $scope.reload();
    }
  };

  $scope.isDirty = function () {
    if ($scope.$details && $scope.$details.isDirty) {
      return $scope.$details.isDirty();
    }
    return false;
  };

  $scope.confirmDirty = function(callback, cancelCallback) {
    if ($scope.$details && $scope.$details.confirmDirty) {
      return $scope.$details.confirmDirty(callback, cancelCallback);
    }
    return callback();
  };

  $scope.reload = function() {
    var fields = $scope.selectFields();
    return ds.search({
      fields: fields
    });
  };

  $scope.onSort = function(event, args) {
    var fields = $scope.selectFields();
    var sortBy = _.map(args.sortCols, function(column) {
      var field = column.sortCol.descriptor;
      var name = column.sortCol.field;
      if (field.jsonField) {
        if (field.type === 'many-to-one' && field.targetName) {
          name = name + "." + field.targetName;
        }
        name += '::' + ('integer,boolean,decimal'.indexOf(field.type) > -1 ? field.type : 'text');
      }
      var spec = column.sortAsc ? name : '-' + name;
      return spec;
    });
    ds.search({
      sortBy: sortBy,
      fields: fields
    });
  };

  $scope.onSelectionChanged = function(event, args) {
    var items = $scope.getItems();
    var selection = [];

    _.each(items, function (item) {
      item.selected = false;
    });
    _.each(args.rows, function(index) {
      var item = args.grid.getDataItem(index);
      if (item && item.id && item.id !== 0) {
        item.selected = true;
        selection.push(index);
      }
    });

    $scope.selection = selection;
    $scope.$timeout(function () {
      $scope.$broadcast('on:grid-selection-change', $scope.getContext());
    });

    if ($scope.$details) {
      $scope.$details.selectionChanged(selection);
    }
  };

  $scope.onItemClick = function(event, args) {
    if (axelor.device.mobile) {
      $scope.$timeout(function () {
        $scope.onEdit();
      });
    }
  };

  $scope.onItemDblClick = function(event, args) {
    $scope.onEdit();
    $scope.$applyAsync();
  };

  var _getContext = $scope.getContext;
  $scope.getContext = function() {

    // if nested grid then return parent's context
    if (_getContext) {
      return _getContext();
    }

    var dataView = $scope.dataView;
    var selected = _.map($scope.selection || [], function(index) {
      return dataView.getItem(index).id;
    });

    return selected.length ? { _ids: selected } : {};
  };

  $scope.getActionData = function() {
    // ignore if nested grid or has selected rows
    if (_getContext || !_.isEmpty($scope.selection)) {
      return false;
    }
    return _.extend({
      _domain: ds._lastDomain,
      _domainContext: ds._lastContext,
      _archived: ds._showArchived
    }, ds._filter);
  };

  $scope.onSave = function() {
    if ($scope.$details) {
      $scope.$details.onSave();
    }
    if ($scope.dataView.saveChanges) {
      $scope.dataView.saveChanges();
    }
  };

  $scope.onArchived = function(e) {
    var button = $(e.currentTarget);
    setTimeout(function(){
      var active = button.is('.active');
      var fields = $scope.selectFields();
      ds.search({
        fields: fields,
        archived: active
      });
    });
  };

  $scope.onExport = function (full) {
    var view = $scope.view || $scope.schema || {};
    if (!view.items) return;

    var names = _.pluck(view.items, 'name');
    var fields = full
      ? []
      : _.chain($scope.getVisibleCols())
         .map(function (col) { return (col.descriptor||{}).name; })
         .compact()
         .filter(function (name) { return names.indexOf(name) > -1; })
         .value();

    return ds.export_(fields).success(function(res) {
      var fileName = res.fileName;
      var filePath = 'ws/rest/' + $scope._model + '/export/' + fileName;
      if (ds._page.total > res.exportSize) {
        axelor.notify.alert(_t("{0} records exported.", res.exportSize), { title: _t('Warning!') });
      }
      ui.download(filePath, fileName);
    });
  };

  function focusFirst() {
    var index = _.first($scope.selection) || 0;
    var first = $scope.dataView.getItem(index);
    if (first) {
      $scope.dataView.$syncSelection([], [first.id], true);
    }
  }

  $scope.onHotKey = function (e, action) {
    if (action === "save" && $scope.canSave()) {
      $scope.onSave();
    }
    if (action === "refresh") {
      $scope.onRefresh();
    }
    if (action === "new") {
      $scope.onNew();
    }
    if (action === "edit") {
      if ($scope.canEdit()) {
        $scope.onEdit(true);
      } else {
        focusFirst();
      }
    }
    if (action === "delete" && $scope.canDelete()) {
      $scope.onDelete();
    }
    if (action === "select") {
      focusFirst();
    }
    if (action === "prev" && $scope.canPrev()) {
      $scope.onPrev();
    }
    if (action === "next" && $scope.canNext()) {
      $scope.onNext();
    }

    $scope.$applyAsync();
    return false;
  };
}

ui.directive('uiViewGrid', function(){
  return {
    replace: true,
    template: '<div ui-slick-grid ui-widget-states></div>'
  };
});

ui.directive('uiViewDetails', ['DataSource', 'ViewService', function(DataSource, ViewService) {
  return {
    scope: {},
    controller: ['$scope', '$element', function ($scope, $element) {
      var parent = $scope.$parent;
      var params =  _.pick(parent._viewParams, ['views', 'model', 'domain', 'context', 'params']);

      var view = _.findWhere(params.views, { type: 'form' }) || { type: 'form' };
      if (params.params && _.isString(params.params['details-view'])) {
        view = { type: 'form', name: params.params['details-view']};
      }

      params.views = [view];
      $scope._viewParams = params;
      $scope._isDetailsForm = true;

      ui.ViewCtrl.call(this, $scope, DataSource, ViewService);

      // use same ds as grid
      $scope._dataSource = parent._dataSource;

      ui.FormViewCtrl.call(this, $scope, $element);

      parent.$parent.$details = $scope;

      var ds = $scope._dataSource;
      var noop = angular.noop;

      $scope.getRouteOptions = noop;
      $scope.setRouteOptions = noop;
      $scope.updateRoute = noop;
      $scope.$locationChangeCheck = noop;
      $scope.switchBack = noop;
      $scope.switchTo = noop;
      $scope.onHotKey = noop;

      $scope._dataSource = parent._dataSource;

      $scope.setEditable(true);
      $scope.show();

      function doEdit(index) {
        var found = ds.at(index);
        var record = $scope.record;
        if (record && found.id === record.id) return;
        $scope.doRead(found.id).success(function(record) {
          $scope.edit(record);
        });
      }

      $scope.selectionChanged = _.debounce(function (selection) {
        var current = $scope.record || {};
        var first = _.first(selection);
        if (first !== undefined) {
          doEdit(first);
        } else if (current.id > 0) {
          $scope.edit(null);
        }
      }, 300);

      $scope.$on("on:new", function(e) {
        var dataView = parent.dataView;
        if (dataView && dataView.$syncSelection) {
          dataView.$syncSelection([], [], true);
        }
      });

      $scope.$on("on:edit", function(e) {
        var record = $scope.record || {};
        var dataView = parent.dataView;
        if (dataView && record.id > 0) {
          var found = _.findWhere(dataView.getItems(), { id: record.id });
          if (found) {
            found.selected = true;
          }
          dataView.$syncSelection([], [], false);
        }
      });
    }],
    link: function (scope, element, attrs) {
      var overlay = $("<div class='slickgrid-overlay'>");
      scope.waitForActions(function () {
        element.parent().children('.slickgrid').append(overlay);
      });

      scope.$watch('$$dirty', function gridDirtyWatch(dirty) {
        overlay.toggle(dirty);
      });
    },
    replace: true,
    templateUrl: "partials/views/details-form.html"
  };
}]);

ui.directive('uiPortletGrid', function(){
  return {
    controller: ['$scope', '$element', 'ViewService', 'NavService', 'MenuService',
                 function($scope, $element, ViewService, NavService, MenuService) {

      GridViewCtrl.call(this, $scope, $element);

      var ds = $scope._dataSource;

      function doEdit(force) {
        var promise = MenuService.action($scope._viewAction, {
          context: $scope.getContext()
        });

        promise.success(function (result) {
          if (!result.data) return;
          var view = result.data[0].view;
          return doOpen(force, view);
        });
      }

      function doOpen(force, tab) {
        var index = $scope.pagerIndex(true);
        var record = ds.at(index);

        if ($scope._viewAction === "dms.file.children") {
          NavService.openTabByName("dms.file", {
            mode: "edit",
            state: record.id
          });
          return;
        }

        tab.viewType = "form";
        tab.recordId = record.id;
        tab.action = _.uniqueId('$act');
        tab.forceReadonly = $scope.isReadonly && $scope.isReadonly();

        if (tab.forceReadonly || $scope._isPopup || (($scope._viewParams || {}).params || {}).popup) {
          tab.$popupParent = $scope;
          tab.params = tab.params || {};
          _.defaults(tab.params, {
            'show-toolbar': false
          });
        }

        setTimeout(function(){
          NavService.openView(tab);
          $scope.$applyAsync();
          if (force) {
            $scope.waitForActions(function() {
              var scope = tab.$viewScope || ($scope.selectedTab || {}).$viewScope;
              if (scope && scope.onEdit) {
                scope.onEdit();
              }
            });
          }
        });
      }

      $scope.showPager = true;
      $scope.onEdit = doEdit;
      $scope.onItemDblClick = function(event, args) {
        doEdit(false);
      };

      $scope.$on("on:new", function(e) {
        ds._page.from = 0;
        $scope.onRefresh();
      });
      $scope.$on("on:edit", function(e) {
        ds._page.from = 0;
        $scope.onRefresh();
      });

      var unwatch = false;
      var loading = false;

      $scope.onRefresh = function () {
        var tab = NavService.getSelected();
        var type = $scope.$parent._viewType
        if (['dashboard', 'form'].indexOf(type) === -1) {
          if (unwatch) {
            unwatch();
            unwatch = null;
          }
          return;
        }

        if (unwatch || loading) {
          return;
        }

        unwatch =  $scope.$watch(function gridVisibleWatch() {
          if ($element.is(":hidden")) {
            return;
          }

          unwatch();
          unwatch = null;
          loading = true;

          $scope.waitForActions(function () {
            $scope.ajaxStop(function () {
              loading = false;
              $scope.filter({});
            });
          });
        });
      };

      var _onShow = $scope.onShow;
      var _filter = $scope.filter;
      var _action = $scope._viewAction;
      var _field = $scope.field || {};

      $scope.onGridInit = function (grid, inst) {
        var editCol = _.findWhere(inst.cols, {id: '_edit_column'}) || {};
        editCol.descriptor = { hidden : true };
        $scope.$parent.$watch("isReadonly()", function (readonly) {
          if (inst.editable) {
            grid.setOptions({ editable: readonly });
          }
          inst.showColumn('_edit_column', !readonly);
          editCol.descriptor = { hidden : readonly };
        });
      };

      $scope.filter = function (searchFilter) {
        var opts = _.extend({}, searchFilter, {
          _action: _action
        });
        var ds = $scope._dataSource;
        var view = $scope.schema || {};
        if (!opts._sortBy && !ds._sortBy && view.orderBy) {
          opts._sortBy = view.orderBy.split(',');
        }
        if ($scope._context && $scope.formPath && $scope.getContext) {
          opts._context = _.extend({id: null}, _.pick($scope.getContext(), _.keys($scope._context)));
          if ($scope._context._id) {
            opts._context._id = opts._context.id;
          }
        }
        return _filter.call($scope, opts);
      };

      $scope.onShow = function () {
        var scope = ($scope.selectedTab || {}).$viewScope;
        if (scope && scope.editRecord) {
          return;
        }
        return _onShow.apply($scope, arguments);
      };
    }],
    replace: true,
    template:
    '<div class="portlet-grid">'+
      '<div ui-view-grid x-view="schema" x-on-init="onGridInit" x-data-view="dataView" x-editable="false" x-no-filter="{{noFilter}}" x-handler="this"></div>'+
    '</div>'
  };
});

ui.directive('uiTopHelp', function () {
  return {
    link: function (scope, element) {
      var unwatch = scope.$watch('schema', function gridSchemaWatch(view) {
        if (view) {
          unwatch();
        }
        if (view && view.help) {
          element.popover({
            html: true,
            title: view.title,
            content: view.help,
            placement: 'bottom',
            trigger: 'hover',
            delay: { show: 500, hide: 100 },
            container: 'body'
          });
        }
      });
    },
    replace: true,
    template:
      "<button ng-show='schema.help'>" +
        "<i class='fa fa-info'></i>" +
      "</button>"
  };
});

})();
