#!/usr/bin/env python3

# Simulacija gravitacijske sile v 2D.

# Pognali bomo dva vzporedna procesa, eden bo nadzoroval uporabniški vmesnik,
# drugi pa bo računal simulacijo n-teles v gravitacijskem polju

import tkinter # Knjižnica za uporabniški vmesnik
from multiprocessing import Queue # Sinhronizacija med procesoma poteka preko vrste
import argparse # Knjižnica za parametere v ukazni vrstici
from telo import Telo
from simulator import Simulator

def koordinate(x, y, zoom, width, height):
    """Izračunaj koordinate točke (x,y) na območju dane velikosti za dano povečavo."""
    r = min(width / zoom, height / zoom) * 0.5
    return (int((x * r + width * 0.5)), int(y * r + height * 0.5))

class GravitacijaApp():
    def __init__(self, root, telesa, dt, G, width=512, height=512):
        # Območje, na katerega rišemo telesa
        self.width = width   # širina območja
        self.height = height # višina območja
        self.canvas = tkinter.Canvas(root, width=width, height=height)
        self.canvas.grid(row=0, column=0, columnspan=2)

        # Drugi stolpec se mora razširiti po vsem razpoložljivem prostoru
        root.grid_columnconfigure(1, weight=1)

        # Napis, ki prikazuje pohitritev
        self.speedupVar = tkinter.IntVar()
        tkinter.Label(root, text="Pohitritev:").grid(row=1, column=0, sticky=tkinter.W)
        tkinter.Label(root, textvariable=self.speedupVar).grid(row=1, column=1, sticky=tkinter.W)

        # Napis, ki prikazuje iteracijo
        self.iterVar = tkinter.IntVar()
        tkinter.Label(root, text="Iteracija:").grid(row=2, column=0, sticky=tkinter.W)
        tkinter.Label(root, textvariable=self.iterVar).grid(row=2, column=1, sticky=tkinter.W)

        # Napis, ki prikazuje faktor povečave
        self.zoomVar = tkinter.DoubleVar()
        tkinter.Label(root, text="Pomanjšava:").grid(row=3, column=0, sticky=tkinter.W)
        tkinter.Label(root, textvariable=self.zoomVar).grid(row=3, column=1, sticky=tkinter.W)

        # Napis za gibalno količino
        self.gibalnaVar = tkinter.DoubleVar()
        tkinter.Label(root, text="Gibalna količina:").grid(row=4, column=0, sticky=tkinter.W)
        tkinter.Label(root, textvariable=self.gibalnaVar).grid(row=4, column=1, sticky=tkinter.W)

        # Vrsta za komunikacijo z računskim procesom
        self.vrsta = Queue(maxsize=1)

        # Računski proces
        self.simulator = Simulator(telesa, self.vrsta, dt, G)

        # Zaženemo simulacijo v vzporednem procesu
        self.simulator.start()

        # Če kliknemo v Canvas, pokličemo metodo klik
        self.canvas.bind('<Button-1>', self.klik)

        # Čez 100ms zaženemo še animacijo
        self.canvas.after(100, self.animacija)

    def klik(self, event):
        print("Čestitamo, kliknili ste z miško na koordinatah ({0}, {1})".format(event.x, event.y))


    def animacija(self):
        # Iz vrste poberemo trenutno stanje teles, zoom, številko iteracije in pohitritev
        (telesa, zoom, iteracija, speedup) = self.vrsta.get()
        # Posodobimo napise
        self.speedupVar.set(speedup)
        self.iterVar.set(iteracija)
        self.zoomVar.set(zoom)

        # Pobrišemo vse krogce
        self.canvas.delete(tkinter.ALL)
        # Narišemo nove krogce in izračunamo skupno gibalno količino
        (px, py) = (0.0, 0.0)
        for t in telesa:
            r = t.radij()
            # Posodobi krogec
            (sx, sy) = koordinate(t.x, t.y, zoom, self.width, self.height)
            self.canvas.create_oval(sx-r, sy-r, sx+r, sy+r, fill='red')
            px += t.vx * t.m
            py += t.vy * t.m
        # Posodobimo gibalno količino na zaslonu
        self.gibalnaVar.set((px * px + py * py) ** 0.5)

        # Čez nekaj milisekund ponovno animiramo
        self.canvas.after(200, self.animacija)


### GLAVNI PROGRAM ###

# Določimo parametre v ukazni vrstici
arg_parser = argparse.ArgumentParser(description='Simulacija gravitacije.')
arg_parser.add_argument("--bodies", default=50, type=int, dest='bodies', help="število teles")
arg_parser.add_argument("--dt", default=0.000001, type=float, dest='dt', help="časovni korak")
arg_parser.add_argument("--G", default=100.0, type=float, dest='G', help="gravitacijska konstanta")

# Sprocesiramo parameter iz ukazne vrstice
args = arg_parser.parse_args()

# Ustvari glavno okno
root = tkinter.Tk()
root.title('Gravitacija')

# Ustvari aplikacijo
app = GravitacijaApp(root, [Telo.nakljucno() for i in range(args.bodies)], args.dt, args.G)

# Postvi okno na vrh (hack, glupa knjižnica)
root.attributes('-topmost', True)
root.update()
root.attributes('-topmost', False)

# Zaženi glavno uporabniško zanko
root.mainloop()
