/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var App = require('app');
var validator = require('utils/validator');

var dbInfo = require('data/db_properties_info') || {};

var delay = (function(){
  var timer = 0;
  return function(callback, ms){
    clearTimeout (timer);
    timer = setTimeout(callback, ms);
  };
})();

/**
 * Abstract view for config fields.
 * Add popover support to control
 */
App.ServiceConfigPopoverSupport = Ember.Mixin.create({

  /**
   * Config object. It will instance of App.ServiceConfigProperty
   */
  serviceConfig: null,
  attributeBindings:['readOnly'],
  isPopoverEnabled: true,
  popoverPlacement: 'right',

  didInsertElement: function () {
    App.tooltip(this.$('[data-toggle=tooltip]'), {placement: 'top'});
    // if description for this serviceConfig not exist, then no need to show popover
    if (this.get('isPopoverEnabled') !== 'false' && this.get('serviceConfig.description')) {
      this.addPopover();
    }
  },

  /**
   * Element where popover is "appended"
   */
  elementForPopover: function () {
    return this.$();
  }.property(),

  /**
   * Add Popover for config-boxes
   *
   * @method addPopover
   */
  addPopover: function () {
    App.popover(this.get('elementForPopover'), {
      title: Em.I18n.t('installer.controls.serviceConfigPopover.title').format(
        this.get('serviceConfig.displayName'),
        (this.get('serviceConfig.displayName') === this.get('serviceConfig.name')) ? '' : this.get('serviceConfig.name')
      ),
      content: this.get('serviceConfig.description'),
      placement: this.get('popoverPlacement'),
      trigger: 'hover'
    });
  },

  willDestroyElement: function() {
    this.$().popover('destroy');
  },

  readOnly: Em.computed.not('serviceConfig.isEditable')
});

App.SupportsDependentConfigs = Ember.Mixin.create({

  /**
   * method send request to check if some of dependent configs was changes
   * and in case there was changes shows popup with info about changed configs
   *
   * @param {App.ServiceConfigProperty} config
   * @param [controller]
   * @returns {$.Deferred}
   */
  sendRequestRorDependentConfigs: function(config, controller) {
    if (!config || (!config.get('isValid') && config.get('isNotDefaultValue'))) return $.Deferred().resolve().promise();
    controller = controller || this.get('controller');
    if (controller && ['mainServiceInfoConfigsController','wizardStep7Controller'].contains(controller.get('name'))) {
      var name = config.get('name');
      var saveRecommended = (config.get('value') === config.get('recommendedValue'));
      var type = App.config.getConfigTagFromFileName(config.get('filename'));
      var p = App.configsCollection.getConfig(App.config.configId(name, type));
      controller.removeCurrentFromDependentList(config, saveRecommended);
       if ((p && Em.get(p, 'propertyDependedBy.length') > 0 || config.get('displayType') === 'user') && config.get('oldValue') !== config.get('value')) {
         var old = config.get('oldValue');
         config.set('oldValue', config.get('value'));
         return controller.loadConfigRecommendations([{
           "type": type,
           "name": name,
           "old_value": Em.isNone(old) ? config.get('initialValue') : old
         }]);
      }
    }

    return $.Deferred().resolve().promise();
  },

  /**
   * Restore values for dependent configs by parent config info.
   * NOTE: If dependent config inherited from multiply configs its
   * value will be restored only when all parent configs are being restored.
   *
   * @param {App.ServiceConfigProperty} parentConfig
   */
  restoreDependentConfigs: function(parentConfig) {
    var controller = this.get('controller');
    var dependentConfigs = controller.get('recommendations');
    try {
      controller.set('recommendations', dependentConfigs.reject(function(item) {
        if (item.parentConfigs.contains(parentConfig.get('name'))) {
          if (item.parentConfigs.length > 1) {
            item.parentConfigs.removeObject(parentConfig.get('name'));
          } else {
            // reset property value
            var property = App.config.findConfigProperty(controller.get('stepConfigs'), item.propertyName, App.config.getOriginalFileName(item.fileName));
            if (property) {
              property.set('value', property.get('savedValue') || property.get('initialValue'));
            }
            return true;
          }
        }
        return false;
      }));
    } catch(e) {
      console.warn('Dependent properties popup was not cleared');
    }
  }

});

/**
 * mixin is used to send request for recommendations
 * when config value is updated by user
 */
App.ValueObserver = Em.Mixin.create(App.SupportsDependentConfigs, {

  selected: false,

  focusOut: function () {
    this.set('selected', false);
  },

  focusIn: function () {
    this.set('selected', true);
  },

  onValueUpdate: function () {
    if (this.get('selected')) {
      var self = this, config = this.get('serviceConfig'),
        controller = this.get('controller');
      delay(function(){
        self.sendRequestRorDependentConfigs(config, controller);
      }, 500);
    }
  }.observes('serviceConfig.value')
});

/**
 * mixin set class that serve as unique element identifier,
 * id not used in order to avoid collision with ember ids
 */
App.ServiceConfigCalculateId = Ember.Mixin.create({
  idClass: Ember.computed(function () {
    var config = this.get('config') && this.get('config.widget') ? this.get('config') : this.get('serviceConfig') || {};
    var label = Em.get(config, 'name') ? Em.get(config, 'name').toLowerCase().replace(/\./g, '-') : '',
        fileName = Em.get(config, 'filename') ? Em.get(config, 'filename').toLowerCase().replace(/\./g, '-') : '',
        group = Em.get(config, 'group.name') || 'default',
        isOrigin = Em.getWithDefault(config, 'compareConfigs.length', 0) > 0 ? '-origin' : '';
    return 'service-config-' + label + '-' + fileName + '-' + group + isOrigin;
  }),
  classNameBindings: 'idClass'
});

/**
 * Default input control
 * @type {*}
 */
