from turtle import Shape, Screen, Turtle, Vec2D as Vec

# Parametry określane przez użytkownika
G = 8  # Stała grawitacji używana do symulacji
NUM_LOOPS = 4100  # Liczba odstępów czasu, jakie trwa symulacja
Ro_X = 0  # Współrzędna x początkowej pozycji statku
Ro_Y = -85  # Współrzędna y początkowej pozycji statku
Vo_X = 485 # Składowa x prędkości w momencie rozpoczęcia manewru TLI
Vo_Y = 0  # Składowa x prędkości w momencie rozpoczęcia manewru TLI


class GravSys():
    """Przeprowadza symulację oddziaływań grawitacyjnych na n ciałach."""
   
    def __init__(self):
        self.bodies = []
        self.t = 0
        self.dt = 0.001                
            
    def sim_loop(self):
        """
        Tworzy pętlę, która aktualizuje wszystkie ciała układu,
        dla każdego odcinka czasu.
        """
        # Zatrzymuje symulację po wodowaniu kapsuły.
        for _ in range(NUM_LOOPS):
            self.t += self.dt
            for body in self.bodies:
                body.step()
                

class Body(Turtle):
    """Ciało niebieskie, które porusza się i ma pole grawitacyjne."""
    def __init__(self, mass, start_loc, vel, gravsys, shape):
        super().__init__(shape=shape)
        self.gravsys = gravsys
        self.penup()
        self.mass = mass
        self.setpos(start_loc)
        self.vel = vel
        gravsys.bodies.append(self)
        #self.resizemode("user")
        #self.pendown()  # Odkomentuj, aby obiekt rysował za sobą ścieżkę.
        
    def acc(self):
        """
        Oblicza połączone siły działające na ciało
        i zwraca składowe wektora.
        """
        a = Vec(0, 0)
        for body in self.gravsys.bodies:
            if body != self:
                r = body.pos() - self.pos()
                # Jednostki: odległość/czas^2.
                a += (G * body.mass / abs(r)**3) * r
        return a    
    
    def step(self):
        """Oblicza pozycję, zwrot i prędkość ciała."""
        dt = self.gravsys.dt
        a = self.acc()
        self.vel = self.vel + dt * a
        self.setpos(self.pos() + dt * self.vel)
        if self.gravsys.bodies.index(self) == 2:  # Indeks 2 = CSM
            rotate_factor = 0.0006
            self.setheading((self.heading() - rotate_factor * self.xcor()))
            if self.xcor() < -20:
                self.shape('arrow')
                self.shapesize(0.5)
                self.setheading(105)

    
def main():
    # Przygotowuje ekran do użycia.
    screen = Screen()
    screen.setup(width=1.0, height=1.0)  # Tryb pełnoekranowy
    screen.bgcolor('black')
    screen.title("Symulacja swobodnego powrotu misji Apollo 8")

    # Tworzy obiekt reprezentujący układ ciał.
    gravsys = GravSys()

    # Tworzy obiekt turtle reprezentujący Ziemię.
    image_earth = 'earth_100x100.gif'
    screen.register_shape(image_earth)
    earth = Body(1000000, (0, -25), Vec(0, -2.5), gravsys, image_earth)
    earth.pencolor('white')
    # Aby nie było widać figur składających się na csm podczas jego budowania
    earth.getscreen().tracer(n=0, delay=0)

    # Tworzy obiekt turtle reprezentujący Księżyc.
    image_moon = 'moon_27x27.gif'
    screen.register_shape(image_moon)
    moon = Body(32000, (344, 42), Vec(-27, 147), gravsys, image_moon)
    moon.pencolor('gray')

    # Tworzy kształt modułu usługowego i dowodzenia (csm).
    csm = Shape('compound')
    cm = ((0, 30), (0, -30), (30, 0))
    # Srebrny i szary również się nadają.
    csm.addcomponent(cm, 'white', 'white')
    sm = ((-60, 30), (0, 30), (0, -30), (-60, -30))
    csm.addcomponent(sm, 'white', 'black')    
    nozzle = ((-55, 0), (-90, 20), (-90, -20))
    csm.addcomponent(nozzle, 'white', 'white')
    screen.register_shape('csm', csm)

    # Tworzy obiekt turtle reprezentujący moduł CSM misji Apollo 8.
    ship = Body(1, (Ro_X, Ro_Y), Vec(Vo_X, Vo_Y), gravsys, 'csm')
    ship.shapesize(0.2)
    # Kolor ścieżki ( srebrny i czerwony również się nadają).
    ship.color('white')
    ship.getscreen().tracer(1, 0)
    ship.setheading(90)

    gravsys.sim_loop()


if __name__ == '__main__':
    main()
