import * as classNames from 'classnames';
import * as React from 'react';
import { HTMLProps, PureComponent } from 'react';
import * as ReactDOM from 'react-dom';
import { ComponentProps } from '../../types';
import { addClassName, getScrollOffset, removeClassName } from '../../utils';

const WITH_FIXED_NAV_BAR = 'with-fixed-nav-bar';

export interface NavBarProps extends ComponentProps, HTMLProps<HTMLElement> {
  /**
   * Fix the navbar to the top of the screen
   */
  fixed?: boolean;
  /**
   * Hide the navbar when scrolling down, but display when scrolling up
   */
  shy?: boolean;
  /**
   * Remove NavBar shadow
   */
  noShadow?: boolean;
}

export interface NavBarState {
  hidden: boolean;
}

export class NavBar extends PureComponent<NavBarProps, NavBarState> {
  private previousScrollY: number;

  public constructor (props: NavBarProps) {
    super(props);

    this.previousScrollY = getScrollOffset().y;

    this.state = {
      hidden: false,
    };
  }

  public componentWillMount () {
    this.updateBodyClass(this.props);
    this.toggleShyListeners(this.props);
  }

  public componentWillUpdate (nextProps: NavBarProps) {
    if (this.props.shy !== nextProps.shy) {
      this.toggleShyListeners(nextProps);
    }

    if (this.props.fixed !== nextProps.fixed || this.props.shy !== nextProps.shy) {
      this.updateBodyClass(nextProps);
    }
  }

  public componentWillUnmount () {
    window.removeEventListener('scroll', this.hideOrShowNavBar);
    window.removeEventListener('resize', this.hideOrShowNavBar);
  }

  public render () {
    const {
      children,
      className,
      fixed,
      shy,
      noShadow,
      component: Component = 'div',
      ...remainingProps,
    } = this.props;

    const {
      hidden,
    } = this.state;

    const myClassNames = [
      'nav-bar',
      fixed || shy ? 'fixed' : null,
      shy ? 'shy' : null,
      hidden ? 'hidden' : null,
      noShadow ? 'no-shadow' : null,
      className
    ];

    return (
      <Component {...remainingProps} className={classNames(myClassNames)}>
        {children}
      </Component>
    );
  }

  private updateBodyClass (props: NavBarProps) {
    const { fixed, shy } = props;

    if (fixed || shy) {
      addClassName(document.body, WITH_FIXED_NAV_BAR);
    } else {
      removeClassName(document.body, WITH_FIXED_NAV_BAR)
    }
  }

  private toggleShyListeners (props: NavBarProps) {
    const { shy } = props;

    if (shy) {
      window.addEventListener('scroll', this.hideOrShowNavBar);
      window.addEventListener('resize', this.hideOrShowNavBar);
    } else {
      window.removeEventListener('scroll', this.hideOrShowNavBar);
      window.removeEventListener('resize', this.hideOrShowNavBar);
    }
  }

  private hideOrShowNavBar = () => {
    const { y } = getScrollOffset();
    const element = ReactDOM.findDOMNode(this);

    if (element) {
      const { height } = element.getBoundingClientRect();

      if (y > this.previousScrollY && y > height) {
        this.setState({
          hidden: true,
        });
      } else if (y < this.previousScrollY) {
        this.setState({
          hidden: false,
        });
      }
    }

    this.previousScrollY = y;
  }
}

export default NavBar;