App.ServiceConfigTextField = Ember.TextField.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.ValueObserver, {

  valueBinding: 'serviceConfig.value',
  classNameBindings: 'textFieldClassName',
  placeholderBinding: 'serviceConfig.placeholder',

  //Set editDone true for last edited config text field parameter
  focusOut: function () {
    this._super();
    this.get('serviceConfig').set("editDone", true);
  },
  //Set editDone false for all current category config text field parameter
  focusIn: function () {
    this._super();
    if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
      if (this.get('parentView.categoryConfigsAll')) {
        this.get("parentView.categoryConfigsAll").setEach("editDone", false);
      }
    }
  },

  textFieldClassName: function () {
    if (this.get('serviceConfig.unit')) {
      return ['input-small'];
    } else if (this.get('serviceConfig.displayType') === 'principal') {
      return ['span12'];
    } else {
      return ['span9'];
    }
  }.property('serviceConfig.displayType', 'serviceConfig.unit')

});

/**
 * Customized input control with Units type specified
 * @type {Em.View}
 */
App.ServiceConfigTextFieldWithUnit = Ember.View.extend(App.ServiceConfigPopoverSupport, App.ValueObserver, {
  valueBinding: 'serviceConfig.value',
  classNames: ['input-append', 'with-unit'],
  placeholderBinding: 'serviceConfig.savedValue',

  templateName: require('templates/wizard/controls_service_config_textfield_with_unit')
});

/**
 * Password control
 * @type {*}
 */
App.ServiceConfigPasswordField = Ember.TextField.extend(App.ServiceConfigPopoverSupport, {

  serviceConfig: null,
  type: 'password',
  attributeBindings:['readOnly'],
  valueBinding: 'serviceConfig.value',
  classNames: [ 'span4' ],
  placeholder: Em.I18n.t('form.item.placeholders.typePassword'),

  template: Ember.Handlebars.compile('{{view view.retypePasswordView}}'),

  keyPress: function (event) {
    if (event.keyCode == 13) {
      return false;
    }
  },

  retypePasswordView: Ember.TextField.extend({
    placeholder: Em.I18n.t('form.passwordRetype'),
    attributeBindings:['readOnly'],
    type: 'password',
    classNames: [ 'span4', 'retyped-password' ],
    keyPress: function (event) {
      if (event.keyCode == 13) {
        return false;
      }
    },
    valueBinding: 'parentView.serviceConfig.retypedPassword',
    readOnly: Em.computed.not('parentView.serviceConfig.isEditable')
  }),

  readOnly: Em.computed.not('serviceConfig.isEditable')

});

/**
 * Textarea control
 * @type {*}
 */
App.ServiceConfigTextArea = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.ValueObserver, {

  valueBinding: 'serviceConfig.value',
  rows: 4,
  classNames: ['directories'],
  classNameBindings: ['widthClass'],
  widthClass: 'span9'
});


/**
 * Special config type for Capacity Scheduler
 */
App.CapacitySceduler = App.ServiceConfigTextArea.extend({

  rows: 16,

  /**
   * specific property handling for cs
   *
   * @param {App.ServiceConfigProperty} config
   * @param [controller]
   * @returns {$.Deferred}
   * @override
   */
  sendRequestRorDependentConfigs: function(config, controller) {
    if (!config.get('isValid') && config.get('isNotDefaultValue')) return $.Deferred().resolve().promise();
    controller = controller || this.get('controller');
    if (controller && ['mainServiceInfoConfigsController','wizardStep7Controller'].contains(controller.get('name'))) {
      return controller.loadConfigRecommendations(config.get('value').split('\n').map(function (_property) {
        return {
          "type": 'capacity-scheduler',
          "name": _property.split('=')[0]
        }
      }));
    }

    return $.Deferred().resolve().promise();
  }

});

/**
 * Textarea control for content type
 * @type {*}
 */
App.ServiceConfigTextAreaContent = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.ValueObserver, {

  valueBinding: 'serviceConfig.value',
  rows: 20,
  classNames: ['span10']
});

/**
 * Textarea control with bigger height
 * @type {*}
 */
App.ServiceConfigBigTextArea = App.ServiceConfigTextArea.extend(App.ServiceConfigCalculateId, {
  rows: 10
});

/**
 * Checkbox control
 * @type {*}
 */
App.ServiceConfigCheckbox = Ember.Checkbox.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {

  allowedPairs: {
    'trueFalse': ["true", "false"],
    'YesNo': ["Yes", "No"],
    'YESNO': ["YES", "NO"],
    'yesNo': ["yes", "no"]
  },

  trueValue: true,
  falseValue: false,

  checked: false,

  elementForPopover: function () {
    return this.$().parent('.control-group').find('.bootstrap-checkbox');
  }.property(),

  /**
   * set appropriate config values pair
   * to define which value is positive (checked) property
   * and what value is negative (unchecked) property
   */
  didInsertElement: function() {
    var self = this;
    if (this.get('serviceConfig').isDestroyed) return;
    this._super();
    this.addObserver('serviceConfig.value', this, 'toggleChecker');
    Object.keys(this.get('allowedPairs')).forEach(function(key) {
      if (this.get('allowedPairs')[key].contains(this.get('serviceConfig.value'))) {
        this.set('trueValue', this.get('allowedPairs')[key][0]);
        this.set('falseValue', this.get('allowedPairs')[key][1]);
      }
    }, this);
    this.set('checked', this.get('serviceConfig.value') === this.get('trueValue'));
    this.propertyDidChange('checked');
    Em.run.next(function () {
      if (self.$()) {
        self.$().checkbox({
          defaultState: self.get('serviceConfig.value'),
          buttonStyle: 'btn-link btn-large',
          checkedClass: 'icon-check',
          uncheckedClass: 'icon-check-empty'
        });
        self.propertyDidChange('elementForPopover');
        self.addPopover();
      }
    });
  },

  willDestroyElement: function() {
    this.removeObserver('serviceConfig.value', this, 'checkedBinding');
  },

  /***
   * defines if checkbox value appropriate to the config value
   * @returns {boolean}
   */
  isNotAppropriateValue: function() {
    return this.get('serviceConfig.value') !== this.get(this.get('checked') + 'Value');
  },

  /**
   * change service config value if click on checkbox
   */
  toggleValue: function() {
    if (this.isNotAppropriateValue()){
      this.set('serviceConfig.value', this.get(this.get('checked') + 'Value'));
      this.get('serviceConfig').set("editDone", true);
      this.sendRequestRorDependentConfigs(this.get('serviceConfig'));
    }
  }.observes('checked'),

  /**
   * change checkbox value if click on undo
   */
  toggleChecker: function() {
    if (this.isNotAppropriateValue()) {
      this.set('checked', !this.get('checked'));
      // change bootstrap-checkbox state
      this.$().change();
    }
  },

  disabled: Em.computed.not('serviceConfig.isEditable'),

  //Set editDone false for all current category config text field parameter
  focusIn: function (event) {
    if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
      this.get("parentView.categoryConfigsAll").setEach("editDone", false);
    }
  }
});

