'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _angular = require('angular');

var _angular2 = _interopRequireDefault(_angular);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// Use factory() instead service() for multiple datasources support.
// Each datasource instance must initialize its own cache.

/** @ngInject */
function ZabbixCachingProxyFactory() {
  var ZabbixCachingProxy = function () {
    function ZabbixCachingProxy(zabbixAPI, cacheOptions) {
      _classCallCheck(this, ZabbixCachingProxy);

      this.zabbixAPI = zabbixAPI;
      this.cacheEnabled = cacheOptions.enabled;
      this.ttl = cacheOptions.ttl || 600000; // 10 minutes by default

      // Internal objects for data storing
      this.cache = {
        groups: {},
        hosts: {},
        applications: {},
        items: {},
        history: {},
        trends: {}
      };

      this.historyPromises = {};

      // Don't run duplicated history requests
      this.getHistory = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getHistory, this.zabbixAPI), this.historyPromises, getHistoryRequestHash);

      // Don't run duplicated requests
      this.groupPromises = {};
      this.getGroupsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getGroups, this.zabbixAPI), this.groupPromises, getRequestHash);

      this.hostPromises = {};
      this.getHostsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getHosts, this.zabbixAPI), this.hostPromises, getRequestHash);

      this.appPromises = {};
      this.getAppsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getApps, this.zabbixAPI), this.appPromises, getRequestHash);

      this.itemPromises = {};
      this.getItemsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getItems, this.zabbixAPI), this.itemPromises, getRequestHash);
    }

    _createClass(ZabbixCachingProxy, [{
      key: 'isExpired',
      value: function isExpired(cacheObject) {
        if (cacheObject) {
          var object_age = Date.now() - cacheObject.timestamp;
          return !(cacheObject.timestamp && object_age < this.ttl);
        } else {
          return true;
        }
      }

      /**
       * Check that result is present in cache and up to date
       * or send request to API.
       */

    }, {
      key: 'proxyRequest',
      value: function proxyRequest(request, params, cacheObject) {
        var hash = getRequestHash(params);
        if (this.cacheEnabled && !this.isExpired(cacheObject[hash])) {
          return Promise.resolve(cacheObject[hash].value);
        } else {
          return request.apply(undefined, _toConsumableArray(params)).then(function (result) {
            cacheObject[hash] = {
              value: result,
              timestamp: Date.now()
            };
            return result;
          });
        }
      }
    }, {
      key: 'getGroups',
      value: function getGroups() {
        return this.proxyRequest(this.getGroupsOnce, [], this.cache.groups);
      }
    }, {
      key: 'getHosts',
      value: function getHosts(groupids) {
        return this.proxyRequest(this.getHostsOnce, [groupids], this.cache.hosts);
      }
    }, {
      key: 'getApps',
      value: function getApps(hostids) {
        return this.proxyRequest(this.getAppsOnce, [hostids], this.cache.applications);
      }
    }, {
      key: 'getItems',
      value: function getItems(hostids, appids, itemtype) {
        var params = [hostids, appids, itemtype];
        return this.proxyRequest(this.getItemsOnce, params, this.cache.items);
      }
    }, {
      key: 'getHistoryFromCache',
      value: function getHistoryFromCache(items, time_from, time_till) {
        var historyStorage = this.cache.history;
        var full_history;
        var expired = _lodash2.default.filter(_lodash2.default.keyBy(items, 'itemid'), function (item, itemid) {
          return !historyStorage[itemid];
        });
        if (expired.length) {
          return this.zabbixAPI.getHistory(expired, time_from, time_till).then(function (history) {
            var grouped_history = _lodash2.default.groupBy(history, 'itemid');
            _lodash2.default.forEach(expired, function (item) {
              var itemid = item.itemid;
              historyStorage[itemid] = item;
              historyStorage[itemid].time_from = time_from;
              historyStorage[itemid].time_till = time_till;
              historyStorage[itemid].history = grouped_history[itemid];
            });
            full_history = _lodash2.default.map(items, function (item) {
              return historyStorage[item.itemid].history;
            });
            return _lodash2.default.flatten(full_history, true);
          });
        } else {
          full_history = _lodash2.default.map(items, function (item) {
            return historyStorage[item.itemid].history;
          });
          return Promise.resolve(_lodash2.default.flatten(full_history, true));
        }
      }
    }, {
      key: 'getHistoryFromAPI',
      value: function getHistoryFromAPI(items, time_from, time_till) {
        return this.zabbixAPI.getHistory(items, time_from, time_till);
      }
    }]);

    return ZabbixCachingProxy;
  }();

  return ZabbixCachingProxy;
}

_angular2.default.module('grafana.services').factory('ZabbixCachingProxy', ZabbixCachingProxyFactory);

/**
 * Wrap zabbix API request to prevent multiple calls
 * with same params when waiting for result.
 */
function callAPIRequestOnce(func, promiseKeeper, argsHashFunc) {
  return function () {
    var hash = argsHashFunc(arguments);
    if (!promiseKeeper[hash]) {
      promiseKeeper[hash] = Promise.resolve(func.apply(this, arguments).then(function (result) {
        promiseKeeper[hash] = null;
        return result;
      }));
    }
    return promiseKeeper[hash];
  };
}

function getRequestHash(args) {
  var requestStamp = _lodash2.default.map(args, function (arg) {
    if (arg === undefined) {
      return 'undefined';
    } else {
      if (_lodash2.default.isArray(arg)) {
        return arg.sort().toString();
      } else {
        return arg.toString();
      }
    }
  }).join();
  return requestStamp.getHash();
}

function getHistoryRequestHash(args) {
  var itemids = _lodash2.default.map(args[0], 'itemid');
  var stamp = itemids.join() + args[1] + args[2];
  return stamp.getHash();
}

String.prototype.getHash = function () {
  var hash = 0,
      i,
      chr,
      len;
  if (this.length !== 0) {
    for (i = 0, len = this.length; i < len; i++) {
      chr = this.charCodeAt(i);
      hash = (hash << 5) - hash + chr;
      hash |= 0; // Convert to 32bit integer
    }
  }
  return hash;
};

// Fix for backward compatibility with lodash 2.4
if (!_lodash2.default.keyBy) {
  _lodash2.default.keyBy = _lodash2.default.indexBy;
}
