/**
 * Navigation component.
 *
 * @author Htmlstream
 * @version 1.0
 * @requires HSScrollBar component (hs.scrollbar.js v1.0.0)
 *
 */
;(function ($) {
  'use strict';

  $.HSCore.components.HSNavigation = {

    /**
     * Base configuration of the component.
     *
     * @private
     */
    _baseConfig: {
      navigationOverlayClasses: '',
      navigationInitClasses: '',
      navigationInitBodyClasses: '',
      navigationPosition: 'right',
      activeClass: 'u-main-nav--overlay-opened',
      navigationBreakpoint: 768,
      breakpointsMap: {
        'sm': 576,
        'md': 768,
        'lg': 992,
        'xl': 1200
      },
      afterOpen: function(){},
      afterClose: function(){}
    },

    /**
     * Collection of all initialized items on the page.
     *
     * @private
     */
    _pageCollection: $(),

    /**
     * Initializtion of the navigation.
     *
     * @param {jQuery} collection
     * @param {Object} config
     *
     * @public
     * @return {jQuery}
     */
    init: function( collection, config ) {

      var _self = this,
          $w = $(window);

      if(!collection || !collection.length) return $();

      config = config && $.isPlainObject(config) ? config : {};

      $w.on('resize.HSNavigation', function(e){

        if(_self.resizeTimeoutId) clearTimeout(_self.resizeTimeoutId);

        _self.resizeTimeoutId = setTimeout(function(){

          _self._pageCollection.each(function(i, el){

            var $this = $(el),
                HSNavigation = $this.data('HSNavigation');

            if($w.width() > HSNavigation.config.breakpointsMap[HSNavigation.config.navigationBreakpoint] && HSNavigation.isInitialized() ) {

              HSNavigation.destroy();

            }
            else if($w.width() <= HSNavigation.config.breakpointsMap[HSNavigation.config.navigationBreakpoint] && !HSNavigation.isInitialized()) {
              HSNavigation.init();
            }

          });

        }, 50);

      });


      collection.each(function(i, el){

        var $this = $(el),
            itemConfig = $.extend(true, {}, _self._baseConfig, config, $this.data());

        if( $this.data('HSNavigation') ) return;

        $this.data('HSNavigation', _self._factoryMethod( $this, itemConfig ));

        _self._pageCollection = _self._pageCollection.add( $this );

      });


      _self._pageCollection.each(function(i, el){

          var $this = $(el),
              HSNavigation = $this.data('HSNavigation');

          if($w.width() > HSNavigation.config.breakpointsMap[HSNavigation.config.navigationBreakpoint] ) {

            HSNavigation.destroy();

          }
          else if($w.width() <= HSNavigation.config.breakpointsMap[HSNavigation.config.navigationBreakpoint] ) {
            HSNavigation.init();
          }
      });

      return collection;

    },

    /**
     * Returns certain object relative to class name.
     *
     * @param {jQuery} element
     * @param {Object} config
     *
     * @private
     * @return {HSNavigationOverlay|HSNavigationPush}
     */
    _factoryMethod: function(element, config) {

      if( element.filter('[class*="u-main-nav--overlay"]').length ) {
        return new HSNavigationOverlay(element, config);
      }
      else if ( element.filter('[class*="u-main-nav--push"]').length ) {
       return new HSNavigationPush(element, config);
      }

    }

  };

  /**
   * Abstract class for all HSNavigation* objects.
   *
   * @param {jQuery} element
   * @param {Object} config
   *
   * @return {Boolean}
   */
  function HSNavigationAbstract(element, config) {

    /**
     * Contains current jQuery object.
     *
     * @public
     */
    this.element = element;

    /**
     * Contains body jQuery object.
     *
     * @public
     */
    this.body = $('body');

    /**
     * Contains configuration.
     *
     * @public
     */
    this.config = config;

    /**
     * Reinitialization of the HSNavigation* object.
     *
     * @public
     */
    this.reinit = function() {

      this.destroy().init();

    }
  };

  /**
   * HSNavigationOverlay.
   *
   * @param {jQuery} element
   * @param {Object} config
   *
   * @constructor
   */
  function HSNavigationOverlay(element, config) {

    var _self = this;

    // extends some functionality from abstract class
    HSNavigationAbstract.call(this, element, config);

    Object.defineProperties(this, {

      overlayClasses: {
        get: function() {
          return 'u-main-nav__overlay ' + _self.config.navigationOverlayClasses
        }
      },

      bodyClasses: {
        get: function() {
          return 'u-main-nav--overlay-' + _self.config.navigationPosition
        }
      },

      isOpened: {
        get: function() {
          return _self.body.hasClass( _self.config.activeClass );
        }
      }

    });

  };


  /**
   * Initialization of the instance.
   *
   * @public
   */
  HSNavigationOverlay.prototype.init = function() {

    var _self = this;

    /**
     * Contains overlay object.
     *
     * @public
     */
    this.overlay = $('<div></div>', {
      class: _self.overlayClasses
    });

    if( $.HSCore.components.HSScrollBar ) {

      setTimeout(function(){
        $.HSCore.components.HSScrollBar.init( _self.element.find( '.u-main-nav__list-wrapper' ) );
      }, 10);

    }

    this.toggler = $('[data-target="#'+ this.element.attr('id') +'"]');

    if(this.toggler && this.toggler.length) this.toggler.css('display', 'block');

    this.body.addClass( this.bodyClasses );
    this.element
        .addClass('u-main-nav--overlay')
        .append(this.overlay);

    setTimeout(function(){
      _self.element.addClass( _self.config.navigationInitClasses );
      _self.body.addClass( _self.config.navigationInitBodyClasses );

      _self.transitionDuration = parseFloat( getComputedStyle(_self.element.get(0)).transitionDuration, 10 );


      if(_self.transitionDuration > 0) {

        _self.element.on("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend", function(e){

          if(_self.isOpened && (e.originalEvent.propertyName == 'right' || e.originalEvent.propertyName == 'left')) {
            _self.config.afterOpen.call(_self.element, _self.overlay);
          }
          else if(!_self.isOpened && (e.originalEvent.propertyName == 'right' || e.originalEvent.propertyName == 'left')) {
            _self.config.afterClose.call(_self.element, _self.overlay);
          }

          e.stopPropagation();
          e.preventDefault();

        });

      }

    },50);

    this._bindEvents();


    this.isInit = true;

  };


  /**
   * Destroys the instance.
   *
   * @public
   */
  HSNavigationOverlay.prototype.destroy = function() {

    var _self = this;

    if(this.overlay) this.overlay.remove();

    if(this.toggler && this.toggler.length) this.toggler.hide();

    if( $.HSCore.components.HSScrollBar ) {

      setTimeout(function(){
        $.HSCore.components.HSScrollBar.destroy( _self.element.find( '.u-main-nav__list-wrapper' ) );
      }, 10);

    }

    setTimeout(function(){
      if(_self.transitionDuration && _self.transitionDuration > 0) {
        _self.element.off("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend");
      }
    },50);

    this.body.removeClass( this.bodyClasses );
    this.element
        .removeClass('u-main-nav--overlay')
        .removeClass(this.config.navigationInitClasses);

    this.body.removeClass( this.bodyClasses ).removeClass(this.config.navigationInitBodyClasses);

    this._unbindEvents();

    this.isInit = false;

  };

  /**
   * Binds necessary events.
   *
   * @private
   */
  HSNavigationOverlay.prototype._bindEvents = function() {

    var _self = this;

    if(this.toggler && this.toggler.length) {
      this.toggler.on('click.HSNavigation', function(e){

        if(_self.isOpened) {
          _self.close();
        }
        else {
          _self.open();
        }

        e.preventDefault();

      });
    }

    this.overlay.on('click.HSNavigation', function(e){
      _self.close();
    });

    $(document).on('keyup.HSNavigation', function(e){
      if(e.keyCode == 27) {
        _self.close();
      }
    });

  };

  /**
   * Unbinds necessary events.
   *
   * @private
   */
  HSNavigationOverlay.prototype._unbindEvents = function() {

    if(this.toggler && this.toggler.length) {
      this.toggler.off('click.HSNavigation');
    }

    if(this.overlay && this.overlay.length) {
      this.overlay.off('click.HSNavigation');
    }

    $(document).off('keyup.HSNavigation');

  };


  /**
   * Shows the navigation.
   *
   * @public
   */
  HSNavigationOverlay.prototype.open = function() {

    this.body.addClass( this.config.activeClass );

    if(this.transitionDuration !== undefined && this.transitionDuration == 0) {
      this.config.afterOpen.call(this.element, this.overlay);
    }

  };

  /**
   * Hides the navigation.
   *
   * @public
   */
  HSNavigationOverlay.prototype.close = function() {

    var hamburgers = this.toggler && this.toggler.length ? this.toggler.find('.is-active') : $();

    if(hamburgers.length) hamburgers.removeClass('is-active');

    this.body.removeClass( this.config.activeClass );

    if(this.transitionDuration !== undefined && this.transitionDuration == 0) {
      this.config.afterClose.call(this.element, this.overlay);
    }

  };

  /**
   * Returns true if the navigation has been initialized.
   *
   * @public
   * @return {Boolean}
   */
  HSNavigationOverlay.prototype.isInitialized = function() {

    return this.isInit;

  };

  /**
   * HSNavigationPush.
   *
   * @param {jQuery} element
   * @param {Object} config
   *
   * @constructor
   */
  function HSNavigationPush(element, config) {

    var _self = this;

    // extends some functionality from abstract class
    HSNavigationAbstract.call(this, element, config);

    Object.defineProperties(this, {

      overlayClasses: {
        get: function() {
          return 'u-main-nav__overlay ' + _self.config.navigationOverlayClasses
        }
      },

      bodyClasses: {
        get: function() {
          return 'u-main-nav--push-' + _self.config.navigationPosition
        }
      },

      isOpened: {
        get: function() {
          return _self.body.hasClass( _self.config.activeClass );
        }
      }

    });

    // this.init();

  };


  /**
   * Initialization of the instance.
   *
   * @public
   */
  HSNavigationPush.prototype.init = function() {

    var _self = this;

    /**
     * Contains overlay object.
     *
     * @public
     */
    this.overlay = $('<div></div>', {
      class: _self.overlayClasses
    });

    if( $.HSCore.components.HSScrollBar ) {

      setTimeout(function(){
        $.HSCore.components.HSScrollBar.init( _self.element.find( '.u-main-nav__list-wrapper' ) );
      }, 10);

    }

    this.toggler = $('[data-target="#'+ this.element.attr('id') +'"]');

    if(this.toggler && this.toggler.length) this.toggler.css('display', 'block');

    this.body.addClass( this.bodyClasses );
    this.element
        .addClass('u-main-nav--push')
        .append(this.overlay);

    setTimeout(function(){
      _self.element.addClass( _self.config.navigationInitClasses );
      _self.body.addClass( _self.config.navigationInitBodyClasses );

      _self.transitionDuration = parseFloat( getComputedStyle(_self.element.get(0)).transitionDuration, 10 );


      if(_self.transitionDuration > 0) {

        _self.element.on("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend", function(e){

          if(_self.isOpened && (e.originalEvent.propertyName == 'right' || e.originalEvent.propertyName == 'left')) {
            _self.config.afterOpen.call(_self.element, _self.overlay);
          }
          else if(!_self.isOpened && (e.originalEvent.propertyName == 'right' || e.originalEvent.propertyName == 'left')) {
            _self.config.afterClose.call(_self.element, _self.overlay);
          }

          e.stopPropagation();
          e.preventDefault();

        });

      }

    },50);

    this._bindEvents();

    this.isInit = true;

  };


  /**
   * Destroys the instance.
   *
   * @public
   */
  HSNavigationPush.prototype.destroy = function() {

    var _self = this;

    if(this.overlay) this.overlay.remove();

    if(this.toggler && this.toggler.length) this.toggler.hide();

    if( $.HSCore.components.HSScrollBar ) {

      setTimeout(function(){
        $.HSCore.components.HSScrollBar.destroy( _self.element.find( '.u-main-nav__list-wrapper' ) );
      }, 10);

    }

    setTimeout(function(){
      if(_self.transitionDuration && _self.transitionDuration > 0) {
        _self.element.off("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend");
      }
    },50);

    this.body.removeClass( this.bodyClasses ).removeClass(this.config.navigationInitBodyClasses);
    this.element
        .removeClass('u-main-nav--push')
        .removeClass(this.config.navigationInitClasses);

    this._unbindEvents();

    this.isInit = false;

  };

  /**
   * Binds necessary events.
   *
   * @private
   */
  HSNavigationPush.prototype._bindEvents = function() {

    var _self = this;

    if(this.toggler && this.toggler.length) {
      this.toggler.on('click.HSNavigation', function(e){

        if(_self.isOpened) {
          _self.close();
        }
        else {
          _self.open();
        }

        e.preventDefault();

      });
    }

    this.overlay.on('click.HSNavigation', function(e){
      _self.close();
    });

    $(document).on('keyup.HSNavigation', function(e){
      if(e.keyCode == 27) {
        _self.close();
      }
    });

  };

  /**
   * Unbinds necessary events.
   *
   * @private
   */
  HSNavigationPush.prototype._unbindEvents = function() {

    if(this.toggler && this.toggler.length) {
      this.toggler.off('click.HSNavigation');
    }

    if(this.overlay && this.overlay.length) {
      this.overlay.off('click.HSNavigation');
    }

    $(document).off('keyup.HSNavigation');

  };


  /**
   * Shows the navigation.
   *
   * @public
   */
  HSNavigationPush.prototype.open = function() {

    this.body.addClass( this.config.activeClass );

    if(this.transitionDuration !== undefined && this.transitionDuration == 0) {
      this.config.afterOpen.call(this.element, this.overlay);
    }

  };

  /**
   * Hides the navigation.
   *
   * @public
   */
  HSNavigationPush.prototype.close = function() {

    var hamburgers = this.toggler && this.toggler.length ? this.toggler.find('.is-active') : $();

    if(hamburgers.length) hamburgers.removeClass('is-active');

    this.body.removeClass( this.config.activeClass );

    if(this.transitionDuration !== undefined && this.transitionDuration == 0) {
      this.config.afterClose.call(this.element, this.overlay);
    }

  };

  /**
   * Returns true if the navigation has been initialized.
   *
   * @public
   * @return {Boolean}
   */
  HSNavigationPush.prototype.isInitialized = function() {

    return this.isInit;

  };


})(jQuery);