"""
    https://en.wikipedia.org/wiki/Ellipse#General_parametric_form
"""

import numpy as np
import matplotlib.pyplot as plt


class Pendel():
    def __init__(self, a: float, b: float, omega: float, phi: float, delta: float, tau: float, sub: int = 0):
        self.a = a
        self.b = b
        self.omega = omega
        self.phi = phi
        self.delta = delta
        self.tau = tau
        self._sub = sub

    def point(self, t: float):
        x = (self.a * np.cos(t*self.omega + self.delta)*np.cos(t*self.phi)
             - self.b*np.sin(t*self.omega + self.delta)*np.sin(t*self.phi))
        y = (self.a * np.cos(t*self.omega + self.delta)*np.sin(t*self.phi)
             + self.b*np.sin(t*self.omega + self.delta)*np.cos(t*self.phi))
        return np.exp(-t*self.tau)*np.array([x, y])

    def __repr__(self):
        SUB = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
        sub = f"{self._sub}".translate(SUB)
        return f"A{sub}={self.a}, B{sub}={self.b}, ω{sub}={self.omega}, Φ{sub}={self.phi}, δ{sub}={self.delta}, τ{sub}={self.tau}"

    def update(self, **kwargs):
        for key, val in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, val)
            else:
                raise ValueError(f'{self.__name__} object has no attribute {key}')


class DoppelPendel():
    def __init__(self, kwargs1, kwargs2):
        self.stift = Pendel(**kwargs1, sub=1)
        self.tisch = Pendel(**kwargs2, sub=2)

    def point(self, t):
        return self.stift.point(t)+self.tisch.point(t)

    def update_stift(self, **kwargs):
        self.stift.update(**kwargs)

    def update_tisch(self, **kwargs):
        self.tisch.update(**kwargs)

    def __repr__(self):
        return str(self.stift) + "\n" + str(self.tisch)


class Harmonograph(DoppelPendel):
    def __init__(self, kwargs1, kwargs2, t_init, n=10000):
        super().__init__(kwargs1, kwargs2)
        self.n = n
        self.t = t_init
        self.fig, self.ax = plt.subplots()

        self.ax.set_xlim(-2, 2)
        self.ax.set_ylim(-2, 2)
        self.ax.set_aspect('equal')
        self.ax.axis('off')

        self.init_harmonograph()

        # self.label = self.ax.text(0, -2, str(self),
        #                           horizontalalignment='center',
        #                           verticalalignment='bottom',)

    def init_harmonograph(self):
        t = np.linspace(0, self.t, self.n)
        self.ax.plot(*self.point(t), 'k', lw=0.5)


if __name__ == "__main__":

    # kw1 = dict(
    #     a=1,
    #     b=1,
    #     omega=np.pi/60,
    #     phi=np.pi/180,
    #     delta=np.pi/2,
    #     tau=0
    # )
    # kw2 = dict(
    #     a=0.2,
    #     b=1,
    #     omega=0.1204277183860873,
    #     phi=np.pi/90,
    #     delta=0,
    #     tau=0
    # )
    kw1 = dict(
        a=1,
        b=0.3,
        omega=0.05,
        phi=2.5e-4,
        delta=0,
        tau=5e-7
    )
    kw2 = dict(
        a=0.5,
        b=0,
        omega=1/10,
        phi=5e-5,
        delta=0,
        tau=5e-5
    )
    p1 = Harmonograph(kw1, kw2, 7000)

    plt.show()