/**
 * Checkbox control which can hide or show dependent  properties
 * @type {*|void}
 */
App.ServiceConfigCheckboxWithDependencies = App.ServiceConfigCheckbox.extend({

  toggleDependentConfigs: function() {
    if (this.get('serviceConfig.dependentConfigPattern')) {
      if (this.get('serviceConfig.dependentConfigPattern') === "CATEGORY") {
        this.disableEnableCategoryConfigs();
      } else {
        this.showHideDependentConfigs();
      }
    }
  }.observes('checked'),

  disableEnableCategoryConfigs: function () {
    this.get('categoryConfigsAll').setEach('isEditable', this.get('checked'));
    this.set('serviceConfig.isEditable', true);
  },

  showHideDependentConfigs: function () {
    this.get('categoryConfigsAll').forEach(function (c) {
      if (c.get('name').match(this.get('serviceConfig.dependentConfigPattern')) && c.get('name') != this.get('serviceConfig.name'))
        c.set('isVisible', this.get('checked'))
    }, this);
  }
});

App.ServiceConfigRadioButtons = Ember.View.extend(App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  templateName: require('templates/wizard/controls_service_config_radio_buttons'),

  didInsertElement: function () {
    // on page render, automatically populate JDBC URLs only for default database settings
    // so as to not lose the user's customizations on these fields
    if (this.get('hostNameProperty')) {
      this.get('hostNameProperty').set('isEditable', !this.get('isNewDb'));
    }
    if (['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name')) && !App.StackService.find(this.get('serviceConfig.serviceName')).get('isInstalled')) {
      if (this.get('isNewDb') || this.get('dontUseHandleDbConnection').contains(this.get('serviceConfig.name'))) {
        this.onOptionsChange();
      } else {
        if ((App.get('isHadoopWindowsStack') && this.get('inMSSQLWithIA')) || this.get('serviceConfig.name') === 'DB_FLAVOR') {
          this.onOptionsChange();
        }
        this.handleDBConnectionProperty();
      }
    }
  },

  /**
   * Radio buttons that are not DB options and should not trigger any observer or change any other property's value
   * Ranger service -> "Authentication method" property is an example for non DB related radio button
   */
  nonDBRadioButtons: function() {
    return this.get('dontUseHandleDbConnection').without('DB_FLAVOR');
  }.property('dontUseHandleDbConnection'),

  /**
   * properties with these names don'use handleDBConnectionProperty method
   */
  dontUseHandleDbConnection: function () {
    // functionality added in Ranger 0.5
    // remove DB_FLAVOR so it can handle DB Connection checks
    var rangerService = App.StackService.find().findProperty('serviceName', 'RANGER');
    var rangerVersion = rangerService ? rangerService.get('serviceVersion') : '';
    if (rangerVersion && rangerVersion.split('.')[0] < 1 && rangerVersion.split('.')[1] < 5) {
      return ['DB_FLAVOR', 'authentication_method'];
    }
    return ['ranger.authentication.method'];
  }.property('App.currentStackName'),

  serviceConfig: null,
  categoryConfigsAll: null,

  /**
   * defines if new db is selected;
   * @type {boolean}
   */
  isNewDb: function() {
    return /New /g.test(this.get('serviceConfig.value'));
  }.property('serviceConfig.serviceName', 'serviceConfig.value'),

  /**
   * defines if 'Existing MSSQL Server database with integrated authentication' is selected
   * in this case some properties can have different behaviour
   * @type {boolean}
   */
  inMSSQLWithIA: Em.computed.equal('serviceConfig.value', 'Existing MSSQL Server database with integrated authentication'),

  /**
   * Radio button has very uncomfortable values for managing it's state
   * so it's better to use code values that easier to manipulate. Ex:
   * "Existing MySQL Database" transforms to "MYSQL"
   * @type {string}
   */
  getDbTypeFromRadioValue: function() {
    var currentValue = this.get('serviceConfig.value');
    /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
    var databases = /MySQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
    if (this.get('inMSSQLWithIA')) {
      return 'MSSQL2';
    } else {
      var matches = currentValue.match(databases);
      if (matches) {
        return currentValue.match(databases)[0].toUpperCase();
      } else {
        return "MYSQL";
      }
    }
  }.property('serviceConfig.serviceName', 'serviceConfig.value'),

  onOptionsChange: function () {
    if (!this.get('nonDBRadioButtons').contains(this.get('serviceConfig.name'))) {
      /** if new db is selected host name must be same as master of selected service (and can't be changed)**/
      this.handleSpecialUserPassProperties();
    }
  }.observes('databaseProperty.value', 'hostNameProperty.value', 'serviceConfig.value'),

  name: function () {
    var name = this.get('serviceConfig.radioName');
    if (!this.get('serviceConfig.isOriginalSCP')) {
      if (this.get('serviceConfig.isComparison')) {
        var version = this.get('serviceConfig.compareConfigs') ? this.get('controller.selectedVersion') : this.get('version');
        name += '-v' + version;
      } else {
        var group = this.get('serviceConfig.group.name');
        name += '-' + group;
      }
    }
    return name;
  }.property('serviceConfig.radioName'),

  /**
   * Just property object for database name
   * @type {App.ServiceConfigProperty}
   */
  databaseProperty: function () {
    return this.getPropertyByType('db_name');
  }.property('serviceConfig.serviceName'),

  /**
   * Just property object for host name
   * @type {App.ServiceConfigProperty}
   */
  hostNameProperty: function () {
    var host = this.getPropertyByType('host_name');
    if (host && !host.get('value')) {
      if (host.get('savedValue')) {
        host.set('value', host.get('savedValue'));
      } else if (host.get('recommendedValue')) {
        host.set('value', host.get('recommendedValue'));
      }
    }
    return host;
  }.property('serviceConfig.serviceName', 'serviceConfig.value'),

  /**
   * Just property object for database name
   * @type {App.ServiceConfigProperty}
   */
  userProperty: function () {
    return this.getPropertyByType('user_name');
  }.property('serviceConfig.serviceName'),

  /**
   * Just property object for database name
   * @type {App.ServiceConfigProperty}
   */
  passwordProperty: function () {
    return this.getPropertyByType('password');
  }.property('serviceConfig.serviceName'),

  /**
   *
   * @param propertyType
   * @returns {*}
   */
  getDefaultPropertyValue: function(propertyType) {
    var dbProperties = dbInfo.dpPropertiesMap[this.get('getDbTypeFromRadioValue')],
      serviceName = this.get('serviceConfig.serviceName');
    return dbProperties[serviceName] && dbProperties[serviceName][propertyType]
      ? dbProperties[serviceName][propertyType] : dbProperties[propertyType];
  },

  /**
   *
   * @param propertyType
   * @returns {*|Object}
   */
  getPropertyByType: function(propertyType) {
    if (dbInfo.dpPropertiesByServiceMap[this.get('serviceConfig.serviceName')]) {
      //@TODO: dbInfo.dpPropertiesByServiceMap has corresponding property name but does not have filenames with it. this can cause issue when there are multiple db properties with same name belonging to different files
      /** check if selected service has db properties**/
      return this.get('controller.selectedService.configs').findProperty('name', dbInfo.dpPropertiesByServiceMap[this.get('serviceConfig.serviceName')][propertyType]);
    }
    return null;
  },

  /**
   * This method hides properties <code>user_name<code> and <code>password<code> in case selected db is
   * "Existing MSSQL Server database with integrated authentication" or similar
   * @method handleSpecialUserPassProperties
   */
  handleSpecialUserPassProperties: function() {
    ['user_name', 'password'].forEach(function(pType) {
      var property = this.getPropertyByType(pType);
      if (property) {
        property.setProperties({
          'isVisible': !this.get('inMSSQLWithIA'),
          'isRequired': !this.get('inMSSQLWithIA')
        });
      }
    }, this);
  },

  /**
   * `Observer` that add <code>additionalView</code> to <code>App.ServiceConfigProperty</code>
   * that responsible for (if existing db selected)
   * 1. checking database connection
   * 2. showing jdbc driver setup warning msg.
   *
   * @method handleDBConnectionProperty
   **/
  handleDBConnectionProperty: function() {
    if (this.get('dontUseHandleDbConnection').contains(this.get('serviceConfig.name'))) {
      return;
    }
    var handledProperties = ['oozie_database', 'hive_database', 'DB_FLAVOR'];
    var currentValue = this.get('serviceConfig.value');
    /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
    var databases = /MySQL|PostgreSQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
    var currentDB = currentValue.match(databases)[0];
    /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
    var databasesTypes = /MySQL|Postgres|Oracle|Derby|MSSQL|SQLA|Anywhere/gi;
    var currentDBType = currentValue.match(databasesTypes)[0];
    var checkDatabase = /existing/gi.test(currentValue);
    // db connection check button show up if existed db selected
    var propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('displayName', 'Database URL');
    // warning msg under database type radio buttons, to warn the user to setup jdbc driver if existed db selected
    var propertyHive = this.get('categoryConfigsAll').findProperty('displayName', 'Hive Database');
    var propertyOozie = this.get('categoryConfigsAll').findProperty('displayName', 'Oozie Database');
    var propertyAppendTo2 = propertyHive ? propertyHive : propertyOozie;
    // RANGER specific
    if (this.get('serviceConfig.serviceName') === 'RANGER') {
      propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('name', 'ranger.jpa.jdbc.url');
      propertyAppendTo2 = this.get('categoryConfigsAll').findProperty('name', 'DB_FLAVOR');
      // check for all db types when installing Ranger - not only for existing ones
      checkDatabase = true;
    }
    if (propertyAppendTo1) {
      propertyAppendTo1.set('additionalView', null);
    }
    if (propertyAppendTo2) {
      propertyAppendTo2.set('additionalView', null);
    }
    var shouldAdditionalViewsBeSet = currentDB && checkDatabase && handledProperties.contains(this.get('serviceConfig.name')),
      driver = this.getDefaultPropertyValue('sql_jar_connector') ? this.getDefaultPropertyValue('sql_jar_connector').split("/").pop() : 'driver.jar',
      dbType = this.getDefaultPropertyValue('db_type'),
      additionalView1 = shouldAdditionalViewsBeSet ? App.CheckDBConnectionView.extend({databaseName: dbType}) : null,
      additionalView2 = shouldAdditionalViewsBeSet ? Ember.View.extend({
        template: Ember.Handlebars.compile('<div class="alert">{{{view.message}}}</div>'),
        message: function() {
          return Em.I18n.t('services.service.config.database.msg.jdbcSetup').format(dbType, driver);
        }.property()
      }) : null;
    if (propertyAppendTo1) {
      Em.run.next(function () {
        propertyAppendTo1.set('additionalView', additionalView1);
      });
    }
    if (propertyAppendTo2) {
      Em.run.next(function () {
        propertyAppendTo2.set('additionalView', additionalView2);
      });
    }
  }.observes('serviceConfig.value'),

  options: function () {
    return this.get('serviceConfig.options').map(function (option) {
      var dbTypePattern = /mysql|postgres|oracle|derby|mssql|sql\s?a/i,
        className = '',
        displayName = Em.get(option, 'displayName'),
        dbTypeMatch = displayName.match(dbTypePattern);
      if (dbTypeMatch) {
        var dbSourcePattern = /new/i,
          newDbMatch = displayName.match(dbSourcePattern);
        if (newDbMatch) {
          className += 'new-';
        }
        className += dbTypeMatch[0].replace(' ', '').toLowerCase();
      }
      return className ? Em.Object.create(option, {
        className: className
      }) : option;
    });
  }.property('serviceConfig.options')
});

