import React, {
  useCallback,
  useEffect,
  useRef,
  useState,
  useMemo,
} from "react";

import classNames from "classnames";
import { useSelector } from "react-redux";

import { off, on } from "@enact/core/dispatcher";
import { Job } from "@enact/core/util";
import Scroller from "@enact/sandstone/Scroller";

import AutoScrollArea, { POSITION } from "../AutoScrollArea/AutoScrollArea";
import css from "./TScroller.module.less";

/**
 * onScroll* event can't use Callback dependency
 */
export default function TScroller({
  className,
  children,
  verticalScrollbar = "hidden",
  focusableScrollbar = false,
  direction = "vertical",
  horizontalScrollbar = "hidden",
  scrollMode,
  onScrollStart,
  onScrollStop,
  onScroll,
  noScrollByWheel = false,
  cbScrollTo,
  autoScroll = direction === "horizontal",
  setScrollVerticalPos,
  setCheckScrollPosition,
  ...rest
}) {
  const { cursorVisible } = useSelector((state) => state.common.appStatus);

  const isScrolling = useRef(false);
  const scrollPosition = useRef("top");

  const scrollToRef = useRef(null);
  const scrollHorizontalPos = useRef(0);
  const scrollVerticalPos = useRef(0);

  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);

    return () => setIsMounted(false);
  }, []);

  const _onScrollStart = useCallback(
    (e) => {
      if (onScrollStart) {
        onScrollStart(e);
      }

      isScrolling.current = true;
    },
    [onScrollStart]
  );

  const _onScrollStop = useCallback(
    (e) => {
      if (onScrollStop) {
        onScrollStop(e);
      }

      isScrolling.current = false;

      if (e.reachedEdgeInfo) {
        if (e.reachedEdgeInfo.top) {
          scrollPosition.current = "top";
        } else if (e.reachedEdgeInfo.bottom) {
          scrollPosition.current = "bottom";
        } else if (e.reachedEdgeInfo.left) {
          scrollPosition.current = "left";
        } else if (e.reachedEdgeInfo.right) {
          scrollPosition.current = "right";
        } else {
          scrollPosition.current = "middle";
        }
      } else {
        scrollPosition.current = "middle";
      }

      scrollHorizontalPos.current = e.scrollLeft;
      scrollVerticalPos.current = e.scrollTop;

      if (setScrollVerticalPos) {
        setScrollVerticalPos(scrollVerticalPos.current);
      }
      if (setCheckScrollPosition) {
        setCheckScrollPosition(scrollPosition.current);
      }
    },
    [onScrollStop]
  );

  const _onScroll = useCallback(
    (ev) => {
      if (onScroll) {
        onScroll(ev);
      }
    },
    [onScroll]
  );

  const _cbScrollTo = useCallback(
    (ref) => {
      if (cbScrollTo) {
        cbScrollTo(ref);
      }

      scrollToRef.current = ref;
    },
    [cbScrollTo]
  );

  const relevantPositions = useMemo(() => {
    switch (direction) {
      case "horizontal":
        return ["left", "right"];
      case "vertical":
        return ["top", "bottom"];
      default:
        return [];
    }
  }, [direction]);

  return (
    <div
      className={classNames(
        className ? className : null,
        css.scrollerContainer
      )}
    >
      <Scroller
        {...rest}
        cbScrollTo={_cbScrollTo}
        onScrollStart={_onScrollStart}
        onScrollStop={_onScrollStop}
        onScroll={_onScroll}
        scrollMode={scrollMode || "translate"}
        focusableScrollbar={focusableScrollbar}
        className={classNames(
          isMounted && css.tScroller,
          noScrollByWheel && css.preventScroll
        )}
        direction={direction}
        horizontalScrollbar={horizontalScrollbar}
        verticalScrollbar={verticalScrollbar}
        overscrollEffectOn={{
          arrowKey: false,
          drag: false,
          pageKey: false,
          track: false,
          wheel: false,
        }}
        noScrollByWheel={noScrollByWheel}
        noScrollByDrag
      >
        {children}
      </Scroller>
      {cursorVisible &&
        autoScroll &&
        relevantPositions.map((pos) => (
          <AutoScrollArea
            key={pos}
            position={POSITION[pos]}
            autoScroll={autoScroll}
            scrollHorizontalPos={scrollHorizontalPos}
            scrollVerticalPos={scrollVerticalPos}
            scrollToRef={scrollToRef}
            scrollPosition={scrollPosition}
            direction={direction}
          />
        ))}
    </div>
  );
}
