import numpy as np
from typing import Any


class SolarSystem:
    def __init__(
        self,
        initial_p: np.ndarray,
        initial_s: np.ndarray,
        masses: np.ndarray,
        delta: float = 1.0e-8,
        initial_a: np.ndarray = None,
    ) -> None:
        self.G = 6.67430e-11
        self.positions = initial_p
        self.speed = initial_s
        self.acceleration = initial_a
        if initial_a is None:
            self.acceleration = np.zeros(shape=self.speed.shape)
        self.masses = masses
        self.delta = delta  # c'est chose là c'est h !

    def compute_acceleration(self):
        """
        computation of the acceleration based on the gravitation formulae
        """
        # option 1
        self.acceleration = np.zeros(shape=self.positions.shape)
        # option 2
        self.acceleration = []
        for cpt in range(len(self.positions)):
            tmp = []
            for cpt2 in range(len(self.positions[cpt])):
                tmp.append(0)
            self.acceleration.append(tmp)
        # option 3
        self.acceleration = self.positions.copy() * 0
        # option 4
        self.acceleration = self.acceleration * 0
        # --------
        for cpt in range(len(self.acceleration)):  # pour chaque acceleration...
            for cpt_ in range(
                len(self.acceleration)
            ):  # la force induite par chaque corps de mon système...
                if cpt == cpt_:  # la force d'un corps sur lui-même
                    pass
                else:  # la force du corps cpt_ sur le corps cpt
                    distance = np.sqrt(
                        np.sum((self.positions[cpt] - self.positions[cpt_]) ** 2)
                    )
                    direction = (
                        self.positions[cpt_] - self.positions[cpt]
                    ) / distance  # on va dans la direction du cours qui nous attire
                    self.acceleration[cpt] += (
                        direction * self.masses[cpt_] / (distance**2)
                    )
        self.acceleration *= self.G

    def update_speed(self):
        """
        in this function we update the speed based on the acceleration
        """
        self.speed += self.delta * self.acceleration

    def update_position(self):
        """
        in this function we update the position based on the speed
        """
        self.positions += self.delta * self.speed

    def __call__(self, *args: Any, **kwds: Any) -> np.ndarray:
        """
        this function updates the solar system by one step and returns log scale positions
        """
        self.update_position()
        self.compute_acceleration()
        self.update_speed()
        return self.positions
