import React, { useEffect, useState, useRef } from "react";
import { assetsPath } from "./Common";
import "./Sheep.css";

const sheepSteps = 12;

const useAnimationFrame = (callback: (t: number) => void) => {
  const requestRef = useRef<number | null>(null);
  const animate = (time: number) => {
    callback(time);
    requestRef.current = requestAnimationFrame(animate);
  };
  useEffect(() => {
    requestRef.current = requestAnimationFrame(animate);
    return () => {
      if (requestRef.current !== null) {
        cancelAnimationFrame(requestRef.current);
      }
    };
  }, []);
};

const Sheep: React.FC<{ radius: number; speed: number; size: number; style?: React.CSSProperties }> = ({
  radius,
  speed,
  size,
  style,
}) => {
  const [time, setTime] = useState(0);
  const [direction, setDirection] = useState(Math.random() * 2 * Math.PI);
  const [y, setY] = useState((Math.random() - 0.5) * radius);
  const [frame, setFrame] = useState(0);
  const [x, setX] = useState((Math.random() - 0.5) * radius);
  const previousTimeRef = useRef<number | undefined>();

  useAnimationFrame((t) => {
    if (previousTimeRef.current !== undefined) {
      const deltaTime = t - previousTimeRef.current;
      previousTimeRef.current = t;
      setTime(deltaTime);
    } else {
      previousTimeRef.current = t;
    }
  });

  useEffect(() => {
    let alpha = time / speed;
    const sheepSpeed = 0.05;
    if (alpha > 1.0) alpha = 0.1;
    let newX = x + sheepSpeed * Math.sin(direction) * alpha;
    let newY = y + sheepSpeed * Math.cos(direction) * alpha;
    if (Math.sqrt(newX * newX + newY * newY) > Math.sqrt(radius * radius)) {
      let newDirection = direction + Math.PI + 0.5 * (Math.random() - 0.5) * Math.PI;
      while (newDirection >= 2 * Math.PI) newDirection -= 2 * Math.PI;
      while (newDirection <= -2 * Math.PI) newDirection += 2 * Math.PI;
      setDirection(newDirection);
      newX += sheepSpeed * Math.sin(newDirection) * alpha;
      newY += sheepSpeed * Math.cos(newDirection) * alpha;
    }
    setX(newX);
    setY(newY);
    setFrame(frame + sheepSteps * alpha);
  }, [time, speed]);

  const cell = Math.floor(frame) % sheepSteps;

  return (
    <div
      className={`Sheep`}
      style={{
        zIndex: `${Math.ceil(50 * y)}`,
        top: `${Math.floor(50 + 50 * y)}%`,
        left: `${Math.floor(50 + 50 * x)}%`,
        width: `${size * 60}px`,
        height: `${size * 52}px`,
        backgroundRepeat: "no-repeat",
        backgroundImage: `url(${assetsPath}/gfx/sheep.png)`,
        backgroundPositionX: `${(100.0 * cell) / (sheepSteps - 1)}%`,
        transformOrigin: `50% 50%`,
        transform: `translate(-50%, -50%) scale(${Math.sin(direction) > 0 ? +1 : -1}, 1)`,
        ...style,
      }}
    />
  );
};

const Herd: React.FC<{ count: number; style?: React.CSSProperties }> = ({ count, style }) => {
  const [sheep, setSheep] = useState<React.ReactNode[]>([]);
  useEffect(() => {
    const tmp: React.ReactNode[] = [];
    for (let i = 0; i < count; i++) {
      const scalar = Math.random();
      tmp.push(<Sheep speed={1000 + 500 * scalar} size={0.25} radius={0.8} key={i} />);
    }
    setSheep(tmp);
  }, [count]);

  return (
    <div className="Herd" style={style}>
      {sheep}
    </div>
  );
};

export { Sheep, Herd };
