window.sweKvgStationsHelper = {};

(function (self) {
  'use strict'

  /***********
   * Private *
   ***********/

  let _requiredModules = [],
    _suggestedModules = [],
    _filename = 'swe-helper',
    _checked = false,
    _cookieProtection = true,
    _elementPrefixes = [
      '#',
      '.',
      '[',
    ],
    _htmlTags = []

  /**
   * @private
   */
  function _check () {
    if (_checked) return
    if (!sweKvgStationsDebugger) throw '[' + _filename + '.js] Required namespace "sweKvgStationsDebugger" not found.'

    sweKvgStationsDebugger.checkModules(_requiredModules, 'required', true, _filename)
    sweKvgStationsDebugger.checkModules(_suggestedModules, 'suggested', false, _filename)
    _checked = true
  }

  /**
   * Set all available html tag names from the current dom.
   * @private
   */
  function _setAllHtmlTagNames () {
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_START, 'sweKvgStationsHelper._getAllHtmlTagNames')
    let all = document.getElementsByTagName('*')

    for (let i = 0, max = all.length; i < max; i += 1) {
      let tag = all[i].tagName.toLowerCase()
      if (_htmlTags.indexOf(tag, 0) !== -1) continue
      _htmlTags.push(tag)
    }

    sweKvgStationsDebugger.debug(_htmlTags, sweKvgStationsDebugger.LEVEL_DEBUG)
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
  }

  /**
   * Prepares a selector string.
   *
   * @param {string} selector The selector.
   * @param {string} prefix The selector prefix.
   * @return {string} Returns selector string.
   */
  function _prepareSelector (selector, prefix) {
    if (_htmlTags.length === 0) _setAllHtmlTagNames()

    if (_htmlTags.indexOf(selector, 0) === -1) {
      if (_elementPrefixes.indexOf(selector.charAt(0), 0) === -1) selector = prefix + selector
    }

    return selector
  }

  /**********
   * Public *
   **********/

  /**
   * @type {number}
   */
  self.recursionCounter = 0

  /**
   * @type {number}
   */
  self.maxRecursions = 10

  /**
   * Get the cookie protection.
   *
   * @return {boolean}
   */
  self.getCookieProtection = function () {
    return _cookieProtection
  }

  /**
   * Set the cookie protection.
   *
   * @param {boolean} protection
   */
  self.setCookieProtection = function (protection) {
    _cookieProtection = protection
  }

  /**
   * Check if we can create cookies.
   *
   * @return {boolean}
   */
  self.isCookieAllowed = function () {
    return self.setCookie('cookie-test', 'test', 0)
  }

  /**
   * Get a cookie.
   *
   * @param {string} name The name of the cookie.
   * @returns {string} Returns the cookie value.
   */
  self.getCookie = function (name) {
    let nameEQ = name + '=',
      cookieArray = document.cookie.split(';')

    for (let i = 0; i < cookieArray.length; i += 1) {
      let cookie = cookieArray[i]

      while (cookie.charAt(0) === ' ') {
        cookie = cookie.substring(1, cookie.length)
      }

      if (cookie.indexOf(nameEQ) === 0) {
        return cookie.substring(nameEQ.length, cookie.length)
      }
    }

    return ''
  }

  /**
   * Set or delete a cookie.
   *
   * @param {string} name The name of the cookie.
   * @param {string} value The value of the cookie.
   * @param {number|null} [days=null] OPTIONAL. The number of days until the cookie expires.
   * @return {boolean} Returns TRUE if the cookie was set, FALSE if the cookieBanner is not set.
   */
  self.setCookie = function (name, value, days) {
    let date = new Date(),
      expires = '; expires=',
      gmtString = 'Thu, 01 Jan 1970 00:00:01 GMT'

    if (_cookieProtection && self.getCookie('cookieBanner').toLowerCase() !== 'set') {
      if (name !== 'cookie-test') {
        sweKvgStationsDebugger.error('The cookie banner is not accepted')
        sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
      }

      return false
    }

    if (days) {
      date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000))
      gmtString = date.toUTCString()
    }

    document.cookie = name + '=' + value + expires + gmtString + '; path=/'
    return true
  }

  /**
   * Remove all children elements from an element.
   *
   * @param {HTMLElement} element The HTMLElement instance.
   */
  self.removeChildren = function (element) {
    while (element.hasChildNodes()) {
      element.removeChild(element.firstChild)
    }
  }

  /**
   * Get an element by a selector.
   *
   * @param {string} selector The selector.
   * @param {HTMLElement|Node|Document} [element=null] OPTIONAL. The element to search from.
   * @return {HTMLElement} Returns the html element.
   */
  self.getElement = function (selector, element) {
    element = element || document

    return element.querySelector(_prepareSelector(selector, '#'))
  }

  /**
   * Get elements by a selector.
   *
   * @param {string} selector The selector.
   * @param {HTMLElement|Node|Document} [element=null] OPTIONAL. The element to search from.
   * @return {NodeListOf<HTMLElement>} Returns a list of html elements.
   */
  self.getElements = function (selector, element) {
    element = element || document

    return element.querySelectorAll(_prepareSelector(selector, '.'))
  }

  /**
   * Get the parent element with the given selector.
   *
   * @param {HTMLElement} element
   * @param {string} selector
   * @return {HTMLElement}
   */
  self.getParentWithClass = function (element, selector) {
    while ((element = element.parentElement) && !(element.classList.contains(selector))) {
      // Nothing inside.
    }

    return element
  }

  /**
   * Remove a css class from a dom element.
   *
   * @param {HTMLElement} element The html element.
   * @param {string} className The name of the class.
   */
  self.removeDomClass = function (element, className) {
    if (element.classList.contains(className)) element.classList.remove(className)
  }

  /**
   * Add a css class to a dom element.
   *
   * @param {HTMLElement} element The html element.
   * @param {string} className The name of the class.
   */
  self.addDomClass = function (element, className) {
    if (!element.classList.contains(className)) element.classList.add(className)
  }

  /**
   * Show an html element.
   *
   * @param {HTMLElement} element The html element.
   */
  self.show = function (element) {
    self.removeDomClass(element, 'hidden')
  }

  /**
   * Hide an html element.
   *
   * @param {HTMLElement} element The html element.
   */
  self.hide = function (element) {
    self.addDomClass(element, 'hidden')
  }

  /**
   * Check if an element is hidden.
   *
   * @param {HTMLElement} element The html element to check.
   * @return {boolean} Returns TRUE if the element is hidden.
   */
  self.isHidden = function (element) {
    return element.classList.contains('hidden')
  }

  /**
   * Get a random index.
   *
   * @param {array} array An array where we want to get a random index.
   * @return {number} Returns the index.
   */
  self.getRandomIndex = function (array) {
    return Math.floor(Math.random() * array.length)
  }

  /**
   * Endless loop protection.
   *
   * @param {number} [max=null] OPTIONAL. The maximum of allowed recursions.
   * @return {number|null} Returns the number of recursions if the limit is reached, NULL if it's alright.
   */
  self.recursionProtection = function (max) {
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_START, 'sweHelperGroupTravel.recursionProtection')
    if (typeof max === 'undefined' || max === 0) max = self.maxRecursions
    let infoMessage = 'Recursion counter: ' + self.recursionCounter.toString()

    if (max > self.recursionCounter) {
      self.recursionCounter += 1
      sweKvgStationsDebugger.debug(infoMessage, sweKvgStationsDebugger.LEVEL_INFO)
      sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
      return null
    }

    let amount = self.recursionCounter
    infoMessage = 'The maximum of recursions is reached. (Amount: ' + amount + ')'
    self.resetRecursion()
    sweKvgStationsDebugger.debug(infoMessage, sweKvgStationsDebugger.LEVEL_INFO)
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
    return amount
  }

  /**
   * Reset the recursion counter.
   */
  self.resetRecursion = function () {
    self.recursionCounter = 0
  }

  /**
   * Check if it's the current action.
   *
   * @param {string} action The action name.
   * @return {boolean} Returns TRUE if it's the current action, FALSE if not.
   */
  self.isAction = function (action) {
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_START, 'sweKvgStationsHelper.isAction')
    let roots = self.getElements('.' + sweKvgStationsDebugger.getExtClass())

    for (let i = 0; i < roots.length; i += 1) {
      let root = roots[i]

      if (!root.hasAttribute('data-action')) continue

      let realAction = root.getAttribute('data-action')
      sweKvgStationsDebugger.debug('Action ' + realAction.toLowerCase() + ' === ' + action.toLowerCase(), sweKvgStationsDebugger.LEVEL_DEBUG)

      if (realAction.toLowerCase() !== action.toLowerCase()) continue

      sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
      return true
    }

    sweKvgStationsDebugger.debug('This isn\'t an action of this extension!', sweKvgStationsDebugger.LEVEL_WARNING)
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
    return false
  }

  /**
   * Check is the current action is one of the given actions.
   *
   * @param {string[]} actions The action names.
   * @return {boolean} Returns TRUE if the current action is one of the given actions, FALSE if not.
   */
  self.isOneOfActions = function (actions) {
    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_START, 'sweKvgStationsHelper.isOneOfActions')
    for (let i = 0; i < actions.length; i += 1) {
      if (!self.isAction(actions[i])) continue
      sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
      return true
    }

    sweKvgStationsDebugger.debuggingFunc(sweKvgStationsDebugger.DEBUG_FUNC_END)
    return false
  }

  /**
   * Set up module sweKvgStationsHelper.
   */
  self.setup = function () {
    _check()
    _setAllHtmlTagNames()

    return self
  }

})(window.sweKvgStationsHelper)
