var _excluded = ["vertical"];

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

/* eslint-disable no-return-assign */

/* eslint-disable no-plusplus */
import React, { forwardRef, memo, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import cls from 'clsx';
import { useSpring, animated } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
import SwiperItem from './SwiperItem';
import useRefs from '../hooks/use-refs';
import useRefState from '../hooks/use-ref-state';
import { bound } from '../utils/bound';
import { devWarning } from '../utils/dev-log';
import { noop } from '../utils';
import { getRect } from '../hooks/use-rect';
var PageIndicator = /*#__PURE__*/memo(function (_ref) {
  var vertical = _ref.vertical,
      props = _objectWithoutPropertiesLoose(_ref, _excluded);

  var _useContext = useContext(ConfigProviderContext),
      prefixCls = _useContext.prefixCls,
      createNamespace = _useContext.createNamespace;

  var _createNamespace = createNamespace('indicator', prefixCls),
      bem = _createNamespace[0];

  var dots = [];

  for (var i = 0; i < props.total; i++) {
    dots.push( /*#__PURE__*/React.createElement("div", {
      key: i,
      className: cls(bem('dot', {
        active: props.current === i
      }))
    }));
  }

  return /*#__PURE__*/React.createElement("div", {
    className: cls(props.className, bem({
      vertical: vertical
    })),
    style: props.style
  }, dots);
});
var Swiper = /*#__PURE__*/forwardRef(function (props, ref) {
  var _useContext2 = useContext(ConfigProviderContext),
      prefixCls = _useContext2.prefixCls,
      createNamespace = _useContext2.createNamespace;

  var _createNamespace2 = createNamespace('swiper', prefixCls),
      bem = _createNamespace2[0];

  var outerLoop = props.loop,
      autoplay = props.autoplay,
      vertical = props.vertical,
      duration = props.duration,
      autoHeight = props.autoHeight;

  var _useRefs = useRefs(),
      childrenRefs = _useRefs[0],
      setChildrenRefs = _useRefs[1];

  var _useState = useState(function () {
    return props.enabled;
  }),
      enabled = _useState[0],
      setEnabled = _useState[1];

  var trackRef = useRef(null);

  var _useState2 = useState(null),
      root = _useState2[0],
      setRoot = _useState2[1];

  var _useState3 = useState(props.initialSwipe),
      current = _useState3[0],
      setCurrent = _useState3[1];

  var _useRefState = useRefState(false),
      dragging = _useRefState[0],
      setDragging = _useRefState[1],
      draggingRef = _useRefState[2];

  var axis = useMemo(function () {
    return vertical ? 'y' : 'x';
  }, [vertical]);
  var slideRatio = props.slideSize / 100;
  var offsetRatio = props.trackOffset / 100;
  var computedStyle = useMemo(function () {
    var _extends2;

    return _extends((_extends2 = {}, _extends2["--" + prefixCls + "-swipe-slide-size"] = props.slideSize + "%", _extends2["--" + prefixCls + "-swipe-track-offset"] = props.trackOffset + "%", _extends2), props.style);
  }, [prefixCls, props.slideSize, props.trackOffset, props.style]);
  var trackStyle = useMemo(function () {
    if (!autoHeight) return {};
    var target = childrenRefs[current];
    if (target) return {
      height: target.getHeight() || 'auto'
    };
    return {}; // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoHeight, childrenRefs.length, current]);
  var axisDistance = useMemo(function () {
    if (!vertical) return 100;
    var rect = getRect(root);
    return rect.height;
  }, [vertical, root]);

  var _useMemo = useMemo(function () {
    var innerCount = 0;
    var innerValidChildren = React.Children.map(props.children, function (child) {
      if (! /*#__PURE__*/React.isValidElement(child)) return null;

      if (child.type !== SwiperItem) {
        devWarning('Swiper', 'The children of `Swiper` must be `Swiper.Item` components.');
        return null;
      }

      innerCount++;
      return child;
    });
    return {
      validChildren: innerValidChildren,
      count: innerCount
    };
  }, [props.children]),
      validChildren = _useMemo.validChildren,
      count = _useMemo.count;

  var loop = useMemo(function () {
    if (count <= 1) return false;
    return outerLoop;
  }, [count, outerLoop]);

  var getSlidePixels = function getSlidePixels() {
    var track = trackRef.current;
    if (!track) return 0;
    var trackPixels = vertical ? track.offsetHeight : track.offsetWidth;
    return trackPixels * props.slideSize / 100;
  };

  var boundIndex = function boundIndex(cur) {
    var min = 0;
    var max = count - 1;

    if (props.stuckAtBoundary) {
      min += (1 - slideRatio - offsetRatio) / slideRatio;
      max -= (1 - slideRatio - offsetRatio) / slideRatio;
    }

    return bound(cur, min, max);
  };

  var _useSpring = useSpring(function () {
    return {
      position: boundIndex(current) * 100,
      config: {
        tension: 200,
        friction: 30,
        duration: duration,
        // eslint-disable-next-line no-cond-assign
        easing: function easing(t) {
          return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
        }
      },
      onRest: function onRest() {
        if (draggingRef.current) return;
        var rawX = position.get();
        var totalWidth = 100 * count;
        var standardPosition = modulus(rawX, totalWidth);
        if (standardPosition === rawX) return;
        api.start({
          position: standardPosition,
          immediate: true
        });
      }
    };
  }, [count]),
      position = _useSpring[0].position,
      api = _useSpring[1];

  var bind = useDrag(function (state) {
    var _childrenRefs$current;

    var slidePixels = getSlidePixels();
    if (!slidePixels) return;

    if (!props.preventScroll && isScrollTarget(state.target, (_childrenRefs$current = childrenRefs[current]) == null ? void 0 : _childrenRefs$current.self)) {
      return;
    }

    var paramIndex = vertical ? 1 : 0;
    var offset = state.offset[paramIndex];
    var direction = state.direction[paramIndex];
    var velocity = state.velocity[paramIndex];
    setDragging(true);

    if (!state.last) {
      api.start({
        position: offset * 100 / slidePixels,
        immediate: true
      });
    } else {
      var index = Math.round((offset + Math.min(velocity * 2000, slidePixels) * direction) / slidePixels);
      swipeTo(index);
      window.setTimeout(function () {
        setDragging(false);
      });
    }
  }, {
    enabled: enabled,
    transform: function transform(_ref2) {
      var x = _ref2[0],
          y = _ref2[1];
      return [-x, -y];
    },
    from: function from() {
      var slidePixels = getSlidePixels();
      return [position.get() / 100 * slidePixels, position.get() / 100 * slidePixels];
    },
    bounds: function bounds() {
      if (loop) return {};
      var slidePixels = getSlidePixels();
      var lowerBound = boundIndex(0) * slidePixels;
      var upperBound = boundIndex(count - 1) * slidePixels;
      return vertical ? {
        top: lowerBound,
        bottom: upperBound
      } : {
        left: lowerBound,
        right: upperBound
      };
    },
    rubberband: props.rubberband,
    axis: axis,
    preventScroll: axis === 'x' ? props.preventScroll : true,
    pointer: {
      touch: true
    }
  });

  var renderIndicator = function renderIndicator() {
    if (props.indicator === undefined || props.indicator === true) {
      return /*#__PURE__*/React.createElement("div", {
        className: cls(bem('indicator', {
          vertical: vertical
        }))
      }, /*#__PURE__*/React.createElement(PageIndicator, _extends({}, props.indicatorProps, {
        vertical: vertical,
        total: count,
        current: current
      })));
    }

    if (typeof props.indicator === 'function') {
      return props.indicator(count, current);
    }

    return null;
  };

  var onClickCapture = function onClickCapture(e) {
    if (draggingRef.current) {
      e.stopPropagation();
    }
  };

  function swipeTo(index, immediate) {
    if (immediate === void 0) {
      immediate = false;
    }

    if (loop) {
      var i = modulus(index, count);
      setCurrent(i);
      props.onChange == null ? void 0 : props.onChange(i);
      api.start({
        position: index * 100,
        immediate: immediate
      });
    } else {
      var _i = bound(index, 0, count - 1);

      setCurrent(_i);
      props.onChange == null ? void 0 : props.onChange(_i);
      api.start({
        position: boundIndex(_i) * 100,
        immediate: immediate
      });
    }
  }

  var swipeNext = function swipeNext() {
    swipeTo(Math.round(position.get() / 100) + 1);
  };

  var swipePrev = function swipePrev() {
    swipeTo(Math.round(position.get() / 100) - 1);
  };

  useImperativeHandle(ref, function () {
    return {
      activeIndex: current,
      swipeTo: swipeTo,
      swipeNext: swipeNext,
      swipePrev: swipePrev,
      enable: function enable() {
        setEnabled(true);
      },
      disable: function disable() {
        setEnabled(false);
      }
    };
  });
  useEffect(function () {
    if (!autoplay || dragging) return noop;
    var autoplayInterval = typeof autoplay === 'boolean' ? 5000 : autoplay;
    var interval = window.setInterval(function () {
      swipeNext();
    }, autoplayInterval);
    return function () {
      window.clearInterval(interval);
    }; // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoplay, dragging, axisDistance]);

  if (count === 0) {
    devWarning('Swiper', '`Swiper` needs at least one child.');
  }

  return /*#__PURE__*/React.createElement("div", {
    ref: setRoot,
    className: cls(props.className, bem({
      vertical: vertical
    })),
    style: computedStyle
  }, /*#__PURE__*/React.createElement("div", _extends({
    ref: trackRef,
    className: cls(bem('track', {
      'allow-touch-move': props.touchable && enabled && props.preventScroll
    })),
    onClickCapture: onClickCapture
  }, props.touchable ? bind() : {}), /*#__PURE__*/React.createElement("div", {
    className: cls(bem('track-inner', {
      vertical: vertical
    })),
    style: trackStyle
  }, React.Children.map(validChildren, function (child, index) {
    var _ref3;

    return /*#__PURE__*/React.createElement(animated.div, {
      className: cls(bem('slide')),
      style: (_ref3 = {}, _ref3[axis] = position.to(function (pos) {
        var finalPosition = -pos + index * 100;
        var totalWidth = count * 100;
        var flagWidth = totalWidth / 2;
        finalPosition = modulus(finalPosition + flagWidth, totalWidth) - flagWidth;
        return finalPosition + "%";
      }), _ref3.left = "-" + index * 100 + "%", _ref3)
    }, /*#__PURE__*/React.cloneElement(child, {
      ref: setChildrenRefs(index)
    }));
  }))), renderIndicator());
}); // defaultProps defined if need

Swiper.defaultProps = {
  initialSwipe: 0,
  touchable: true,
  loop: true,
  enabled: true,
  rubberband: true,
  autoplay: false,
  slideSize: 100,
  trackOffset: 0,
  stuckAtBoundary: false,
  preventScroll: true
};
export default Swiper;

function modulus(value, division) {
  var remainder = value % division;
  return remainder < 0 ? remainder + division : remainder;
}

function isScrollTarget(element, parent) {
  if (!parent) return false;

  if (element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight) {
    return true;
  }

  if (element.parentElement && element.parentElement !== parent) {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    return isScrollTarget(element.parentElement, parent);
  }

  return false;
}