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

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

import { VirtualGridList } from "@enact/sandstone/VirtualList";
import Spotlight from "@enact/spotlight";

import { scaleH, scaleW } from "../../utils/helperMethods";
import AutoScrollArea, { POSITION } from "../AutoScrollArea/AutoScrollArea";
import css from "./TVirtualGridList.module.less";

export default function TVirtualGridList({
  defaultSpotlightId,
  restorePositionX,
  dataSize,
  direction = "vertical",
  className,
  verticalScrollbar = "hidden",
  horizontalScrollbar = "hidden",
  noScrollByWheel = true,
  scrollMode = "translate",
  itemWidth,
  itemHeight,
  spacing,
  logoDisplay,
  cbScrollTo,
  spotlightId,
  renderItem,
  onScrollStart,
  onScrollStop,
  autoScroll = direction === "horizontal",
  ...rest
}) {
  const cursorVisible = useSelector(
    (state) => state.common.appStatus.cursorVisible
  );
  const webOSVersion = useSelector(
    (state) => state.common.appStatus.webOSVersion
  );

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

  const scrollToRef = useRef(null);
  const defaultFocusTargetRef = useRef(null);
  const scrollHorizontalPos = useRef(0);
  const scrollVerticalPos = useRef(0);
  const visibleIndexRef = useRef({ firstVisibleIndex: 0, lastVisibleIndex: 0 });

  const gridlistParentRef = useRef(null);

  const handleWheel = useCallback((ev) => {
    ev.stopPropagation();
    ev.preventDefault();
    const targetIndex =
      ev.deltaY > 0
        ? visibleIndexRef.current.firstVisibleIndex + 1
        : visibleIndexRef.current.firstVisibleIndex - 1;
    scrollToRef.current &&
      scrollToRef.current({ index: targetIndex, animate: true });
  }, []);

  useEffect(() => {
    const listRef = gridlistParentRef.current.childNodes[0];
    if (
      spotlightId &&
      defaultSpotlightId &&
      defaultSpotlightId.indexOf(spotlightId) === 0
    ) {
      let matched = defaultSpotlightId.match(/\d+$/); //find index
      if (matched) {
        defaultFocusTargetRef.current = {
          spotlightId: defaultSpotlightId,
          index: Number(matched[0]),
          left: restorePositionX,
        };
        if (restorePositionX) {
          scrollToRef.current({
            position: { x: Math.floor(restorePositionX), y: 0 },
            animate: false,
            focus: false,
          });
        } else {
          scrollToRef.current({
            index: Number(matched[0]),
            animate: false,
            focus: false,
          });
        }
      }
    }
    if (direction === "vertical") {
      listRef.addEventListener("wheel", handleWheel, true);
    }
    return () => {
      scrollToRef.current = null;
      if (direction === "vertical") {
        listRef.removeEventListener("wheel", handleWheel);
      }
    };
  }, []);

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

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

  const _onScrollStop = useCallback(
    (e) => {
      if (onScrollStop) {
        onScrollStop(e);
      }
      isScrolling.current = false;
      if (defaultFocusTargetRef.current) {
        if (
          (defaultFocusTargetRef.current.index >=
            e.moreInfo.firstVisibleIndex &&
            defaultFocusTargetRef.current.index <=
              e.moreInfo.lastVisibleIndex) ||
          (defaultFocusTargetRef.left >= 0 &&
            defaultFocusTargetRef.left > e.scrollLeft)
        ) {
          Spotlight.focus(defaultFocusTargetRef.current.spotlightId);
          defaultFocusTargetRef.current = null;
        }
      }
      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;
      visibleIndexRef.current = e.moreInfo;
    },
    [onScrollStop]
  );

  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]);

  const isLowerBrowser = useMemo(() => {
    if (typeof window === "object") {
      // tv
      if (window.PalmSystem) {
        return Number(webOSVersion) < 5;
      }

      // local
      const userAgent = window.navigator.userAgent;
      const chromeMatch = userAgent.match(/Chrome\/([0-9]+)\./);
      if (chromeMatch) {
        const n = parseInt(chromeMatch[1], 10);
        return n < 68;
      }
    }

    return false;
  }, [webOSVersion]);
  return (
    <div
      className={classNames(
        className ? className : null,
        css.tVirtualGridListContainer
      )}
      ref={gridlistParentRef}
    >
      <VirtualGridList
        className={classNames(
          css.tVirtualGridList,
          isLowerBrowser && css.lowVersionBr
        )}
        onScrollStart={_onScrollStart}
        onScrollStop={_onScrollStop}
        direction={direction}
        dataSize={dataSize}
        itemRenderer={renderItem}
        verticalScrollbar={verticalScrollbar}
        horizontalScrollbar={horizontalScrollbar}
        noScrollByWheel={noScrollByWheel}
        scrollMode={scrollMode}
        itemSize={{
          minWidth: scaleW(itemWidth),
          minHeight: scaleH(itemHeight),
        }}
        spacing={scaleW(spacing)}
        cbScrollTo={_cbScrollTo}
        spotlightId={spotlightId}
        {...rest}
      />
      {cursorVisible &&
        autoScroll &&
        relevantPositions.map((pos) => (
          <AutoScrollArea
            key={pos}
            position={POSITION[pos]}
            autoScroll={autoScroll}
            scrollHorizontalPos={scrollHorizontalPos}
            scrollVerticalPos={scrollVerticalPos}
            scrollToRef={scrollToRef}
            scrollPosition={scrollPosition}
            direction={direction}
          />
        ))}
    </div>
  );
}
