import React from 'react'
import {
  DOTS_TOTAL_ANIMATION_DURATION,
  OVERSHOOT_FACTOR,
  UNDERSHOOT_FACTOR,
  BOUNCY_CURVE,
  DOTS_SPACING_X,
  DOTS_BASE_Y_FACTOR,
  DOTS_JUMP_HEIGHT_FACTOR,
  DOTS_PADDING_FACTOR,
  DOTS_FADEOUT_KEYTIMES,
  DOTS_FADEOUT_VALUES,
  DOT1_COLOR_KEYTIMES,
  DOT2_COLOR_KEYTIMES,
  DOT3_COLOR_KEYTIMES,
  DOT1_ANIMATION_KEYTIMES,
  DOT2_ANIMATION_KEYTIMES,
  DOT3_ANIMATION_KEYTIMES,
  propTypes
} from './shared'
import { useA11yInfo } from '../A11yInfoProvider/A11yInfoProvider'

const CY_PROPS = {
  attributeName: 'cy',
  calcMode: 'spline',
  dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
  repeatCount: 'indefinite'
}

const FILL_PROPS = {
  attributeName: 'fill',
  calcMode: 'linear',
  dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
  repeatCount: 'indefinite'
}

const OPACITY_PROPS = {
  attributeName: 'opacity',
  calcMode: 'linear',
  dur: `${DOTS_TOTAL_ANIMATION_DURATION}ms`,
  repeatCount: 'indefinite'
}

export const Dots = React.forwardRef(
  ({ size, color, indicatorBackgroundColor, label, isStatic = false }, ref) => {
    const { reduceMotionEnabled } = useA11yInfo()
    const reduceMotion = reduceMotionEnabled || isStatic

    const spacingX = size + DOTS_SPACING_X
    const baseY = size * DOTS_BASE_Y_FACTOR
    const jumpHeight = size * DOTS_JUMP_HEIGHT_FACTOR
    const padding = size * DOTS_PADDING_FACTOR

    return (
      <svg
        ref={ref}
        aria-valuetext={label}
        role="progressbar"
        aria-busy="true"
        width={size * 3 + spacingX * 2 + padding * 2}
        height={size * 6}
        viewBox={`0 0 ${size * 3 + spacingX * 2 + padding * 2} ${size * 6}`}
      >
        <g>
          {/* ===================== DOT 1 ===================== */}
          <circle cx={padding + size / 2} cy={baseY} r={size / 2} fill={color}>
            {!reduceMotion && (
              <>
                <animate
                  {...CY_PROPS}
                  keyTimes={DOT1_ANIMATION_KEYTIMES}
                  keySplines={`${BOUNCY_CURVE};${BOUNCY_CURVE};${BOUNCY_CURVE}`}
                  values={`
                  ${baseY};
                  ${baseY - jumpHeight * OVERSHOOT_FACTOR};
                  ${baseY + jumpHeight * UNDERSHOOT_FACTOR};
                  ${baseY}
                `}
                />
                {/* color (fill) */}
                <animate
                  {...FILL_PROPS}
                  keyTimes={DOT1_COLOR_KEYTIMES}
                  values={`
                  ${color}; 
                  ${color}; 
                  ${indicatorBackgroundColor}; 
                  ${indicatorBackgroundColor}
                `}
                />
                {/* fade-out */}
                <animate
                  {...OPACITY_PROPS}
                  keyTimes={DOTS_FADEOUT_KEYTIMES}
                  values={DOTS_FADEOUT_VALUES}
                />
              </>
            )}
          </circle>

          {/* ===================== DOT 2 ===================== */}
          <circle
            cx={padding + size + spacingX + size / 2}
            cy={baseY}
            r={size / 2}
            fill={indicatorBackgroundColor}
          >
            {reduceMotion ? null : (
              <>
                <animate
                  {...CY_PROPS}
                  keyTimes={DOT2_ANIMATION_KEYTIMES}
                  keySplines={`
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE}
                `}
                  values={`
                  ${baseY};
                  ${baseY};
                  ${baseY - jumpHeight * OVERSHOOT_FACTOR};
                  ${baseY + jumpHeight * UNDERSHOOT_FACTOR};
                  ${baseY}
                `}
                />
                {/* color (fill) */}
                <animate
                  {...FILL_PROPS}
                  keyTimes={DOT2_COLOR_KEYTIMES}
                  values={`
                  ${indicatorBackgroundColor}; 
                  ${indicatorBackgroundColor}; 
                  ${color}; 
                  ${color}; 
                  ${indicatorBackgroundColor}; 
                  ${indicatorBackgroundColor}
                `}
                />
                {/* fade-out */}
                <animate
                  {...OPACITY_PROPS}
                  keyTimes={DOTS_FADEOUT_KEYTIMES}
                  values={DOTS_FADEOUT_VALUES}
                />
              </>
            )}
          </circle>

          {/* ===================== DOT 3 ===================== */}
          <circle
            cx={padding + (size + spacingX) * 2 + size / 2}
            cy={baseY}
            r={size / 2}
            fill={indicatorBackgroundColor}
          >
            {reduceMotion ? null : (
              <>
                <animate
                  {...CY_PROPS}
                  keyTimes={DOT3_ANIMATION_KEYTIMES}
                  keySplines={`
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE};
                  ${BOUNCY_CURVE}
                `}
                  values={`
                  ${baseY};
                  ${baseY};
                  ${baseY - jumpHeight * OVERSHOOT_FACTOR};
                  ${baseY + jumpHeight * UNDERSHOOT_FACTOR};
                  ${baseY}
                `}
                />
                {/* color (fill) */}
                <animate
                  {...FILL_PROPS}
                  keyTimes={DOT3_COLOR_KEYTIMES}
                  values={`
                  ${indicatorBackgroundColor}; 
                  ${indicatorBackgroundColor}; 
                  ${color}; 
                  ${color}; 
                  ${color}
                `}
                />
              </>
            )}
          </circle>
        </g>
      </svg>
    )
  }
)

Dots.displayName = 'Dots'
Dots.propTypes = propTypes