App.ServiceConfigRadioButton = Ember.Checkbox.extend(App.SupportsDependentConfigs, {
  tagName: 'input',
  attributeBindings: ['type', 'name', 'value', 'checked', 'disabled'],
  checked: false,
  clicked: false,
  type: 'radio',
  name: null,
  value: null,

  didInsertElement: function () {
    this.set('checked', this.get('parentView.serviceConfig.value') === this.get('value'));
  },

  click: function () {
    this.set('clicked', true);
    this.set('checked', true);
    this.onChecked();
  },

  onChecked: function () {
    // Wrapping the call with Ember.run.next prevents a problem where setting isVisible on component
    // causes JS error due to re-rendering.  For example, this occurs when switching the Config Group
    // in Service Config page
    if (this.get('clicked')) {
      Em.run.next(this, function() {
        this.set('parentView.serviceConfig.value', this.get('value'));
        this.sendRequestRorDependentConfigs(this.get('parentView.serviceConfig'));
        this.set('clicked', false);
        this.updateForeignKeys();
      });
    }
  }.observes('checked'),

  updateCheck: function() {
    if (!this.get('clicked')) {
      this.set('checked', this.get('parentView.serviceConfig.value') === this.get('value'));
      this.updateForeignKeys();
    }
  }.observes('parentView.serviceConfig.value'),

  updateForeignKeys: function() {
    var components = this.get('parentView.serviceConfig.options');
    if (components && components.someProperty('foreignKeys')) {
      this.get('controller.stepConfigs').findProperty('serviceName', this.get('parentView.serviceConfig.serviceName')).propertyDidChange('errorCount');
    }
  },


  disabled: function () {
    return !this.get('parentView.serviceConfig.isEditable') ||
      !['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name')) && /^New\s\w+\sDatabase$/.test(this.get('value'));
  }.property('parentView.serviceConfig.isEditable')
});

