/**
 * Copyright (c) 2015 NAVER Corp.
 * egjs projects are licensed under the MIT license
 */

import State from "./State";
import { STATE_TYPE, EVENTS, DIRECTION } from "../consts";
import { FlickingContext } from "../types";

class HoldingState extends State {
  public readonly type = STATE_TYPE.HOLDING;
  public readonly holding = true;
  public readonly playing = true;

  private releaseEvent: any = null;

  public onChange(e: any, context: FlickingContext): void {
    const { flicking, triggerEvent, transitTo } = context;

    const offset = flicking.options.horizontal
      ? e.inputEvent.offsetX
      : e.inputEvent.offsetY;
    this.direction = offset < 0
      ? DIRECTION.NEXT
      : DIRECTION.PREV;

    triggerEvent(EVENTS.MOVE_START, e, true)
      .onSuccess(() => {
        // Trigger DraggingState's onChange, to trigger "move" event immediately
        transitTo(STATE_TYPE.DRAGGING)
          .onChange(e, context);
      })
      .onStopped(() => {
        transitTo(STATE_TYPE.DISABLED);
      });
  }

  public onRelease(e: any, context: FlickingContext): void {
    const { viewport, triggerEvent, transitTo } = context;

    triggerEvent(EVENTS.HOLD_END, e, true);

    if (e.delta.flick !== 0) {
      // Sometimes "release" event on axes triggered before "change" event
      // Especially if user flicked panel fast in really short amount of time
      // if delta is not zero, that means above case happened.

      // Event flow should be HOLD_START -> MOVE_START -> MOVE -> HOLD_END
      // At least one move event should be included between holdStart and holdEnd
      e.setTo({ flick: viewport.getCameraPosition() }, 0);
      transitTo(STATE_TYPE.IDLE);
      return;
    }

    // Can't handle select event here,
    // As "finish" axes event happens
    this.releaseEvent = e;
  }

  public onFinish(e: any, { viewport, triggerEvent, transitTo }: FlickingContext): void {
    // Should transite to IDLE state before select event
    // As user expects hold is already finished
    transitTo(STATE_TYPE.IDLE);

    if (!this.releaseEvent) {
      return;
    }

    // Handle release event here
    // To prevent finish event called twice
    const releaseEvent = this.releaseEvent;

    // Static click
    const srcEvent = releaseEvent.inputEvent.srcEvent;

    let clickedElement: HTMLElement;
    if (srcEvent.type === "touchend") {
      const touchEvent = srcEvent as TouchEvent;
      const touch = touchEvent.changedTouches[0];
      clickedElement = document.elementFromPoint(touch.clientX, touch.clientY) as HTMLElement;
    } else {
      clickedElement = srcEvent.target;
    }
    const clickedPanel = viewport.panelManager.findPanelOf(clickedElement);
    const cameraPosition = viewport.getCameraPosition();

    if (clickedPanel) {
      const clickedPanelPosition = clickedPanel.getPosition();
      const direction = clickedPanelPosition > cameraPosition
        ? DIRECTION.NEXT
        : clickedPanelPosition < cameraPosition
          ? DIRECTION.PREV
          : null;

      // Don't provide axes event, to use axes instance instead
      triggerEvent(EVENTS.SELECT, null, true, {
        direction, // Direction to the clicked panel
        index: clickedPanel.getIndex(),
        panel: clickedPanel,
      });
    }
  }
}

export default HoldingState;
