import React, { ReactElement, SVGProps } from "react";
import { Center, theme } from "../../..";

interface Props {
  speed: number;
  stroke: string;
  strokeWidth: number | string;
  strokeOpacity: number | string;
  fill?: string;
  fillOpacity?: number | string;
}

const Puff = ({ speed, ...rest }: Props) => (
  <g fill="none" fillRule="evenodd" {...{ ...rest }}>
    <circle cx={22} cy={22} r={1}>
      <animate
        attributeName="r"
        begin="0s"
        dur={`${1.8 / speed}s`}
        values="1; 20"
        calcMode="spline"
        keyTimes="0; 1"
        keySplines="0.165, 0.84, 0.44, 1"
        repeatCount="indefinite"
      />
      <animate
        attributeName="stroke-opacity"
        begin="0s"
        dur={`${1.8 / speed}s`}
        values="1; 0"
        calcMode="spline"
        keyTimes="0; 1"
        keySplines="0.3, 0.61, 0.355, 1"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={22} cy={22} r={1}>
      <animate
        attributeName="r"
        begin="-0.9s"
        dur={`${1.8 / speed}s`}
        values="1; 20"
        calcMode="spline"
        keyTimes="0; 1"
        keySplines="0.165, 0.84, 0.44, 1"
        repeatCount="indefinite"
      />
      <animate
        attributeName="stroke-opacity"
        begin="-0.9s"
        dur={`${1.8 / speed}s`}
        values="1; 0"
        calcMode="spline"
        keyTimes="0; 1"
        keySplines="0.3, 0.61, 0.355, 1"
        repeatCount="indefinite"
      />
    </circle>
  </g>
);

const Oval = ({ speed, ...rest }: Props) => (
  <g transform="translate(1 1)" fill="none" fillRule="evenodd" {...{ ...rest }}>
    <circle strokeOpacity={0.5} cx={18} cy={18} r={18} />
    <path d="M36 18c0-9.94-8.06-18-18-18">
      <animateTransform
        attributeName="transform"
        type="rotate"
        from="0 18 18"
        to="360 18 18"
        dur={`${1 / speed}s`}
        repeatCount="indefinite"
      />
    </path>
  </g>
);

const Grid = ({ speed, fill = theme.color.primary[500], ...rest }: Props) => (
  <>
    <circle cx={12.5} cy={12.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="0s"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={12.5} cy={52.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="100ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={52.5} cy={12.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="300ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={52.5} cy={52.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="600ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={92.5} cy={12.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="800ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={92.5} cy={52.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="400ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={12.5} cy={92.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="700ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={52.5} cy={92.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="500ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
    <circle cx={92.5} cy={92.5} r={12.5} {...{ fill, ...rest }}>
      <animate
        attributeName="fill-opacity"
        begin="200ms"
        dur={`${1 / speed}s`}
        values="1;.2;1"
        calcMode="linear"
        repeatCount="indefinite"
      />
    </circle>
  </>
);

interface SpinnerProps {
  /**
   * Size of the loader in pixels, default is 40
   */
  size?: number | string;
  /**
   * Speed of the spinner in the format `${1 / speed}s`, the higher the faster
   */
  speed?: number;
  /**
   * Color of the spinner
   */
  color?: string;
  /**
   * Thickness of the spinner
   */
  strokeWidth?: string | number;
  /**
   * Opacity of the spinner
   */
  strokeOpacity?: string | number;
  /**
   * Type of spinner to display
   */
  variant?: "grid" | "puff" | "oval";
}

export const Spinner = ({
  size = 40,
  color,
  speed = 1,
  strokeWidth = 2,
  strokeOpacity,
  variant = "oval",
  ...props
}: JSX.IntrinsicAttributes &
  SVGProps<SVGSVGElement> &
  SpinnerProps): ReactElement => {
  return (
    <Center h={size} w={size || "fit-content"}>
      <svg
        {...{
          ...props,
          fill: undefined,
          fillOpacity: undefined,
          height: "inherit",
          speed: undefined,
          stroke: undefined,
          strokeOpacity: undefined,
          strokeWidth: undefined,
          width: "inherit"
        }}
        viewBox={variant === "grid" ? "0 0 105 105" : "0 0 38 38"}
        overflow="visible"
      >
        {variant === "grid" && (
          <Grid
            speed={speed as number}
            stroke={color}
            strokeWidth={strokeWidth}
            strokeOpacity={strokeOpacity}
          />
        )}
        {variant === "puff" && (
          <Puff
            speed={speed as number}
            stroke={color || theme.color.primary[500]}
            strokeWidth={strokeWidth}
            strokeOpacity={strokeOpacity}
          />
        )}
        {variant === "oval" && (
          <Oval
            speed={speed as number}
            stroke={color || theme.color.primary[500]}
            strokeWidth={strokeWidth}
            strokeOpacity={strokeOpacity}
          />
        )}
      </svg>
    </Center>
  );
};