App.ServiceConfigComboBox = Ember.Select.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, App.SupportsDependentConfigs, {
  contentBinding: 'serviceConfig.options',
  selectionBinding: 'serviceConfig.value',
  placeholderBinding: 'serviceConfig.savedValue',
  classNames: [ 'span3' ]
});


/**
 * Base component for host config with popover support
 */
App.ServiceConfigHostPopoverSupport = Ember.Mixin.create({

  /**
   * Config object. It will instance of App.ServiceConfigProperty
   */
  serviceConfig: null,

  didInsertElement: function () {
    App.popover(this.$(), {
      title: this.get('serviceConfig.displayName'),
      content: this.get('serviceConfig.description'),
      placement: 'right',
      trigger: 'hover'
    });
  }
});

/**
 * Master host component.
 * Show hostname without ability to edit it
 * @type {*}
 */
App.ServiceConfigMasterHostView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {

  classNames: ['master-host', 'span6'],
  valueBinding: 'serviceConfig.value',

  template: Ember.Handlebars.compile('{{value}}')

});

/**
 * text field property view that enables possibility
 * for check connection
 * @type {*}
 */
App.checkConnectionView = App.ServiceConfigTextField.extend({
  didInsertElement: function() {
    this._super();
    if (this.get('controller.isHostsConfigsPage')) {
      return;
    }
    var kdc = this.get('categoryConfigsAll').findProperty('name', 'kdc_type');
    var propertyAppendTo = this.get('categoryConfigsAll').findProperty('name', 'domains');
    if (propertyAppendTo) {
      try {
        propertyAppendTo.set('additionalView', App.CheckDBConnectionView.extend({databaseName: kdc && kdc.get('value')}));
      } catch (e) {
        console.error('error while adding "Test connection button"');
      }
    }
  }
});

/**
 * Show value as plain label in italics
 * @type {*}
 */
App.ServiceConfigLabelView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {

  classNames: ['master-host', 'span6'],
  valueBinding: 'serviceConfig.value',

  template: Ember.Handlebars.compile('<i>{{view.value}}</i>')
});

/**
 * Base component to display Multiple hosts
 * @type {*}
 */
App.ServiceConfigMultipleHostsDisplay = Ember.Mixin.create(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {

  hasNoHosts: function () {
    if (!this.get('value')) {
      return true;
    }
    return this.get('value').length === 0;
  }.property('value'),

  formatValue: function() {
    if (Em.isArray(this.get('value')) && this.get('value').length === 1) {
      return this.get('value.firstObject');
    } else {
      return this.get('value');
    }
  }.property('value'),

  hasOneHost: Em.computed.equal('value.length', 1),

  hasMultipleHosts: Em.computed.gt('value.length', 1),

  otherLength: function () {
    var len = this.get('value').length;
    if (len > 2) {
      return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.others').format(len - 1);
    } else {
      return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.other');
    }
  }.property('value')

});

/**
 * Multiple Slave Hosts component
 * @type {*}
 */
App.ServiceConfigComponentHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, App.ServiceConfigCalculateId, {

  viewName: 'serviceConfigSlaveHostsView',

  classNames: ['component-hosts', 'span6'],

  valueBinding: 'serviceConfig.value',

  templateName: require('templates/wizard/component_hosts'),

  /**
   * Onclick handler for link
   */
  showHosts: function () {
    var serviceConfig = this.get('serviceConfig');
    App.ModalPopup.show({
      header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
      bodyClass: Ember.View.extend({
        serviceConfig: serviceConfig,
        templateName: require('templates/wizard/component_hosts_popup')
      }),
      secondary: null
    });
  }

});


/**
 * DropDown component for <code>select hosts for groups</code> popup
 * @type {*}
 */
App.SlaveComponentDropDownGroupView = Ember.View.extend(App.ServiceConfigCalculateId, {

  viewName: "slaveComponentDropDownGroupView",

  /**
   * On change handler for <code>select hosts for groups</code> popup
   * @param event
   */
  changeGroup: function (event) {
    var host = this.get('content');
    var groupName = $('#' + this.get('elementId') + ' select').val();
    this.get('controller').changeHostGroup(host, groupName);
  },

  optionTag: Ember.View.extend({

    /**
     * Whether current value(OptionTag value) equals to host value(assigned to SlaveComponentDropDownGroupView.content)
     */
    selected: function () {
      return this.get('parentView.content.group') === this.get('content');
    }.property('content')
  })
});

/**
 * View for testing connection to database.
 **/
