package wtf.opal.utility.render.animation;

import java.util.function.Function;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.math.MathHelper;

@Environment(EnvType.CLIENT)
public enum Easing {
   LINEAR(x -> x),
   DECELERATE(x -> 1.0F - (x - 1.0F) * (x - 1.0F)),
   SMOOTH_STEP(x -> (float)(-2.0 * Math.pow(x, 3.0) + 3.0 * Math.pow(x, 2.0))),
   EASE_IN_QUAD(x -> x * x),
   EASE_OUT_QUAD(x -> x * (2.0F - x)),
   EASE_IN_OUT_QUAD(x -> x < 0.5F ? 2.0F * x * x : -1.0F + (4.0F - 2.0F * x) * x),
   EASE_IN_CUBIC(x -> x * x * x),
   EASE_OUT_CUBIC(x -> {
      float shifted = x - 1.0F;
      return shifted * shifted * shifted + 1.0F;
   }),
   EASE_IN_OUT_CUBIC(x -> x < 0.5F ? 4.0F * x * x * x : (x - 1.0F) * (2.0F * x - 2.0F) * (2.0F * x - 2.0F) + 1.0F),
   EASE_IN_QUART(x -> x * x * x * x),
   EASE_OUT_QUART(x -> {
      float shifted = x - 1.0F;
      return 1.0F - shifted * shifted * shifted * shifted;
   }),
   EASE_IN_OUT_QUART(x -> {
      if (x < 0.5F) return 8.0F * x * x * x * x;
      float shifted = x - 1.0F;
      return 1.0F - 8.0F * shifted * shifted * shifted * shifted;
   }),
   EASE_IN_QUINT(x -> x * x * x * x * x),
   EASE_OUT_QUINT(x -> {
      float shifted = x - 1.0F;
      return 1.0F + shifted * shifted * shifted * shifted * shifted;
   }),
   EASE_IN_OUT_QUINT(x -> {
      if (x < 0.5F) return 16.0F * x * x * x * x * x;
      float shifted = x - 1.0F;
      return 1.0F + 16.0F * shifted * shifted * shifted * shifted * shifted;
   }),
   EASE_IN_SINE(x -> 1.0F - MathHelper.cos((float)(x * Math.PI * 0.5))),
   EASE_OUT_SINE(x -> MathHelper.sin((float)(x * Math.PI * 0.5))),
   EASE_IN_OUT_SINE(x -> 1.0F - MathHelper.cos((float)(Math.PI * x * 0.5))),
   EASE_IN_EXPO(x -> x == 0.0F ? 0.0F : (float)Math.pow(2.0, 10.0F * x - 10.0F)),
   EASE_OUT_EXPO(x -> x == 1.0F ? 1.0F : 1.0F - (float)Math.pow(2.0, -10.0F * x)),
   EASE_IN_OUT_EXPO(x -> x == 0.0F ? 0.0F : x == 1.0F ? 1.0F : x < 0.5F ? (float)Math.pow(2.0, 20.0F * x - 10.0F) * 0.5F : (2.0F - (float)Math.pow(2.0, -20.0F * x + 10.0F)) * 0.5F),
   EASE_IN_CIRC(x -> 1.0F - (float)Math.sqrt(1.0F - x * x)),
   EASE_OUT_CIRC(x -> {
      float shifted = x - 1.0F;
      return (float)Math.sqrt(1.0F - shifted * shifted);
   }),
   EASE_IN_OUT_CIRC(x -> x < 0.5F ? (1.0F - (float)Math.sqrt(1.0F - 4.0F * x * x)) * 0.5F : ((float)Math.sqrt(1.0F - 4.0F * (x - 1.0F) * x) + 1.0F) * 0.5F),
   SIGMOID(x -> 1.0F / (1.0F + (float)Math.exp(-x))),
   EASE_OUT_ELASTIC(x -> x == 0.0F ? 0.0F : x == 1.0F ? 1.0F : (float)(Math.pow(2.0, -10.0F * x) * Math.sin((x * 10.0F - 0.75F) * (2.0 * Math.PI / 3.0)) * 0.5F + 1.0)),
   EASE_IN_BACK(x -> 2.70158F * x * x * x - 1.70158F * x * x),
   DYNAMIC_ISLAND(x -> (float)(1.0 - Math.cos(x * Math.PI * (0.2 + 2.5 * Math.pow(x, 3.0))) * Math.exp(-x * 5.0F)));

   private final Function<Float, Float> function;

   Easing(Function<Float, Float> function) {
      this.function = function;
   }

   public Function<Float, Float> getFunction() {
      return this.function;
   }
}
