import * as React from 'react';
import { generateHandlers } from './utils';
import { events, EventEmitTypes } from '../utils/events';
import { emitHotArea } from '../eventListener';
import { HotAreaEventListenerTypes } from './types';

const hotAreaIntersectionObserver = new window.IntersectionObserver(entries => {
  entries.forEach(({ target, isIntersecting }) => {
    const key = parseInt(target.getAttribute('data-key'), 10);
    events.emit(EventEmitTypes.HOT_AREA_INTERSECTING_CHANGE, isIntersecting, key);
  });
});

const LONG_PRESS_TIMEOUT = 1000;
const DOUBLE_CLICK_TIMEOUT = 200;
const INTERSECTION_TIMEOUT = 500;

export class HotAreaActionHandler extends React.PureComponent {
  handlerParams = {};

  handlers = {};

  containerRef = null;

  longPressTimer = null;

  preventClick = false;

  doubleClickTimer = null;

  intersectionTimer = null;

  constructor(props) {
    super(props);

    this.handlerParams = { global: props.global, meta: props.meta };
    this.updateHandlersWithParams(props.hotArea.actions);
  }

  componentDidMount() {
    this.observeIntersection();

    events.on(EventEmitTypes.HOT_AREA_INTERSECTING_CHANGE, this.onHotAreaIntersectionChange);

    if (this.handlers.onInit) {
      this.handlers.onInit();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.global !== this.props.global || nextProps.meta !== this.props.meta) {
      this.updateHandlerParams(nextProps.meta, nextProps.global);
    }

    if (nextProps.hotArea.actions !== this.props.hotArea.actions) {
      this.updateHandlersWithParams(nextProps.hotArea.actions);
    }
  }

  componentWillUnmount() {
    this.unobserveIntersection();

    events.cancel(EventEmitTypes.HOT_AREA_INTERSECTING_CHANGE, this.onHotAreaIntersectionChange);

    if (this.longPressTimer) {
      window.clearTimeout(this.longPressTimer);
    }
  }

  observeIntersection = () => hotAreaIntersectionObserver.observe(this.containerRef);

  unobserveIntersection = () => hotAreaIntersectionObserver.unobserve(this.containerRef);

  onHotAreaIntersectionChange = (isIntersecting, key) => {
    if (key !== this.props.hotArea.key) {
      return;
    }

    if (this.intersectionTimer) {
      clearTimeout(this.intersectionTimer);
      this.intersectionTimer = null;
    }

    const callback = isIntersecting ? this.onHotAreaEnterView : this.onHotAreaLeaveView;

    this.intersectionTimer = window.setTimeout(() => {
      callback();
      clearTimeout(this.intersectionTimer);
      this.intersectionTimer = null;
    }, INTERSECTION_TIMEOUT);
  };

  onHotAreaEnterView = () => {
    if (this.handlers.onEnterView) {
      this.handlers.onEnterView();
    }
  };

  onHotAreaLeaveView = () => {
    if (this.handlers.onLeaveView) {
      this.handlers.onLeaveView();
    }
  };

  onHotAreaPress = () => {
    if (!this.handlers.onLongPress) {
      return;
    }

    this.longPressTimer = window.setTimeout(this.handlers.onLongPress, LONG_PRESS_TIMEOUT);
  };

  onHotAreaPressRelease = () => {
    if (this.longPressTimer) {
      window.clearTimeout(this.longPressTimer);
    }
    this.longPressTimer = null;
  };

  onSingleClick = () => {
    this.doubleClickTimer = window.setTimeout(() => {
      if (!this.preventClick) {
        try {
          this.handlers.onClick();
        } catch (e) {
          console.error('Exec onClick action error:', e);
        }
      }
    }, DOUBLE_CLICK_TIMEOUT);
  };

  onDoubleClick = () => {
    if (this.doubleClickTimer) {
      clearTimeout(this.doubleClickTimer);
      this.doubleClickTimer = null;
    }

    this.preventClick = true;
    try {
      this.handlers.onDoubleClick();
    } catch (e) {
      console.error('Exec onClick action error:', e);
    }

    setTimeout(() => (this.preventClick = false), DOUBLE_CLICK_TIMEOUT);
  };

  emitHotAreaEvent = type => {
    const {
      props: { hotArea, instance },
      handlerParams: { global, meta },
    } = this;
    return emitHotArea(type, hotArea, instance, meta, global);
  };

  withEmitHotAreaEvent = (type, handler) => () => {
    this.emitHotAreaEvent(type);
    if (handler) {
      return handler(this.handlerParams);
    }
  };

  updateHandlerParams = (meta, global) => {
    this.handlerParams.meta = meta;
    this.handlerParams.global = global;
  };

  updateHandlersWithParams = actions => {
    const {
      onInit,
      onClick,
      onMouseEnter,
      onMouseLeave,
      onDoubleClick,
      onLongPress,
      onEnterView,
      onLeaveView,
    } = generateHandlers(actions);
    const { withEmitHotAreaEvent, handlers } = this;

    handlers.onInit = withEmitHotAreaEvent(HotAreaEventListenerTypes.INIT, onInit);
    handlers.onClick = withEmitHotAreaEvent(HotAreaEventListenerTypes.CLICK, onClick);
    handlers.onMouseEnter = withEmitHotAreaEvent(
      HotAreaEventListenerTypes.MOUSE_ENTER,
      onMouseEnter
    );
    handlers.onMouseLeave = withEmitHotAreaEvent(
      HotAreaEventListenerTypes.MOUSE_LEAVE,
      onMouseLeave
    );
    handlers.onDoubleClick = withEmitHotAreaEvent(
      HotAreaEventListenerTypes.DOUBLE_CLICK,
      onDoubleClick
    );
    handlers.onLongPress = withEmitHotAreaEvent(HotAreaEventListenerTypes.LONG_PRESS, onLongPress);
    handlers.onEnterView = withEmitHotAreaEvent(HotAreaEventListenerTypes.ENTER_VIEW, onEnterView);
    handlers.onLeaveView = withEmitHotAreaEvent(HotAreaEventListenerTypes.LEAVE_VIEW, onLeaveView);
  };

  setNode = node => (this.containerRef = node);

  render() {
    const { children, style = {} } = this.props;
    const { onMouseLeave, onMouseEnter } = this.handlers;

    return (
      <div
        className="__impage-hot-area-actions-proxy"
        ref={this.setNode}
        data-key={this.props.hotArea.key}
        onClick={this.onSingleClick}
        onDoubleClick={this.onDoubleClick}
        onMouseEnter={onMouseEnter}
        onMouseLeave={onMouseLeave}
        onTouchStart={this.onHotAreaPress}
        onTouchEnd={this.onHotAreaPressRelease}
        onMouseDown={this.onHotAreaPress}
        onMouseUp={this.onHotAreaPressRelease}
        style={style}
      >
        {children}
      </div>
    );
  }
}
