var sparkle = sparkle || {};
(function () {
  /* Application paths *****************************************/

  //Current application root path (including virtual directory if exists).
  sparkle.appPath = sparkle.appPath || '/';
  sparkle.config = sparkle.config || {};
  sparkle.pageLoadTime = new Date();

  //Converts given path to absolute path using sparkle.appPath variable.
  sparkle.toAbsAppPath = function (path) {
    if (path.indexOf('/') == 0) {
      path = path.substring(1);
    }

    return sparkle.appPath + path;
  };

  /* AUTHORIZATION **********************************************/
  //Implements Authorization API that simplifies usage of authorization scripts generated by Abp.

  sparkle.auth = sparkle.auth || {};

  sparkle.auth.allPermissions = sparkle.auth.allPermissions || {};

  sparkle.auth.grantedPermissions = sparkle.auth.grantedPermissions || {};

  sparkle.auth.isGranted = function (permissionName) {
    return sparkle.auth.allPermissions[permissionName] != undefined && sparkle.auth.grantedPermissions[permissionName] != undefined;
  };

  sparkle.auth.isAnyGranted = function () {
    if (!arguments || arguments.length <= 0) {
      return true;
    }

    for (var i = 0; i < arguments.length; i++) {
      if (sparkle.auth.isGranted(arguments[i])) {
        return true;
      }
    }

    return false;
  };

  sparkle.auth.areAllGranted = function () {
    if (!arguments || arguments.length <= 0) {
      return true;
    }

    for (var i = 0; i < arguments.length; i++) {
      if (!sparkle.auth.isGranted(arguments[i])) {
        return false;
      }
    }

    return true;
  };

  sparkle.auth.tokenCookieName = 'Sparkle.AuthToken';

  sparkle.auth.setToken = function (authToken, expireDate) {
    sparkle.utils.setCookieValue(sparkle.auth.tokenCookieName, authToken, expireDate, sparkle.appPath, sparkle.domain);
  };

  sparkle.auth.getToken = function () {
    return sparkle.utils.getCookieValue(sparkle.auth.tokenCookieName);
  };

  sparkle.auth.clearToken = function () {
    sparkle.auth.setToken();
  };

  sparkle.auth.refreshTokenCookieName = 'Sparkle.AuthRefreshToken';

  sparkle.auth.setRefreshToken = function (refreshToken, expireDate) {
    sparkle.utils.setCookieValue(sparkle.auth.refreshTokenCookieName, refreshToken, expireDate, sparkle.appPath, sparkle.domain);
  };

  sparkle.auth.getRefreshToken = function () {
    return sparkle.utils.getCookieValue(sparkle.auth.refreshTokenCookieName);
  };

  sparkle.auth.clearRefreshToken = function () {
    sparkle.auth.setRefreshToken();
  };

  /* SETTINGS **************************************************/
  //Implements Settings API that simplifies usage of setting scripts generated by Abp.

  sparkle.setting = sparkle.setting || {};

  sparkle.setting.values = sparkle.setting.values || {};

  sparkle.setting.get = function (name) {
    return sparkle.setting.values[name];
  };

  sparkle.setting.getBoolean = function (name) {
    var value = sparkle.setting.get(name);
    return value == 'true' || value == 'True';
  };

  sparkle.setting.getInt = function (name) {
    return parseInt(sparkle.setting.values[name]);
  };

  /* REALTIME NOTIFICATIONS ************************************/

  sparkle.notifications = sparkle.notifications || {};

  sparkle.notifications.severity = {
    INFO: 0,
    SUCCESS: 1,
    WARN: 2,
    ERROR: 3,
    FATAL: 4,
  };

  sparkle.notifications.userNotificationState = {
    UNREAD: 0,
    READ: 1,
  };

  sparkle.notifications.getUserNotificationStateAsString = function (userNotificationState) {
    switch (userNotificationState) {
      case sparkle.notifications.userNotificationState.READ:
        return 'READ';
      case sparkle.notifications.userNotificationState.UNREAD:
        return 'UNREAD';
      default:
        sparkle.log.warn('Unknown user notification state value: ' + userNotificationState);
        return '?';
    }
  };

  sparkle.notifications.getUiNotifyFuncBySeverity = function (severity) {
    switch (severity) {
      case sparkle.notifications.severity.SUCCESS:
        return sparkle.notify.success;
      case sparkle.notifications.severity.WARN:
        return sparkle.notify.warn;
      case sparkle.notifications.severity.ERROR:
        return sparkle.notify.error;
      case sparkle.notifications.severity.FATAL:
        return sparkle.notify.error;
      case sparkle.notifications.severity.INFO:
      default:
        return sparkle.notify.info;
    }
  };

  sparkle.notifications.messageFormatters = {};

  sparkle.notifications.messageFormatters['Sparkle.Notifications.MessageNotificationData'] = function (userNotification) {
    return userNotification.notification.data.message || userNotification.notification.data.properties.Message;
  };

  sparkle.notifications.messageFormatters['Sparkle.Notifications.LocalizableMessageNotificationData'] = function (userNotification) {
    var message = userNotification.notification.data.message || userNotification.notification.data.properties.Message;
    var localizedMessage = sparkle.localization.localize(message.name, message.sourceName);

    if (userNotification.notification.data.properties) {
      var properties = Object.keys(userNotification.notification.data.properties);
      for (var i = 0; i < properties.length; i++) {
        localizedMessage = localizedMessage.replace('{' + properties[i] + '}', userNotification.notification.data.properties[properties[i]]);
      }
    }

    return localizedMessage;
  };

  sparkle.notifications.getFormattedMessageFromUserNotification = function (userNotification) {
    var formatter = sparkle.notifications.messageFormatters[userNotification.notification.data.type];
    if (!formatter) {
      sparkle.log.warn('No message formatter defined for given data type: ' + userNotification.notification.data.type);
      return '?';
    }

    if (!sparkle.utils.isFunction(formatter)) {
      sparkle.log.warn('Message formatter should be a function! It is invalid for data type: ' + userNotification.notification.data.type);
      return '?';
    }

    return formatter(userNotification);
  };

  sparkle.notifications.showUiNotifyForUserNotification = function (userNotification, options) {
    var message = sparkle.notifications.getFormattedMessageFromUserNotification(userNotification);
    var uiNotifyFunc = sparkle.notifications.getUiNotifyFuncBySeverity(userNotification.notification.severity);
    uiNotifyFunc(message, undefined, options);
  };

  /* UI *******************************************************/

  sparkle.ui = sparkle.ui || {};

  /* UI BLOCK */
  //Defines UI Block API, not implements it

  sparkle.ui.block = function (elm) {
    sparkle.log.warn('sparkle.ui.block is not implemented!');
  };

  sparkle.ui.unblock = function (elm) {
    sparkle.log.warn('sparkle.ui.unblock is not implemented!');
  };

  /* UI BUSY */
  //Defines UI Busy API, not implements it

  sparkle.ui.setBusy = function (elm, optionsOrPromise) {
    sparkle.log.warn('sparkle.ui.setBusy is not implemented!');
  };

  sparkle.ui.clearBusy = function (elm) {
    sparkle.log.warn('sparkle.ui.clearBusy is not implemented!');
  };

  /* SIMPLE EVENT BUS *****************************************/

  sparkle.event = (function () {
    var _callbacks = {};

    var on = function (eventName, callback) {
      if (!_callbacks[eventName]) {
        _callbacks[eventName] = [];
      }

      _callbacks[eventName].push(callback);
    };

    var off = function (eventName, callback) {
      var callbacks = _callbacks[eventName];
      if (!callbacks) {
        return;
      }

      var index = -1;
      for (var i = 0; i < callbacks.length; i++) {
        if (callbacks[i] === callback) {
          index = i;
          break;
        }
      }

      if (index < 0) {
        return;
      }

      _callbacks[eventName].splice(index, 1);
    };

    var trigger = function (eventName) {
      var callbacks = _callbacks[eventName];
      if (!callbacks || !callbacks.length) {
        return;
      }

      var args = Array.prototype.slice.call(arguments, 1);
      for (var i = 0; i < callbacks.length; i++) {
        callbacks[i].apply(this, args);
      }
    };

    // Public interface ///////////////////////////////////////////////////

    return {
      on: on,
      off: off,
      trigger: trigger,
    };
  })();

  /* UTILS ***************************************************/

  sparkle.utils = sparkle.utils || {};

  /* Creates a name namespace.
   *  Example:
   *  var taskService = sparkle.utils.createNamespace(sparkle, 'services.task');
   *  taskService will be equal to sparkle.services.task
   *  first argument (root) must be defined first
   ************************************************************/
  sparkle.utils.createNamespace = function (root, ns) {
    var parts = ns.split('.');
    for (var i = 0; i < parts.length; i++) {
      if (typeof root[parts[i]] == 'undefined') {
        root[parts[i]] = {};
      }

      root = root[parts[i]];
    }

    return root;
  };

  /* Find and replaces a string (search) to another string (replacement) in
   *  given string (str).
   *  Example:
   *  sparkle.utils.replaceAll('This is a test string', 'is', 'X') = 'ThX X a test string'
   ************************************************************/
  sparkle.utils.replaceAll = function (str, search, replacement) {
    var fix = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    return str.replace(new RegExp(fix, 'g'), replacement);
  };

  /* Formats a string just like string.format in C#.
   *  Example:
   *  sparkle.utils.formatString('Hello {0}','Tuana') = 'Hello Tuana'
   ************************************************************/
  sparkle.utils.formatString = function () {
    if (arguments.length < 1) {
      return null;
    }

    var str = arguments[0];

    for (var i = 1; i < arguments.length; i++) {
      var placeHolder = '{' + (i - 1) + '}';
      str = sparkle.utils.replaceAll(str, placeHolder, arguments[i]);
    }

    return str;
  };

  sparkle.utils.toPascalCase = function (str) {
    if (!str || !str.length) {
      return str;
    }

    if (str.length === 1) {
      return str.charAt(0).toUpperCase();
    }

    return str.charAt(0).toUpperCase() + str.substr(1);
  };

  sparkle.utils.toCamelCase = function (str) {
    if (!str || !str.length) {
      return str;
    }

    if (str.length === 1) {
      return str.charAt(0).toLowerCase();
    }

    return str.charAt(0).toLowerCase() + str.substr(1);
  };

  sparkle.utils.truncateString = function (str, maxLength) {
    if (!str || !str.length || str.length <= maxLength) {
      return str;
    }

    return str.substr(0, maxLength);
  };

  sparkle.utils.truncateStringWithPostfix = function (str, maxLength, postfix) {
    postfix = postfix || '...';

    if (!str || !str.length || str.length <= maxLength) {
      return str;
    }

    if (maxLength <= postfix.length) {
      return postfix.substr(0, maxLength);
    }

    return str.substr(0, maxLength - postfix.length) + postfix;
  };

  sparkle.utils.isFunction = function (obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  };

  /**
   * parameterInfos should be an array of { name, value } objects
   * where name is query string parameter name and value is it's value.
   * includeQuestionMark is true by default.
   */
  sparkle.utils.buildQueryString = function (parameterInfos, includeQuestionMark) {
    if (includeQuestionMark === undefined) {
      includeQuestionMark = true;
    }

    var qs = '';

    function addSeperator() {
      if (!qs.length) {
        if (includeQuestionMark) {
          qs = qs + '?';
        }
      } else {
        qs = qs + '&';
      }
    }

    for (var i = 0; i < parameterInfos.length; ++i) {
      var parameterInfo = parameterInfos[i];
      if (parameterInfo.value === undefined) {
        continue;
      }

      if (parameterInfo.value === null) {
        parameterInfo.value = '';
      }

      addSeperator();

      if (parameterInfo.value.toJSON && typeof parameterInfo.value.toJSON === 'function') {
        qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value.toJSON());
      } else if (Array.isArray(parameterInfo.value) && parameterInfo.value.length) {
        for (var j = 0; j < parameterInfo.value.length; j++) {
          if (j > 0) {
            addSeperator();
          }

          qs = qs + parameterInfo.name + '[' + j + ']=' + encodeURIComponent(parameterInfo.value[j]);
        }
      } else {
        qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value);
      }
    }

    return qs;
  };

  /**
   * Sets a cookie value for given key.
   * This is a simple implementation created to be used by ABP.
   * Please use a complete cookie library if you need.
   * @param {string} key
   * @param {string} value
   * @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session.
   * @param {string} path (optional)
   */
  sparkle.utils.setCookieValue = function (key, value, expireDate, path, domain) {
    var cookieValue = encodeURIComponent(key) + '=';

    if (value) {
      cookieValue = cookieValue + encodeURIComponent(value);
    }

    if (expireDate) {
      cookieValue = cookieValue + '; expires=' + expireDate.toUTCString();
    }

    if (path) {
      cookieValue = cookieValue + '; path=' + path;
    }

    if (domain) {
      cookieValue = cookieValue + '; domain=' + domain;
    }

    document.cookie = cookieValue;
  };

  /**
   * Gets a cookie with given key.
   * This is a simple implementation created to be used by ABP.
   * Please use a complete cookie library if you need.
   * @param {string} key
   * @returns {string} Cookie value or null
   */
  sparkle.utils.getCookieValue = function (key) {
    var equalities = document.cookie.split('; ');
    for (var i = 0; i < equalities.length; i++) {
      if (!equalities[i]) {
        continue;
      }

      var splitted = equalities[i].split('=');
      if (splitted.length != 2) {
        continue;
      }

      if (decodeURIComponent(splitted[0]) === key) {
        return decodeURIComponent(splitted[1] || '');
      }
    }

    return null;
  };

  /**
   * Deletes cookie for given key.
   * This is a simple implementation created to be used by ABP.
   * Please use a complete cookie library if you need.
   * @param {string} key
   * @param {string} path (optional)
   */
  sparkle.utils.deleteCookie = function (key, path) {
    var cookieValue = encodeURIComponent(key) + '=';

    cookieValue = cookieValue + '; expires=' + new Date(new Date().getTime() - 86400000).toUTCString();

    if (path) {
      cookieValue = cookieValue + '; path=' + path;
    }

    document.cookie = cookieValue;
  };

  /* SECURITY ***************************************/
  sparkle.security = sparkle.security || {};
  sparkle.security.antiForgery = sparkle.security.antiForgery || {};

  sparkle.security.antiForgery.tokenCookieName = 'XSRF-TOKEN';
  sparkle.security.antiForgery.tokenHeaderName = 'X-XSRF-TOKEN';

  sparkle.security.antiForgery.getToken = function () {
    return sparkle.utils.getCookieValue(sparkle.security.antiForgery.tokenCookieName);
  };

  sparkle.security.antiForgery.shouldSendToken = function (settings) {
    if (settings.crossDomain === undefined || settings.crossDomain === null) {
      return sparkle.utils.getDomain(location.href) === sparkle.utils.getDomain(settings.url);
    }

    return !settings.crossDomain;
  };
})();