App.CheckDBConnectionView = Ember.View.extend({
  templateName: require('templates/common/form/check_db_connection'),
  /** @property {string} btnCaption - text for button **/
  btnCaption: function() {
    return this.get('parentView.service.serviceName') === 'KERBEROS'
      ? Em.I18n.t('services.service.config.kdc.btn.idle')
      : Em.I18n.t('services.service.config.database.btn.idle')
  }.property('parentView.service.serviceName'),
  /** @property {string} responseCaption - text for status link **/
  responseCaption: null,
  /** @property {boolean} isConnecting - is request to server activated **/
  isConnecting: false,
  /** @property {boolean} isValidationPassed - check validation for required fields **/
  isValidationPassed: null,
  /** @property {string} databaseName- name of current database **/
  databaseName: null,
  /** @property {boolean} isRequestResolved - check for finished request to server **/
  isRequestResolved: false,
  /** @property {boolean} isConnectionSuccess - check for successful connection to database **/
  isConnectionSuccess: null,
  /** @property {string} responseFromServer - message from server response **/
  responseFromServer: null,
  /** @property {Object} ambariRequiredProperties - properties that need for custom action request **/
  ambariRequiredProperties: null,
  /** @property {Number} currentRequestId - current custom action request id **/
  currentRequestId: null,
  /** @property {Number} currentTaskId - current custom action task id **/
  currentTaskId: null,
  /** @property {jQuery.Deferred} request - current $.ajax request **/
  request: null,
  /** @property {Number} pollInterval - timeout interval for ajax polling **/
  pollInterval: 3000,
  /** @property {Object} logsPopup - popup with DB connection check info **/
  logsPopup: null,
  /** @property {string} hostNameProperty - host name property based on service and database names **/
  hostNameProperty: function() {
    if (!/wizard/i.test(this.get('controller.name')) && this.get('parentView.service.serviceName') === 'HIVE') {
      return this.get('parentView.service.serviceName').toLowerCase() + '_hostname';
    } else if (this.get('parentView.service.serviceName') === 'KERBEROS') {
      return 'kdc_hosts';
    } else if (this.get('parentView.service.serviceName') === 'RANGER') {
      return '{0}_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
    }
    return '{0}_existing_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
  }.property('databaseName'),
  /** @property {boolean} isBtnDisabled - disable button on failed validation or active request **/
  isBtnDisabled: Em.computed.or('!isValidationPassed', 'isConnecting'),
  /** @property {object} requiredProperties - properties that necessary for database connection **/
  requiredProperties: function() {
    var propertiesMap = {
      OOZIE: ['oozie.db.schema.name', 'oozie.service.JPAService.jdbc.username', 'oozie.service.JPAService.jdbc.password', 'oozie.service.JPAService.jdbc.driver', 'oozie.service.JPAService.jdbc.url'],
      HIVE: ['ambari.hive.db.schema.name', 'javax.jdo.option.ConnectionUserName', 'javax.jdo.option.ConnectionPassword', 'javax.jdo.option.ConnectionDriverName', 'javax.jdo.option.ConnectionURL'],
      KERBEROS: ['kdc_hosts'],
      RANGER: App.get('isHadoop23Stack') ? ['db_user', 'db_password', 'db_name', 'ranger.jpa.jdbc.url', 'ranger.jpa.jdbc.driver'] :
          ['db_user', 'db_password', 'db_name', 'ranger_jdbc_connection_url', 'ranger_jdbc_driver']
    };
    return propertiesMap[this.get('parentView.service.serviceName')];
  }.property('App.isHadoop23Stack'),
  /** @property {Object} propertiesPattern - check pattern according to type of connection properties **/
  propertiesPattern: function() {
    var patterns = {
      db_connection_url: /jdbc\.url|connection_url|connectionurl|kdc_hosts/ig
    };
    if (this.get('parentView.service.serviceName') != "KERBEROS") {
      patterns.user_name = /(username|dblogin|db_user)$/ig;
      patterns.user_passwd = /(dbpassword|password|db_password)$/ig;
    }
    return patterns;
  }.property('parentView.service.serviceName'),
  /** @property {String} masterHostName - host name location of Master Component related to Service **/
  masterHostName: function() {
    var serviceMasterMap = {
      'OOZIE': 'oozie_server_hosts',
      'HDFS': 'hadoop_host',
      'HIVE': 'hive_metastore_hosts',
      'KERBEROS': 'kdc_hosts',
      'RANGER': 'ranger_server_hosts'
    };
    return this.get('parentView.categoryConfigsAll').findProperty('name', serviceMasterMap[this.get('parentView.service.serviceName')]).get('value');
  }.property('parentView.service.serviceName', 'parentView.categoryConfigsAll.@each.value'),
  /** @property {Object} connectionProperties - service specific config values mapped for custom action request **/
  connectionProperties: function() {
    var propObj = {};
    for (var key in this.get('propertiesPattern')) {
      propObj[key] = this.getConnectionProperty(this.get('propertiesPattern')[key]);
    }

    if (this.get('parentView.service.serviceName') === 'RANGER') {
      var dbFlavor = this.get('parentView.categoryConfigsAll').findProperty('name','DB_FLAVOR').get('value'),
        /** TODO: Remove SQLA from the list of databases once Ranger DB_FLAVOR=SQLA is replaced with SQL Anywhere */
        databasesTypes = /MYSQL|POSTGRES|ORACLE|MSSQL|SQLA|Anywhere/gi,
        dbType = dbFlavor.match(databasesTypes)?dbFlavor.match(databasesTypes)[0].toLowerCase():'';

      if (dbType==='oracle') {
        // fixes oracle SYSDBA issue
        propObj['user_name'] = "\'%@ as sysdba\'".fmt(propObj['user_name']);
      }
    }
    return propObj;
  }.property('parentView.categoryConfigsAll.@each.value'),
  /**
   * Properties that stores in local storage used for handling
   * last success connection.
   *
   * @property {Object} preparedDBProperties
   **/
  preparedDBProperties: function() {
    var propObj = {};
    for (var key in this.get('propertiesPattern')) {
      var propName = this.getConnectionProperty(this.get('propertiesPattern')[key], true);
      propObj[propName] = this.get('parentView.categoryConfigsAll').findProperty('name', propName).get('value');
    }
    return propObj;
  }.property(),
  /** Check validation and load ambari properties **/
  didInsertElement: function() {
    var kdc = this.get('parentView.categoryConfigsAll').findProperty('name', 'kdc_type');
    if (kdc) {
      var name = kdc.get('value');
      if (name == 'Existing MIT KDC') {
        name = 'KDC';
      } else if (name == 'Existing IPA') {
        name = 'IPA';
      } else {
        name = 'AD';
      }
      App.popover(this.$(), {
        title: Em.I18n.t('services.service.config.database.btn.idle'),
        content: Em.I18n.t('installer.controls.checkConnection.popover').format(name),
        placement: 'right',
        trigger: 'hover'
      });
    }
    this.handlePropertiesValidation();
    this.getAmbariProperties();
  },
  /** On view destroy **/
  willDestroyElement: function() {
    this.set('isConnecting', false);
    this._super();
  },
  /**
   * Observer that take care about enabling/disabling button based on required properties validation.
   *
   * @method handlePropertiesValidation
   **/
  handlePropertiesValidation: function() {
    this.restore();
    var isValid = true;
    var properties = [].concat(this.get('requiredProperties'));
    properties.push(this.get('hostNameProperty'));
    properties.forEach(function(propertyName) {
      var property = this.get('parentView.categoryConfigsAll').findProperty('name', propertyName);
      if(property && !property.get('isValid')) isValid = false;
    }, this);
    this.set('isValidationPassed', isValid);
  }.observes('parentView.categoryConfigsAll.@each.isValid', 'parentView.categoryConfigsAll.@each.value', 'databaseName'),

  getConnectionProperty: function (regexp, isGetName) {
    var _this = this;
    var propertyName = _this.get('requiredProperties').filter(function (item) {
      return regexp.test(item);
    })[0];
    return (isGetName) ? propertyName : _this.get('parentView.categoryConfigsAll').findProperty('name', propertyName).get('value');
  },
  /**
   * Set up ambari properties required for custom action request
   *
   * @method getAmbariProperties
   **/
  getAmbariProperties: function() {
    var clusterController = App.router.get('clusterController');
    var _this = this;
    if (!App.isEmptyObject(App.db.get('tmp', 'ambariProperties')) && !this.get('ambariProperties')) {
      this.set('ambariProperties', App.db.get('tmp', 'ambariProperties'));
      return;
    }
    if (App.isEmptyObject(clusterController.get('ambariProperties'))) {
      clusterController.loadAmbariProperties().done(function(data) {
        _this.formatAmbariProperties(data.RootServiceComponents.properties);
      });
    } else {
      this.formatAmbariProperties(clusterController.get('ambariProperties'));
    }
  },

  formatAmbariProperties: function(properties) {
    var defaults = {
      threshold: "60",
      ambari_server_host: location.hostname,
      check_execute_list : "db_connection_check"
    };
    var properties = App.permit(properties, ['jdk.name','jdk_location','java.home']);
    var renameKey = function(oldKey, newKey) {
      if (properties[oldKey]) {
        defaults[newKey] = properties[oldKey];
        delete properties[oldKey];
      }
    };
    renameKey('java.home', 'java_home');
    renameKey('jdk.name', 'jdk_name');
    $.extend(properties, defaults);
    App.db.set('tmp', 'ambariProperties', properties);
    this.set('ambariProperties', properties);
  },
  /**
   * `Action` method for starting connect to current database.
   *
   * @method connectToDatabase
   **/
  connectToDatabase: function() {
    if (this.get('isBtnDisabled')) return;
    this.set('isRequestResolved', false);
    App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', {});
    this.setConnectingStatus(true);
    if (App.get('testMode')) {
      this.startPolling();
    } else {
      this.runCheckConnection();
    }
  },

  /**
   * runs check connections methods depending on service
   * @return {void}
   * @method runCheckConnection
   */
  runCheckConnection: function() {
    if (this.get('parentView.service.serviceName') === 'KERBEROS') {
      this.runKDCCheck();
    } else {
      this.createCustomAction();
    }
  },

  /**
   * send ajax request to perforn kdc host check
   * @return {App.ajax}
   * @method runKDCCheck
   */
  runKDCCheck: function() {
    return App.ajax.send({
      name: 'admin.kerberos_security.test_connection',
      sender: this,
      data: {
        kdcHostname: this.get('masterHostName')
      },
      success: 'onRunKDCCheckSuccess',
      error: 'onCreateActionError'
    });
  },

  /**
   *
   * @param data
   */
  onRunKDCCheckSuccess: function(data) {
    var statusCode = {
      success: 'REACHABLE',
      failed: 'UNREACHABLE'
    };
    if (data == statusCode['success']) {
      this.setResponseStatus('success');
    } else {
      this.setResponseStatus('failed');
    }
    this.set('responseFromServer', data);
  },

  /**
   * Run custom action for database connection.
   *
   * @method createCustomAction
   **/
  createCustomAction: function() {
    var isServiceInstalled = App.Service.find(this.get('parentView.service.serviceName')).get('isLoaded');
    var params = $.extend(true, {}, { db_name: this.get('databaseName').toLowerCase() }, this.get('connectionProperties'), this.get('ambariProperties'));
    App.ajax.send({
      name: (isServiceInstalled) ? 'cluster.custom_action.create' : 'custom_action.create',
      sender: this,
      data: {
        requestInfo: {
          parameters: params
        },
        filteredHosts: [this.get('masterHostName')]
      },
      success: 'onCreateActionSuccess',
      error: 'onCreateActionError'
    });
  },
  /**
   * Run updater if task is created successfully.
   *
   * @method onConnectActionS
   **/
  onCreateActionSuccess: function(data) {
    this.set('currentRequestId', data.Requests.id);
    App.ajax.send({
      name: 'custom_action.request',
      sender: this,
      data: {
        requestId: this.get('currentRequestId')
      },
      success: 'setCurrentTaskId'
    });
  },

  setCurrentTaskId: function(data) {
    this.set('currentTaskId', data.items[0].Tasks.id);
    this.startPolling();
  },

  startPolling: function() {
    if (this.get('isConnecting'))
      this.getTaskInfo();
  },

  getTaskInfo: function() {
    var request = App.ajax.send({
      name: 'custom_action.request',
      sender: this,
      data: {
        requestId: this.get('currentRequestId'),
        taskId: this.get('currentTaskId')
      },
      success: 'getTaskInfoSuccess'
    });
    this.set('request', request);
  },

  getTaskInfoSuccess: function(data) {
    var task = data.Tasks;
    this.set('responseFromServer', {
      stderr: task.stderr,
      stdout: task.stdout
    });
    if (task.status === 'COMPLETED') {
      var structuredOut = task.structured_out.db_connection_check;
      if (structuredOut.exit_code != 0) {
        this.set('responseFromServer', {
          stderr: task.stderr,
          stdout: task.stdout,
          structuredOut: structuredOut.message
        });
        this.setResponseStatus('failed');
      } else {
        App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', this.get('preparedDBProperties'));
        this.setResponseStatus('success');
      }
    }
    if (task.status === 'FAILED') {
      this.setResponseStatus('failed');
    }
    if (/PENDING|QUEUED|IN_PROGRESS/.test(task.status)) {
      Em.run.later(this, function() {
        this.startPolling();
      }, this.get('pollInterval'));
    }
  },

  onCreateActionError: function(jqXhr, status, errorMessage) {
    this.setResponseStatus('failed');
    this.set('responseFromServer', errorMessage);
  },

  setResponseStatus: function(isSuccess) {
    var isSuccess = isSuccess == 'success';
    this.setConnectingStatus(false);
    this.set('responseCaption', isSuccess ? Em.I18n.t('services.service.config.database.connection.success') : Em.I18n.t('services.service.config.database.connection.failed'));
    this.set('isConnectionSuccess', isSuccess);
    this.set('isRequestResolved', true);
    if (this.get('logsPopup')) {
      var statusString = isSuccess ? 'common.success' : 'common.error';
      this.set('logsPopup.header', Em.I18n.t('services.service.config.connection.logsPopup.header').format(this.get('databaseName'), Em.I18n.t(statusString)));
    }
  },
  /**
   * Switch captions and statuses for active/non-active request.
   *
   * @method setConnectionStatus
   * @param {Boolean} [active]
   */
  setConnectingStatus: function(active) {
    if (active) {
      this.set('responseCaption', Em.I18n.t('services.service.config.database.connection.inProgress'));
    }
    this.set('controller.testConnectionInProgress', !!active);
    this.set('btnCaption', !!active ? Em.I18n.t('services.service.config.database.btn.connecting') : Em.I18n.t('services.service.config.database.btn.idle'));
    this.set('isConnecting', !!active);
  },
  /**
   * Set view to init status.
   *
   * @method restore
   **/
  restore: function() {
    if (this.get('request')) {
      this.get('request').abort();
      this.set('request', null);
    }
    this.set('responseCaption', null);
    this.set('responseFromServer', null);
    this.setConnectingStatus(false);
    this.set('isRequestResolved', false);
  },
  /**
   * `Action` method for showing response from server in popup.
   *
   * @method showLogsPopup
   **/
  showLogsPopup: function() {
    if (this.get('isConnectionSuccess')) return;
    var _this = this;
    var statusString = this.get('isRequestResolved') ? 'common.error' : 'common.testing';
    var popup = App.showAlertPopup(Em.I18n.t('services.service.config.connection.logsPopup.header').format(this.get('databaseName'), Em.I18n.t(statusString)), null, function () {
      _this.set('logsPopup', null);
    });
    popup.reopen({
      onClose: function () {
        this._super();
        _this.set('logsPopup', null);
      }
    });
    if (typeof this.get('responseFromServer') == 'object') {
      popup.set('bodyClass', Em.View.extend({
        checkDBConnectionView: _this,
        templateName: require('templates/common/error_log_body'),
        openedTask: function () {
          return this.get('checkDBConnectionView.responseFromServer');
        }.property('checkDBConnectionView.responseFromServer.stderr', 'checkDBConnectionView.responseFromServer.stdout', 'checkDBConnectionView.responseFromServer.structuredOut')
      }));
    } else {
      popup.set('body', this.get('responseFromServer'));
    }
    this.set('logsPopup', popup);
    return popup;
  }
});

