text: /**
   * 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');
  
  App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
    name: 'mainHostController',
  
    clearFilters: null,
  
    filteredCount: 0,
  
    /**
     * total number of installed hosts
     * @type {number}
     */
    totalCount: function () {
      return this.get('hostsCountMap')['TOTAL'] || 0;
    }.property('hostsCountMap'),
  
    /**
     * @type {boolean}
     * @default false
     */
    resetStartIndex: false,
  
    /**
     * flag responsible for updating status counters of hosts
     */
    isCountersUpdating: false,
  
    hostsCountMap: {},
  
    startIndex: 1,
  
    /**
     * true if any host page filter changed
     */
    filterChangeHappened: false,
  
    /**
     * if true, do not clean stored filter before hosts page rendering.
     */
    showFilterConditionsFirstLoad: false,
  
    content: App.Host.find(),
  
    allHostStackVersions: App.HostStackVersion.find(),
    /**
     * filterProperties support follow types of filter:
     * MATCH - match of RegExp
     * EQUAL - equality "="
     * LESS - "<"
     * MORE - ">"
     * MULTIPLE - multiple values to compare
     * CUSTOM - substitute values with keys "{#}" in alias
     */
    filterProperties: [
      {
        name: 'hostName',
        key: 'Hosts/host_name',
        type: 'MATCH'
      },
      {
        name: 'ip',
        key: 'Hosts/ip',
        type: 'MATCH'
      },
      {
        name: 'cpu',
        key: 'Hosts/cpu_count',
        type: 'EQUAL'
      },
      {
        name: 'memoryFormatted',
        key: 'Hosts/total_mem',
        type: 'EQUAL'
      },
      {
        name: 'loadAvg',
        key: 'metrics/load/load_one',
        type: 'EQUAL'
      },
      {
        name: 'rack',
        key: 'Hosts/rack_info',
        type: 'MATCH'
      },
      {
        name: 'hostComponents',
        key: 'host_components/HostRoles/component_name',
        type: 'EQUAL',
        isComponentRelatedFilter: true
      },
      {
        name: 'services',
        key: 'host_components/HostRoles/service_name',
        type: 'MATCH',
        isComponentRelatedFilter: true
      },
      {
        name: 'state',
        key: 'host_components/HostRoles/state',
        type: 'MATCH',
        isComponentRelatedFilter: true
      },
      {
        name: 'healthClass',
        key: 'Hosts/host_status',
        type: 'EQUAL'
      },
      {
        name: 'criticalWarningAlertsCount',
        key: '(alerts_summary/CRITICAL{0}|alerts_summary/WARNING{1})',
        type: 'CUSTOM'
      },
      {
        name: 'componentsWithStaleConfigsCount',
        key: 'host_components/HostRoles/stale_configs',
        type: 'EQUAL',
        isComponentRelatedFilter: true
      },
      {
        name: 'componentsInPassiveStateCount',
        key: 'host_components/HostRoles/maintenance_state',
        type: 'MULTIPLE',
        isComponentRelatedFilter: true
      },
      {
        name: 'selected',
        key: 'Hosts/host_name',
        type: 'MULTIPLE'
      },
      {
        name: 'version',
        key: 'stack_versions/repository_versions/RepositoryVersions/display_name',
        type: 'EQUAL'
      },
      {
        name: 'versionState',
        key: 'stack_versions/HostStackVersions/state',
        type: 'EQUAL'
      },
      {
        name: 'hostStackVersion',
        key: 'stack_versions',
        type: 'EQUAL'
      },
      {
        name: 'componentState',
        key: [
          '(host_components/HostRoles/component_name={0})',
          '(host_components/HostRoles/component_name={0}&host_components/HostRoles/state={1})',
          '(host_components/HostRoles/component_name={0}&host_components/HostRoles/desired_admin_state={1})',
          '(host_components/HostRoles/component_name={0}&host_components/HostRoles/maintenance_state={1})'
        ],
        type: 'COMBO',
        isComponentRelatedFilter: true
      }
    ],
  
    sortProps: [
      {
        name: 'hostName',
        key: 'Hosts/host_name'
      },
      {
        name: 'ip',
        key: 'Hosts/ip'
      },
      {
        name: 'cpu',
        key: 'Hosts/cpu_count'
      },
      {
        name: 'memoryFormatted',
        key: 'Hosts/total_mem'
      },
      {
        name: 'diskUsage',
        //TODO disk_usage is relative property and need support from API, metrics/disk/disk_free used temporarily
        key: 'metrics/disk/disk_free'
      },
      {
        name: 'rack',
        key: 'Hosts/rack_info'
      },
      {
        name: 'loadAvg',
        key: 'metrics/load/load_one'
      }
    ],
  
    /**
     * Validate and convert input string to valid url parameter.
     * Detect if user have passed string as regular expression or extend
     * string to regexp.
     *
     * @param {String} value
     * @return {String}
     **/
    getRegExp: function (value) {
      value = validator.isValidMatchesRegexp(value) ? value.replace(/(\.+\*?|(\.\*)+)$/, '') + '.*' : '^$';
      value = /^\.\*/.test(value) || value == '^$' ? value : '.*' + value;
      return value;
    },
  
    /**
     * Sort by host_name by default
     * @method getSortProps
     * @returns {{value: 'asc|desc', name: string, type: 'SORT'}[]}
     */
    getSortProps: function () {
      var controllerName = this.get('name'),
        db = App.db.getSortingStatuses(controllerName);
      if (db && db.everyProperty('status', 'sorting')) {
        App.db.setSortingStatuses(controllerName, {
          name: 'hostName',
          status: 'sorting_asc'
        });
      }
      return this._super();
    },
  
    /**
     * get query parameters computed from filter properties, sort properties and custom properties of view
     * @param {boolean} [skipNonFilterProperties]
     * @return {Array}
     * @method getQueryParameters
     */
    getQueryParameters: function (skipNonFilterProperties) {
      skipNonFilterProperties = skipNonFilterProperties || false;
      var queryParams = [],
        savedFilterConditions = App.db.getFilterConditions(this.get('name')) || [],
        colPropAssoc = this.get('colPropAssoc'),
        filterProperties = this.get('filterProperties'),
        sortProperties = this.get('sortProps'),
        oldProperties = App.router.get('updateController.queryParams.Hosts');
  
      this.set('resetStartIndex', false);
  
      queryParams.pushObjects(this.getPaginationProps());
  
      savedFilterConditions.forEach(function (filter) {
        var property = filterProperties.findProperty('name', colPropAssoc[filter.iColumn]);
        if (property && filter.value.length > 0 && !filter.skipFilter) {
          var result = {
            key: property.key,
            value: filter.value,
            type: property.type,
            isFilter: true,
            isComponentRelatedFilter: property.isComponentRelatedFilter
          };
          if (filter.type === 'string' && sortProperties.someProperty('name', colPropAssoc[filter.iColumn])) {
            if (Em.isArray(filter.value)) {
              for(var i = 0; i < filter.value.length; i++) {
                filter.value[i] = this.getRegExp(filter.value[i]);
              }
            } else {
              result.value = this.getRegExp(filter.value);
            }
          }
          if (filter.type === 'number' || filter.type === 'ambari-bandwidth') {
            result.type = this.getComparisonType(filter.value);
            result.value = this.getProperValue(filter.value);
          }
          // enter an exact number for RAM filter, need to do a range number match for this
          if (filter.type === 'ambari-bandwidth' && result.type == 'EQUAL' && result.value) {
            var valuePair = this.convertMemoryToRange(filter.value);
            queryParams.push({
              key: result.key,
              value: valuePair[0],
              type: 'MORE'
            });
            queryParams.push({
              key: result.key,
              value: valuePair[1],
              type: 'LESS'
            });
          } else if (filter.type === 'ambari-bandwidth' && result.type != 'EQUAL' && result.value){
            // enter a comparison type, eg > 1, just do regular match
            result.value = this.convertMemory(filter.value);
            queryParams.push(result);
          } else if (filter.type === 'sub-resource') {
            filter.value.forEach(function (item) {
              queryParams.push({
                key: result.key + "/" + item.property,
                value: item.value,
                type: 'EQUAL'
              });
            }, this);
          } else {
            queryParams.push(result);
          }
  
        }
      }, this);
  
      if (queryParams.filterProperty('isFilter').length !== oldProperties.filterProperty('isFilter').length) {
        queryParams.findProperty('key', 'from').value = 0;
        this.set('resetStartIndex', true);
      } else {
        queryParams.filterProperty('isFilter').forEach(function (queryParam) {
          var oldProperty = oldProperties.filterProperty('isFilter').findProperty('key', queryParam.key);
          if (!oldProperty || JSON.stringify(oldProperty.value) !== JSON.stringify(queryParam.value)) {
            queryParams.findProperty('key', 'from').value = 0;
            this.set('resetStartIndex', true);
          }
        }, this);
      }
  
      if (!skipNonFilterProperties) {
        queryParams.pushObjects(this.getSortProps());
      }
  
      return queryParams;
    },
  
    /**
     * update status counters of hosts
     */
    updateStatusCounters: function () {
      var self = this;
  
      if (this.get('isCountersUpdating')) {
        App.ajax.send({
          name: 'host.status.counters',
          sender: this,
          data: {},
          success: 'updateStatusCountersSuccessCallback',
          error: 'updateStatusCountersErrorCallback',
          callback: function() {
            setTimeout(function () {
              self.updateStatusCounters();
            }, App.get('hostStatusCountersUpdateInterval'));
          }
        });
      }
    },
  
    /**
     * success callback on <code>updateStatusCounters()</code>
     * map counters' value to categories
     * @param data
     */
    updateStatusCountersSuccessCallback: function (data) {
      var hostsCountMap = {
        'HEALTHY': data.Clusters.health_report['Host/host_status/HEALTHY'],
        'UNHEALTHY': data.Clusters.health_report['Host/host_status/UNHEALTHY'],
        'ALERT': data.Clusters.health_report['Host/host_status/ALERT'],
        'UNKNOWN': data.Clusters.health_report['Host/host_status/UNKNOWN'],
        'health-status-RESTART': data.Clusters.health_report['Host/stale_config'],
        'health-status-PASSIVE_STATE': data.Clusters.health_report['Host/maintenance_state'],
        'TOTAL': data.Clusters.total_hosts
      };
  
      this.set('hostsCountMap', hostsCountMap);
    },
  
    /**
     * success callback on <code>updateStatusCounters()</code>
     */
    updateStatusCountersErrorCallback: Em.K,
  
    /**
     * Return value without predicate
     * @param {String} value
     * @return {String}
     */
    getProperValue: function (value) {
      return (['>', '<', '='].contains(value.charAt(0))) ? value.substr(1, value.length) : value;
    },
  
    /**
     * Return value converted to kilobytes
     * @param {String} value
     * @return {number}
     */
    convertMemory: function (value) {
      var scale = value.charAt(value.length - 1);
      // first char may be predicate for comparison
      value = this.getProperValue(value);
      var parsedValue = parseFloat(value);
  
      if (isNaN(parsedValue)) {
        return value;
      }
  
      switch (scale) {
        case 'g':
          parsedValue *= 1048576;
          break;
        case 'm':
          parsedValue *= 1024;
          break;
        case 'k':
          break;
        default:
          //default value in GB
          parsedValue *= 1048576;
      }
      return Math.round(parsedValue);
    },
  
    /**
     * Return value converted to a range of kilobytes
     * @param {String} value
     * @return {Array}
     */
    convertMemoryToRange: function (value) {
      var scale = value.charAt(value.length - 1);
      // first char may be predicate for comparison
      value = this.getProperValue(value);
      var parsedValue = parseFloat(value);
      if (isNaN(parsedValue)) {
        return [0, 0];
      }
      var parsedValuePair = this.rangeConvertNumber(parsedValue, scale);
      var multiplyingFactor = 1;
      switch (scale) {
        case 'g':
          multiplyingFactor = 1048576;
          break;
        case 'm':
          multiplyingFactor = 1024;
          break;
        case 'k':
          break;
        default:
          //default value in GB
          multiplyingFactor = 1048576;
      }
      parsedValuePair[0]  = Math.round( parsedValuePair[0] * multiplyingFactor);
      parsedValuePair[1]  = Math.round( parsedValuePair[1] * multiplyingFactor);
      return parsedValuePair;
    },
  
    /**
     * Return value converted to a range of kilobytes
     * eg, return value 1.83 g will target 1.82500 ~ 1.83499 g
     * eg, return value 1.8 k will target 1.7500 ~ 1.8499 k
     * eg, return value 1.8 m will target 1.7500 ~ 1.8499 m
     * @param {number} value
     * @param {String} scale
     * @return {Array}
     */
    rangeConvertNumber: function (value, scale) {
      if (isNaN(value)) {
        return [0, 0];
      }
      var valuePair = [];
      switch (scale) {
        case 'g':
          valuePair = [value - 0.005000, value + 0.004999999];
          break;
        case 'm':
        case 'k':
          valuePair = [value - 0.05000, value + 0.04999];
          break;
        default:
          //default value in GB
          valuePair = [value - 0.005000, value + 0.004999999];
      }
      return valuePair;
    },
  
    /**
     * Return comparison type depending on populated predicate
     * @param {string} value
     * @return {string}
     */
    getComparisonType: function (value) {
      var comparisonChar = value.charAt(0);
      var result = 'EQUAL';
      if (isNaN(comparisonChar)) {
        switch (comparisonChar) {
          case '>':
            result = 'MORE';
            break;
          case '<':
            result = 'LESS';
            break;
        }
      }
      return result;
    },
  
    labelValueMap: {},
  
    /**
     * Filter hosts by componentName of <code>component</code>
     * @param {App.HostComponent} component
     */
    filterByComponent: function (component) {
      if (!component) return;
      var componentName = component.get('componentName');
      var displayName = App.format.role(componentName, false);
      var colPropAssoc = this.get('colPropAssoc');
      var map = this.get('labelValueMap');
  
      var filterForComponent = {
        iColumn: 15,
        value: componentName + ':ALL',
        type: 'string'
      };
      map[displayName] = componentName;
      map['All'] = 'ALL';
      var filterStr = '"' + displayName + '"' + ': "All"';
      App.db.setFilterConditions(this.get('name'), [filterForComponent]);
      App.db.setComboSearchQuery(this.get('name'), filterStr);
    },
  
    /**
     * Filter hosts by stack version and state
     * @param {String} displayName
     * @param {Array} states
     */
    filterByStack: function (displayName, states) {
      if (Em.isNone(displayName) || Em.isNone(states) || !states.length) return;
      var colPropAssoc = this.get('colPropAssoc');
      var map = this.get('labelValueMap');
      var stateFilterStrs = [];
  
      var versionFilter = {
        iColumn: 16,
        value: displayName,
        type: 'string'
      };
      var stateFilter = {
        iColumn: 17,
        value: states,
        type: 'string'
      };
      map["Stack Version"] = colPropAssoc[versionFilter.iColumn];
      map["Version State"] = colPropAssoc[stateFilter.iColumn];
      stateFilter.value.forEach(function(state) {
        map[App.HostStackVersion.formatStatus(state)] = state;
        stateFilterStrs.push('"Version State": "' + App.HostStackVersion.formatStatus(state) + '"');
      });
      var versionFilterStr = '"Stack Version": "' + versionFilter.value + '"';
      App.db.setFilterConditions(this.get('name'), [versionFilter, stateFilter]);
      App.db.setComboSearchQuery(this.get('name'), [versionFilterStr, stateFilterStrs.join(' ')].join(' '));
    },
  
    goToHostAlerts: function (event) {
      var host = event && event.context;
      if (host) {
        App.router.transitionTo('main.hosts.hostDetails.alerts', host);
      }
    },
  
    /**
     * remove selected hosts
     */
    removeHosts: function () {
      var hosts = this.get('content');
      var selectedHosts = hosts.filterProperty('isChecked');
      this.get('fullContent').removeObjects(selectedHosts);
    },
  
    /**
     * remove hosts with id equal host_id
     * @param {String} host_id
     */
    checkRemoved: function (host_id) {
      var hosts = this.get('content');
      var selectedHosts = hosts.filterProperty('id', host_id);
      this.get('fullContent').removeObjects(selectedHosts);
    },
  
    /**
     * associations between host property and column index
     * @type {Array}
     */
    colPropAssoc: function () {
      var associations = [];
      associations[0] = 'healthClass';
      associations[1] = 'hostName';
      associations[2] = 'ip';
      associations[3] = 'cpu';
      associations[4] = 'memoryFormatted';
      associations[5] = 'loadAvg';
      associations[6] = 'hostComponents';
      associations[7] = 'criticalWarningAlertsCount';
      associations[8] = 'componentsWithStaleConfigsCount';
      associations[9] = 'componentsInPassiveStateCount';
      associations[10] = 'selected';
      associations[11] = 'hostStackVersion';
      associations[12] = 'rack';
      associations[13] = 'services';
      associations[14] = 'state';
      associations[15] = 'componentState';
      associations[16] = 'version';
      associations[17] = 'versionState';
      return associations;
    }.property()
  
  });
  