window.sweKvgStationsDebugger = {};

(function (self) {
  'use strict'

  /***********
   * Private *
   ***********/

  let _availableModules = {},
    _debug = false,
    _extClass = ''

  /**
   * Add available modules.
   *
   * @param {string} filename
   * @param {string[]} modules
   * @private
   */
  function _addModules (filename, modules) {
    if (!_availableModules.hasOwnProperty(filename)) _availableModules[filename] = []

    for (let i = 0; i < modules.length; i += 1) {
      if (_availableModules[filename].indexOf(modules[i]) !== -1) continue
      _availableModules[filename].push(modules[i])
    }
  }

  /**
   * Check if an object is available in window.
   *
   * @param {string} namespace The namespace to check
   * @return {boolean}
   * @private
   */
  function _isWindowGlobal (namespace) {
    for (let name in window) {
      if (name === namespace) return true
    }

    return false
  }

  /**
   * Get the current line number.
   *
   * @return {string} Returns the line number.
   * @private
   */
  function _getLineNumber () {
    let e = new Error()
    if (!e.stack) {
      try {
        throw e
      } catch (e) {
        if (!e.stack) return '0'
      }
    }

    let stack = e.stack.toString().split(/\r\n|\n/),
      frameRe = /:(\d+):(?:\d+)[^\d]*$/,
      frame

    do {
      frame = stack.shift()
    } while (!frameRe.exec(frame) && stack.length)
    stack.shift()
    return frameRe.exec(stack.shift())[1]
  }

  /**
   * Get the debug level.
   *
   * @return {number} Returns the debug level.
   */
  function _getDebugLevel () {
    if (_extClass.length < 1) return 0
    let element = document.querySelector('#' + _extClass + '-debug')
    if (!element || !element.hasAttribute('data-debug-level')) return 0
    return parseInt(element.getAttribute('data-debug-level'))
  }

  /**********
   * Public *
   **********/

  // constants
  self.DEBUG_FUNC_END = 0
  self.DEBUG_FUNC_START = 1
  self.DEBUG_FUNC_CALLED = 2

  // @see \SWE\SweCore\Service\LogServiceInterface
  self.LEVEL_ERROR = 0
  self.LEVEL_NOTICE = 1
  self.LEVEL_WARNING = 2
  self.LEVEL_INFO = 3
  self.LEVEL_DEBUG = 4
  self.LEVEL_EVERYTHING = 5

  /**
   * Check if checked module is available.
   *
   * @param {string} filename
   * @param {string} module
   * @return {boolean}
   */
  self.isModuleAvailable = function (filename, module) {
    if (!_availableModules.hasOwnProperty(filename)) return false
    return _availableModules[filename].indexOf(module) !== -1
  }

  /**
   * @param {number} group
   * @param {string} [name='anonymous'] OPTIONAL.
   */
  self.debuggingFunc = function (group, name) {
    let functionDebugLevel = self.LEVEL_WARNING

    if (!self.isDebug() || _getDebugLevel() < functionDebugLevel) return
    if (typeof name === 'undefined' || name === null) name = 'anonymous'

    switch (group) {
      case self.DEBUG_FUNC_START:
        console.group(name)
        break
      case self.DEBUG_FUNC_END:
        console.groupEnd()
        break
      case self.DEBUG_FUNC_CALLED:
        self.debuggingFunc(self.DEBUG_FUNC_START, name)
        self.debuggingFunc(self.DEBUG_FUNC_END, name)
        break
    }
  }

  /**
   * Print a variable via console.log if the debug mode is enabled.
   *
   * @param {*} variable The variable to debug.
   * @param {number|null} [level=self.LEVEL_INFO] OPTIONAL. The log level.
   */
  self.debug = function (variable, level) {
    if (typeof level === 'undefined' || level === null) level = self.LEVEL_INFO
    if (!self.isDebug() || _getDebugLevel() < level ) return
    console.log('Line: ' + _getLineNumber() + ' -', variable)
  }

  /**
   * Print an error message via console.warn. If the debug mode is disabled, printing a default error message.
   *
   * @param {string} message The error message.
   * @param {number|null} [level=self.LEVEL_ERROR] OPTIONAL. The log level.
   */
  self.error = function (message, level) {
    if (typeof level === 'undefined' || level === null) level = self.LEVEL_ERROR
    if (_getDebugLevel() >= level || self.isDebug()) {
      console.warn('Line: ' + _getLineNumber() + ' -', message)
      return
    }

    console.warn('Line: ' + _getLineNumber() + ' -', 'An error occurred')
  }

  /**
   * @param {string} extClass
   */
  self.setExtClass = function (extClass) {
    _extClass = extClass
  }

  /**
   * @return {string}
   */
  self.getExtClass = function () {
    return _extClass
  }

  /**
   * Check if the debug mode is enabled.
   *
   * @return {boolean} Returns TRUE if the debug mode is enabled, FALSE if it's disabled.
   */
  self.isDebug = function () {
    if (_debug) return _debug
    if (_extClass.length < 1) return _debug
    let element = document.querySelector('#' + _extClass + '-debug')
    if (!element || !element.hasAttribute('data-debug')) return _debug
    return element.getAttribute('data-debug') === '1'
  }

  /**
   * @param {boolean} debug
   */
  self.setDebug = function (debug) {
    _debug = debug === true
  }

  /**
   * Check if modules are available.
   *
   * @param {string[]} modules
   * @param {string} type
   * @param {boolean} stop
   * @param {string} filename
   */
  self.checkModules = function (modules, type, stop, filename) {
    self.debuggingFunc(self.DEBUG_FUNC_START, 'sweKvgStationsDebugger.checkModules')
    self.debug('Checking modules for: ' + filename, self.LEVEL_INFO)
    self.debug('Modules to check: ' + JSON.stringify(modules), self.LEVEL_DEBUG)
    self.debug('Modules are: ' + type, self.LEVEL_DEBUG)

    let notFound = [],
      stringFilename = '[' + filename + '.js] ',
      string = stringFilename + type.toUpperCase() + ' NAMESPACES NOT FOUND: '

    if (modules.length < 1) {
      _addModules(filename, modules)
      string = stringFilename + 'No modules to load.'
      self.debug(string, self.LEVEL_INFO)
      self.debuggingFunc(self.DEBUG_FUNC_END)
      return
    }

    for (let i = 0; i < modules.length; i += 1) {
      if (_isWindowGlobal(modules[i])) {
        _addModules(filename, [modules[i]])
        continue
      }

      notFound.push(modules[i])
    }

    if (notFound.length === 0) {
      string = stringFilename + 'All ' + type + ' modules (' + modules.length + ') loaded.'
      self.debug(string, self.LEVEL_INFO)
      self.debuggingFunc(self.DEBUG_FUNC_END)
      return
    }

    if (stop) {
      self.debuggingFunc(self.DEBUG_FUNC_END)
      throw string + notFound.join(', ')
    }

    self.error(string + notFound.join(', '), self.LEVEL_ERROR)
    self.debuggingFunc(self.DEBUG_FUNC_END)
  }

})(window.sweKvgStationsDebugger)