/**
 * View for switch group text
 *
 * @type {Em.View}
 */
App.SwitchToGroupView = Em.View.extend({

  group: null,

  tagName: 'a',

  classNames: ['action'],

  template: Ember.Handlebars.compile('{{ view.group.switchGroupTextShort }}'),

  didInsertElement: function() {
    var self = this;
    App.tooltip($(self.get('element')), {
      placement: 'top',
      title: self.get('group.switchGroupTextFull')
    });
  },

  willDestroyElement: function() {
    $(this.get('element')).tooltip('destroy');
  },

  click: function() {
    this.get('controller').selectConfigGroup({context: this.get('group')});
  }
});

/**
 * View with input field used to repo-version URLs
 * @type {*}
 */
App.BaseUrlTextField = Ember.TextField.extend({

  layout: Ember.Handlebars.compile('<div class="pull-left">{{yield}}</div> {{#if view.valueWasChanged}}<div class="pull-right"><a class="btn-small" {{action "restoreValue" target="view"}}><i class="icon-undo"></i></a></div>{{/if}}'),

  /**
   * Binding in the template
   * @type {App.RepositoryVersion}
   */
  repository: null,

  /**
   * @type {string}
   */
  valueBinding: 'repository.baseUrl',

  /**
   * @type {string}
   */
  defaultValue: '',

  /**
   *  validate base URL
   */
  validate: function () {
    if (this.get('repository.skipValidation')) {
      this.set('repository.hasError', false);
    } else {
      this.set('repository.hasError', !(validator.isValidBaseUrl(this.get('value'))));
    }
    this.get('parentView').uiValidation();
  }.observes('value', 'repository.skipValidation'),

  /**
   * Determines if user have put some new value
   * @type {boolean}
   */
  valueWasChanged: function () {
    return this.get('value') !== this.get('defaultValue');
  }.property('value', 'defaultValue'),

  didInsertElement: function () {
    this.set('defaultValue', this.get('value'));
  },

  /**
   * Restore value and unset error-flag
   * @method restoreValue
   */
  restoreValue: function () {
    this.set('value', this.get('defaultValue'));
  }
});
