"""
Base easing functions modified from: https://easings.net/
"""

import math
import numpy as np
from .bezier import bezierMotionCurve


class EasingBase:
    def __init__(self, start=0, stop=1, duration=1.0):
        self.start = start
        self.stop = stop
        self.duration = duration

    def __call__(self):
        t = np.linspace(0, 1, self.duration)
        _, a = self.func(t)
        return self.stop * a + self.start * (1 - a)

    @classmethod
    def func(cls, t):
        raise NotImplementedError


class BezierEase(EasingBase):
    def __init__(
        self, x0=0.45, y0=0.25, x1=0.55, y1=0.75, start=0, stop=1, duration=1
    ):

        super().__init__(start, stop, duration)
        self.f = bezierMotionCurve(x0, y0, x1, y1)

    def func(self, t):
        return self.f(t)


def BezierClassFactory(x0, y0, x1, y1):
    class B(BezierEase):
        def __init__(self, start=0, stop=1, duration=1):
            super().__init__(x0, y0, x1, y1, start, stop, duration)

    return B


named_easings = {
    "easeInSine": (0.47, 0, 0.745, 0.715),
    "easeOutSine": (0.39, 0.575, 0.565, 1),
    "easeInOutSine": (0.445, 0.05, 0.55, 0.95),
    "easeInQuad": (0.55, 0.085, 0.68, 0.53),
    "easeOutQuad": (0.25, 0.46, 0.45, 0.94),
    "easeInOutQuad": (0.455, 0.03, 0.515, 0.955),
    "easeInCubic": (0.55, 0.055, 0.675, 0.19),
    "easeOutCubic": (0.215, 0.61, 0.355, 1),
    "easeInOutCubic": (0.645, 0.045, 0.355, 1),
    "easeInQuart": (0.895, 0.03, 0.685, 0.22),
    "easeOutQuart": (0.165, 0.84, 0.44, 1),
    "easeInOutQuart": (0.77, 0, 0.175, 1),
    "easeInQuint": (0.755, 0.05, 0.855, 0.06),
    "easeOutQuint": (0.23, 1, 0.32, 1),
    "easeInOutQuint": (0.86, 0, 0.07, 1),
    "easeInExpo": (0.95, 0.05, 0.795, 0.035),
    "easeOutExpo": (0.19, 1, 0.22, 1),
    "easeInOutExpo": (1, 0, 0, 1),
    "easeInCirc": (0.6, 0.04, 0.98, 0.335),
    "easeOutCirc": (0.075, 0.82, 0.165, 1),
    "easeInOutCirc": (0.785, 0.135, 0.15, 0.86),
    "easeInBack": (0.6, -0.28, 0.735, 0.045),
    "easeOutBack": (0.175, 0.885, 0.32, 1.275),
    "easeInOutBack": (0.68, -0.55, 0.265, 1.55),
}

for key, vals in named_easings.items():
    locals()[key] = BezierClassFactory(*vals)

#########################################################################
# Custom Easing functions
#########################################################################


class offsetEase(BezierEase):
    def __init__(self, dx=0.0, dy=0.1, baseEase="easeInSine", *args, **kwargs):
        x0, x1, y0, y1 = named_easings[baseEase]
        x0 -= dx
        x1 += dx
        y0 -= dy
        y1 += dy

        super().__init__(x0, x1, y0, y1, *args, **kwargs)


def easeReturn(easing_func, start, stop, frames):
    """
    Returns an easing run that finishes halfway through and returns.
    """
    if isinstance(easing_func, str):
        easing_func = globals()[easing_func]

    n0 = frames // 2
    n1 = frames - n0

    x0 = easing_func(start, stop, n0)()
    x1 = easing_func(stop, start, n1)()

    return np.hstack([x0, x1])
