/*
 * 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() {

"use strict";

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

ui.RefFieldCtrl = RefFieldCtrl;

function RefFieldCtrl($scope, $element, DataSource, ViewService, initCallback) {

  var field = $scope.getViewDef($element),
    params = {
      model: field.target || $element.attr('x-target'),
      views: field.views || {},
      domain: field.domain,
      context: field.context
    },
    views = {};

  if (field.jsonTarget) {
    params.context = _.extend({}, params.context, { jsonModel: field.jsonTarget });
  }

  if (!$element.is('fieldset')) {

    _.each(field.views, function(view){
      views[view.type] = view;
    });

    var formView = null,
      gridView = null,
      summaryView = null;

    if (field.summaryView === "" || field.summaryView === "true") {
      summaryView = views.form;
    }

    if (field.gridView) {
      gridView = {
        type: 'grid',
        name: field.gridView
      };
    }
    if (field.formView) {
      formView = {
        type: 'form',
        name: field.formView
      };
    }

    if (field.summaryView === "" || field.summaryView === "true") {
      summaryView = views.form || formView || { type: 'form' };
    } else if (field.summaryView) {
      summaryView = {
        type: "form",
        name: field.summaryView
      };
    }

    views.form = formView || views.form;
    views.grid = gridView || views.grid;
    params.summaryView = angular.copy(summaryView);
    params.summaryViewDefault = params.summaryView || views.form;

    params.views = _.compact([views.grid, views.form]);
    $scope._viewParams = params;
  }

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

  $scope.ngModel = null;
  $scope.editorCanSave = true;
  $scope.editorCanReload = field.canReload;

  if (initCallback) {
    initCallback.call(this);
  }

  var editor = null;
  var selector = null;
  var embedded = null;

  $scope.createNestedEditor = function() {
    return null;
  };

  /**
   * Show/Hide the nested editor according to the show parameter, if
   * undefined then toggle.
   *
   */
  $scope.showNestedEditor = function showNestedEditor(show) {
    if (!params.summaryView) {
      return;
    }
    if (embedded === null) {
      embedded = $scope.createNestedEditor();
    }
    var es = embedded.data('$scope');
    if (es !== null) {
      es.visible = (show === undefined ? !es.visible : show);
      embedded.toggle(es.visible);
    }
    return embedded;
  };

  $scope.showPopupEditor = function(record) {
    if (!record && this.isReadonly()) {
      return;
    }
    if (editor == null) {
      editor = ViewService.compile('<div ui-editor-popup></div>')($scope);
      editor.data('$target', $element);
    }

    var popup = editor.isolateScope();
    popup.show(record);
    popup._afterPopupShow = function() {
      if (record == null) {
        popup.$broadcast("on:new");
      }
    };
  };

  function _showEditor(record) {

    if (!$scope._isPopup && field.editWindow === "blank" && record && record.id > 0) {
      var checkVersion = "" + axelor.config["view.form.check-version"];
      var context = ($scope.selectedTab || {}).context || {};
      if (context.__check_version !== undefined) {
        checkVersion = "" + context.__check_version;
      }
      var tab = {
        action: _.uniqueId('$act'),
        title: field.title,
        model: field.target,
        recordId: record.id,
        views: [{
          type: 'form',
          name: field.formView
        }, {
          type: 'grid',
          name: field.gridView
        }]
      };
      if (checkVersion) {
        tab.context = { __check_version: checkVersion };
      }

      return $scope.$root.openTab(tab);
    }

    if ($scope.editorCanReload && record && record.id) {
      var parent = $scope.$parent;
      if (parent && parent.canSave()) {
        var opts = {
          callOnSave: field.callOnSave
        };
        return parent.onSave(opts).then(function(){
          $scope.showPopupEditor(record);
        });
      }
    }
    return $scope.showPopupEditor(record);
  }

  $scope.showEditor = function(record) {
    var perm = record ? "read" : "create";
    var id = (record||{}).id;

    if (perm === 'read' && (!id || id < 0)) {
      return _showEditor(record);
    }
    return $scope.isPermitted(perm, record, function(){
      _showEditor(record);
    });
  };

  $scope.parentReload = function() {
    var parent = $scope.$parent;
    if (parent) {
      parent.reload();
    }
  };

  $scope.showSelector = function() {
    if (this.isReadonly()) {
      return;
    }
    function doShow() {
      if (selector == null) {
        selector = $('<div ui-selector-popup></div>').attr('x-select-mode', $scope.selectMode || "multi");
        selector = ViewService.compile(selector)($scope);
        selector.data('$target', $element);
      }
      var popup = selector.isolateScope();
      popup._domain = $scope._domain; // make sure that popup uses my domain (#1233)
      popup.show();
    }

    var onSelect = this.$events.onSelect;
    if (onSelect) {
      onSelect().then(function(){
        doShow();
      });
    } else {
      doShow();
    }
  };

  $scope.$on("on:edit", function(record){
    var domain = ($scope.field||field).domain;
    var context = ($scope.field||field).context;
    if (domain !== undefined) $scope._domain = domain;
    if (context !== undefined) $scope._context = context;
  });

  $scope.setDomain = function(domain, context) {
    if (domain !== undefined) $scope._domain = domain;
    if (context !== undefined) $scope._context = context;
  };

  $scope.getDomain = function() {
    return {
      _domain: $scope._domain,
      _context: $scope._context
    };
  };

  var fetchDS = (function () {
    var fds = null;
    return function () {
      if (fds) return fds;
      var ds = $scope._dataSource;
      return fds = DataSource.create(ds._model, {
        domain: ds._domain,
        context: ds._context
      });
    };
  })();

  $scope.fetchData = function(value, success) {

    var records = $.makeArray(value),
      ids = [];

    _.each(records, function(item) {
      if (_.isNumber(item)) {
        return ids.push(item);
      }
      if (_.isNumber(item.id) && item.id > 0 &&
        _.isUndefined(item.version) &&
        _.isUndefined(item.$fetched)) {
        return ids.push(item.id);
      }
    });

    if (ids.length === 0) {
      return success(value);
    }

    var fields = $scope.selectFields();

    function doFetch(view) {
      var domain = "self.id in (:_field_ids)";
      var context = _.pick($scope.getContext(), ['id', '_model']);

      var sortBy = view.sortBy || view.orderBy;
      if (sortBy) {
        sortBy = sortBy.split(",");
      }
      if (view.canMove && fields.indexOf('sequence') === -1) {
        fields.push('sequence');
      }

      context._field = field.name;
      context._field_ids = ids;

      return fetchDS().search({
        fields: fields,
        sortBy: fetchDS()._sortBy || sortBy,
        archived: true,
        limit: -1,
        domain: domain,
        context: context
      }).success(function(records, page){
        // only edited records should have version property
        var items = _.map(records, function(item){
          item.$version = item.version;
          item.$fetched = false;
          delete item.version;
          return item;
        });
        success(items, page);
      });
    }

    if ($scope.isHidden()) {
      return doFetch($scope.view || {});
    }

    return $scope._viewPromise.then(function(view) {
      return doFetch(view || {});
    });
  };

  $scope.fetchSelection = function(request, response) {
    var fn = fetchSelection.bind(this);
    var onSelect = this.$events.onSelect;
    if (onSelect) {
      return onSelect(true).then(function() {
        return fn(request, response);
      });
    }
    return fn(request, response);
  };

  function fetchSelection(request, response) {

    /* jshint validthis: true */

    var field = this.field;
    var nameField = field.targetName || 'id',
      fields = field.targetSearch || [],
      filter = {},
      limit = field.limit || (axelor.device.small ? 6 : 10),
      sortBy = field.orderBy;

    fields = ["id", nameField].concat(fields);
    fields = _.chain(fields).compact().unique().value();

    _.each(fields, function(name){
      if (name !== "id" && request.term) {
        filter[name] = request.term;
      }
    });

    var domain = this._domain,
      context = this._context;

    if (domain !== undefined && this.getContext) {
      context = _.extend({}, context, this.getContext());
    }

    if (sortBy) {
      sortBy = sortBy.split(",");
    }

    var params = {
      filter: filter,
      fields: fields,
      sortBy: sortBy,
      limit: limit
    };

    if (domain !== undefined) {
      params.domain = domain;
      params.context = context;
    }

    fetchDS().search(params).success(function(records, page){
      var trKey = '$t:' + nameField;
      var items = _.map(records, function(record) {
        return {
          label: record[trKey] || record[nameField],
          value: record
        };
      });
      response(items, page);
    });
  }

  $scope.createOnTheFly = function (term, popup, onSaveCallback) {

    var field = $scope.field;
    var targetFields = null;
    var requiredFields = (field.create||"").split(/,\s*/);

    function createItem(fields, term, popup) {
      var ds = $scope._dataSource,
        data = { $forceDirty: true }, missing = false;

      _.each(fields, function(field) {
        if (field.name === "name") return data["name"] = term;
        if (field.name === "code") return data["code"] = term;
        if (field.nameColumn) return data[field.name] = term;
        if (requiredFields.indexOf(field.name) > -1) {
          return data[field.name] = term;
        }
        if (field.required) {
          missing = true;
        }
      });
      if (popup || missing || _.isEmpty(data)) {
        return $scope.showPopupEditor(data);
      }
      return ds.save(data).success(onSaveCallback);
    }


    if (targetFields) {
      return createItem(targetFields, term, popup);
    }

    return $scope.loadView("form").success(function(fields, view){
      targetFields = fields;
      return createItem(fields, term, popup);
    });
  };

  $scope.attachTagEditor = function attachTagEditor(scope, element, attrs) {

    var field = scope.field;
    var input = null;

    if (!field.target) {
      return;
    }

    function onTagEdit(e, item) {

      var elem = $(e.target);
      var field = scope.field;
      var value = item[field.targetName];

      function onKeyDown(e) {

        // enter key
        if (e.keyCode === 13) {
          item[field.targetName] = input.val();
          saveAndSelect(item);
          hideEditor();
        }

        // escape
        if (e.keyCode === 27) {
          hideEditor();
        }
      }

      function hideEditor(forceSave) {

        $(document).off('mousedown.tag-editor');
        $(input).off('keydown.tag-editor').hide();

        if (forceSave && value !== input.val()) {
          item[field.targetName] = input.val();
          saveAndSelect(item);
        }
      }

      if (input === null) {
        input = $('<input class="tag-editor" type="text">').appendTo(element);
      }

      input.val(value)
        .width(element.width() - 6)
        .show().focus()
        .position({
          my: 'left top',
          at: 'left+3 top+3',
          of: element
        });

      $(input).on('keydown.tag-editor', onKeyDown);
      $(document).on('mousedown.tag-editor', function (e) {
        if (!input.is(e.target)) {
          hideEditor(true);
        }
      });
    }

    function saveAndSelect(record) {
          var ds = scope._dataSource;
          var data = _.extend({}, record, {
            version: record.version || record.$version
          });
          ds.save(data).success(function (rec) {
            scope.select(rec);
          });
        }

    scope.onTagEdit = onTagEdit;
  };

  $scope.canSelect = function() {
    var canSelect = $scope.attr('canSelect');
    if (canSelect !== undefined) return canSelect;
    if ($scope.selectEnable !== undefined) return $scope.selectEnable;
    return true;
  };

  $scope.canNew = function() {
    return $scope.attr('canNew') !== false;
  };

  $scope.canEdit = function() {
    return !$scope.isReadonly();
  };

  $scope.canView = function() {
    return $scope.attr('canView') !== false;
  };

  $scope.canRemove = function() {
    return $scope.attr('canRemove') !== false;
  };

  $scope.select = function(value) {

  };

  $scope.onNew = function() {
    $scope.showEditor(null);
  };

  $scope.onEdit = function() {

  };

  $scope.onSelect = function() {
    $scope.showSelector();
  };

  $scope.onRemove = function() {

  };

  var hasPermission = $scope.hasPermission;
  $scope.hasPermission = function(perm) {
    if (hasPermission && !hasPermission.apply($scope, arguments)) {
      return false;
    }
    if (!field.perms) return true;
    var perms = field.perms;
    var permitted = perms[perm];
    if (!permitted) {
      return false;
    }
    return true;
  };
}

})();
