import os, datetime, math, random
import customtkinter as ctk
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk, ImageDraw
import ephem
from skyfield.api import Loader, wgs84

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

class Menu15App(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("Live Animasi 2D Tracker (Modul 15)")
        self.geometry("1100x700")
        ctk.set_appearance_mode("Dark")
        
        self.load_obj = Loader(BASE_DIR)
        self.eph = self.load_obj('de421.bsp')
        self.ts = self.load_obj.timescale()
        
        self.setup_ui()
        self.anim_is_live = True
        self.anim_custom_time = None
        self.anim_running = True
        
        self.draw_static_background()
        self.update_animation()

    def setup_ui(self):
        self.sidebar = ctk.CTkFrame(self, width=280)
        self.sidebar.pack(side="left", fill="y", padx=10, pady=10)
        
        ctk.CTkLabel(self.sidebar, text="LIVE ANIMASI 2D", font=("Segoe UI", 16, "bold")).pack(pady=20)
        
        # Form Lokasi
        self.entry_lat = ctk.CTkEntry(self.sidebar, placeholder_text="Latitude"); self.entry_lat.insert(0, "-7.0667"); self.entry_lat.pack(pady=5)
        self.entry_lon = ctk.CTkEntry(self.sidebar, placeholder_text="Longitude"); self.entry_lon.insert(0, "110.4100"); self.entry_lon.pack(pady=5)
        self.entry_tz = ctk.CTkEntry(self.sidebar, placeholder_text="Timezone"); self.entry_tz.insert(0, "7.0"); self.entry_tz.pack(pady=5)
        
        now = datetime.datetime.now()
        self.entry_y = ctk.CTkEntry(self.sidebar); self.entry_y.insert(0, str(now.year)); self.entry_y.pack(pady=5)
        self.entry_m = ctk.CTkEntry(self.sidebar); self.entry_m.insert(0, f"{now.month:02d}"); self.entry_m.pack(pady=5)
        self.entry_d = ctk.CTkEntry(self.sidebar); self.entry_d.insert(0, f"{now.day:02d}"); self.entry_d.pack(pady=5)
        self.entry_time = ctk.CTkEntry(self.sidebar); self.entry_time.insert(0, now.strftime("%H:%M:%S")); self.entry_time.pack(pady=5)
        
        ctk.CTkButton(self.sidebar, text="Terapkan Kustom", command=self.set_kustom).pack(pady=10)
        ctk.CTkButton(self.sidebar, text="🌅 Ke Waktu Sunset", fg_color="#F57C00", command=self.set_sunset).pack(pady=5)
        ctk.CTkButton(self.sidebar, text="🔴 Kembali Live", fg_color="#2E7D32", command=self.set_live).pack(pady=5)
        
        # Main Canvas Area
        self.main_frame = ctk.CTkFrame(self, fg_color="#050510")
        self.main_frame.pack(side="right", fill="both", expand=True, padx=10, pady=10)
        
        header = ctk.CTkFrame(self.main_frame, fg_color="#181818", corner_radius=8)
        header.pack(fill="x", padx=15, pady=10)
        ctk.CTkLabel(header, text="🔴 REAL-TIME TRACKER", font=("Segoe UI", 16, "bold"), text_color="#FF5252").pack(side="left", padx=15)
        
        self.is_anim_zoomed = False
        self.btn_anim_zoom = ctk.CTkButton(header, text="🔍 Zoom Hilal", fg_color="#673AB7", command=self.toggle_zoom)
        self.btn_anim_zoom.pack(side="right", padx=15, pady=10)
        
        self.anim_canvas = tk.Canvas(self.main_frame, bg='#030514', highlightthickness=0)
        self.anim_canvas.pack(fill="both", expand=True, padx=15, pady=(0, 15))
        self.anim_canvas.bind("<Configure>", self.draw_static_background)

    def set_live(self):
        self.anim_is_live = True
        messagebox.showinfo("Live", "Dikembalikan ke Waktu Real-Time.")
        
    def set_kustom(self):
        self.anim_is_live = False
        try:
            y, m, d = int(self.entry_y.get()), int(self.entry_m.get()), int(self.entry_d.get())
            h, mn, s = map(int, self.entry_time.get().split(":"))
            tz = float(self.entry_tz.get())
            
            dt_lokal = datetime.datetime(y, m, d, h, mn, s)
            dt_utc = dt_lokal - datetime.timedelta(hours=tz)
            import pytz
            self.anim_custom_time = self.ts.from_datetime(dt_utc.replace(tzinfo=pytz.utc))
        except Exception as e:
            messagebox.showerror("Error", f"Format tidak valid: {e}")
            self.set_live()

    def set_sunset(self):
        self.anim_is_live = False
        try:
            y, m, d = int(self.entry_y.get()), int(self.entry_m.get()), int(self.entry_d.get())
            lat, lon, tz = float(self.entry_lat.get()), float(self.entry_lon.get()), float(self.entry_tz.get())
            
            pengamat = ephem.Observer()
            pengamat.lat, pengamat.lon = str(lat), str(lon)
            pengamat.elevation, pengamat.pressure, pengamat.temp = 0, 1010, 25
            
            waktu_mulai = datetime.datetime(y, m, d, 12, 0, 0) - datetime.timedelta(hours=tz)
            pengamat.date = ephem.Date(waktu_mulai)
            waktu_sunset_utc = pengamat.next_setting(ephem.Sun()).datetime()
            
            dt_lokal = waktu_sunset_utc + datetime.timedelta(hours=tz)
            import pytz
            self.anim_custom_time = self.ts.from_datetime(waktu_sunset_utc.replace(tzinfo=pytz.utc))
            
            self.entry_time.delete(0, 'end')
            self.entry_time.insert(0, dt_lokal.strftime("%H:%M:%S"))
        except Exception as e:
            messagebox.showerror("Error", f"Gagal mencari sunset: {e}")

    def toggle_zoom(self):
        self.is_anim_zoomed = not getattr(self, 'is_anim_zoomed', False)
        if self.is_anim_zoomed:
            self.btn_anim_zoom.configure(text="🔍 Normal View", fg_color="#D32F2F")
            self.anim_canvas.delete("static") 
        else:
            self.btn_anim_zoom.configure(text="🔍 Zoom Hilal", fg_color="#673AB7")
            self.draw_static_background()

    def draw_static_background(self, event=None):
        if getattr(self, 'is_anim_zoomed', False):
            self.anim_canvas.delete("static")
            return
            
        self.anim_canvas.delete("static")
        width = self.anim_canvas.winfo_width()
        height = self.anim_canvas.winfo_height()
        if height <= 10: return
        
        horizon_y = height / 2
        pad_x = 45  
        usable_width = width - (2 * pad_x)

        self.anim_canvas.create_rectangle(0, horizon_y, width, height, fill="#0A150A", outline="", tags="static")
        self.anim_canvas.create_line(0, horizon_y, width, horizon_y, fill="#00E676", width=2, dash=(4, 2), tags="static")
        
        # Bintang
        for _ in range(150):
            x = random.randint(0, width)
            y = random.randint(0, int(horizon_y))
            color = random.choice(["#FFFFFF", "#E0F7FA", "#FFFDE7"])
            self.anim_canvas.create_oval(x-1, y-1, x+1, y+1, fill=color, outline="", tags="static")
            
        # Kompas
        kompas = {0: "U (0°)", 45: "TL (45°)", 90: "T (90°)", 135: "TG (135°)", 180: "S (180°)", 225: "BD (225°)", 270: "B (270°)", 315: "BL (315°)", 360: "U (360°)"}
        for az, label in kompas.items():
            x = pad_x + (az / 360.0) * usable_width
            self.anim_canvas.create_text(x, horizon_y + 15, text=label, fill="white", font=("Consolas", 9), tags="static")
            self.anim_canvas.create_line(x, horizon_y, x, horizon_y + 6, fill="white", tags="static")

    def update_animation(self):
        if not self.anim_running: return 
        width = self.anim_canvas.winfo_width()
        height = self.anim_canvas.winfo_height()
        if width < 10 or height < 10:
            self.after(500, self.update_animation)
            return

        self.anim_canvas.delete("dyn")
        try:
            lat, lon, tz = float(self.entry_lat.get()), float(self.entry_lon.get()), float(self.entry_tz.get())
            
            if self.anim_is_live: t_sim = self.ts.now()
            else: t_sim = self.anim_custom_time

            dt_lokal = t_sim.utc_datetime() + datetime.timedelta(hours=tz)
            
            earth, sun, moon = self.eph['earth'], self.eph['sun'], self.eph['moon']
            observer = earth + wgs84.latlon(lat, lon)

            astro_sun = observer.at(t_sim).observe(sun).apparent()
            alt_s, az_s, _ = astro_sun.altaz()
            alt_s_deg, az_s_deg = alt_s.degrees, az_s.degrees
            
            astro_moon = observer.at(t_sim).observe(moon).apparent()
            alt_m, az_m, _ = astro_moon.altaz()
            alt_m_deg, az_m_deg = alt_m.degrees, az_m.degrees
            
            elong_geo = earth.at(t_sim).observe(sun).apparent().separation_from(earth.at(t_sim).observe(moon).apparent()).degrees

            pad_x = 45
            usable_width = width - (2 * pad_x)
            horizon_y = height / 2
            
            if self.is_anim_zoomed:
                # Mode Zoom (Fokus Selisih Azimut)
                diff_az = az_m_deg - az_s_deg
                if diff_az > 180: diff_az -= 360
                elif diff_az < -180: diff_az += 360
                center_az = az_s_deg + (diff_az / 2.0)
                center_alt = (alt_s_deg + alt_m_deg) / 2.0
                
                px_per_deg_y = 60.0
                px_per_deg_x = 60.0
                horizon_y = (height / 2.0) + (center_alt * px_per_deg_y)
                
                r_sun, r_moon = 40, 30
                def get_zoomed_x(az_target):
                    d_az = az_target - center_az
                    if d_az > 180: d_az -= 360
                    elif d_az < -180: d_az += 360
                    return (width / 2.0) + (d_az * px_per_deg_x)
                    
                x_sun, x_moon = get_zoomed_x(az_s_deg), get_zoomed_x(az_m_deg)
                self.anim_canvas.create_line(0, horizon_y, width, horizon_y, fill="#00E676", width=3, tags="dyn")
            else:
                # Mode Normal (360 Derajat)
                px_per_deg_y = horizon_y / 90.0  
                px_per_deg_x = usable_width / 360.0
                r_sun = max(36, int(3.6 * px_per_deg_y))
                r_moon = max(22, int(2.2 * px_per_deg_y))
                
                x_sun = pad_x + (az_s_deg * px_per_deg_x)
                x_moon = pad_x + (az_m_deg * px_per_deg_x)

            y_sun = horizon_y - (alt_s_deg * px_per_deg_y)
            y_moon = horizon_y - (alt_m_deg * px_per_deg_y)

            # Gambar Garis Elongasi
            self.anim_canvas.create_line(x_sun, y_sun, x_moon, y_moon, fill="#00E676", dash=(4, 2), width=2, tags="dyn")

            # Menggambar Matahari (Fallback ke warna orange jika gambar gagal)
            self.anim_canvas.create_oval(x_sun-r_sun, y_sun-r_sun, x_sun+r_sun, y_sun+r_sun, fill="#FFD54F", outline="#FFEA00", width=2, tags="dyn")
            
            # Menggambar Bulan (Warna cyan redup sebagai ilustrasi hilal)
            self.anim_canvas.create_oval(x_moon-r_moon, y_moon-r_moon, x_moon+r_moon, y_moon+r_moon, fill="#E0E0E0", outline="white", width=2, tags="dyn")

            # Teks Info Koordinat
            self.anim_canvas.create_text(x_sun, y_sun + r_sun + 15, text=f"SUN\nAlt:{alt_s_deg:.1f}°", fill="#FFD54F", font=("Consolas", 10), tags="dyn")
            self.anim_canvas.create_text(x_moon, y_moon - r_moon - 15, text=f"MOON\nAlt:{alt_m_deg:.1f}°", fill="#00E5FF", font=("Consolas", 10), tags="dyn")

            # Box Kiri Atas
            box_text = f"Waktu: {dt_lokal.strftime('%d-%m-%Y %H:%M:%S')}\nElongasi Geo: {elong_geo:.2f}°"
            self.anim_canvas.create_rectangle(15, 15, 300, 75, fill="#1A1A1A", outline="#00E5FF", tags="dyn")
            self.anim_canvas.create_text(25, 25, text=box_text, fill="white", font=("Consolas", 12, "bold"), anchor="nw", tags="dyn")

        except Exception as e:
            self.anim_canvas.create_text(width/2, height/2, text=f"Updating Data... {e}", fill="gray", tags="dyn")

        self.after(1000, self.update_animation)

if __name__ == "__main__":
    app = Menu15App()
    app.mainloop()