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

    def compute_acceleration(self):
        """
        computation of the acceleration based on the gravitation formulae
        """
        self.acceleration = np.zeros(shape=self.positions.shape)
        for cpt in range(len(self.acceleration)):
            for cpt_ in range(len(self.acceleration)):
                if cpt != cpt_:
                    direction = self.positions[cpt_] - self.positions[cpt]
                    distance = np.sqrt(
                        np.sum((self.positions[cpt] - self.positions[cpt_]) ** 2)
                    )
                    direction = direction / distance
                    r2 = max(distance**2, 1)
                    self.acceleration[cpt] += direction * self.masses[cpt_] / r2
        self.acceleration *= self.G

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

    def update_position(self):
        """
        in this function we update the position based on the speed
        """
        self.positions = 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