import os, datetime
import numpy as np
import customtkinter as ctk
from tkinter import messagebox
from skyfield.api import Loader, wgs84
from skyfield import almanac

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

def get_safe_events(t_obj, y_obj):
    y_arr = np.atleast_1d(y_obj)
    if len(y_arr) == 0: return []
    events = []
    for k in range(len(y_arr)):
        try: t_val = t_obj[k]
        except Exception: t_val = t_obj
        events.append((t_val, int(y_arr[k])))
    return events

def safe_monthrange(year, month):
    is_leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
    days_in_month = [31, 29 if is_leap else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    return 0, days_in_month[month - 1]

def format_tahun_aman(year):
    return f"{year} M" if year > 0 else f"{abs(year-1)} SM"

class Menu18App(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("Planetary Times (Modul 18)")
        self.geometry("900x600")
        ctk.set_appearance_mode("Dark")
        
        self.load_obj = Loader(BASE_DIR)
        self.ephemeris_name = 'de421.bsp'
        try:
            self.eph = self.load_obj(self.ephemeris_name)
        except Exception:
            messagebox.showwarning("Peringatan", f"File {self.ephemeris_name} tidak ditemukan. Harap unduh terlebih dahulu.")
        self.ts = self.load_obj.timescale()
        
        self.setup_ui()

    def auto_switch_ephemeris(self, target_year):
        ephemeris_priority = [("de421.bsp", 1900, 2050), ("de442.bsp", 1550, 2650), ("de406.bsp", -3000, 3000)]
        best_bsp = "de421.bsp"
        for filename, min_yr, max_yr in ephemeris_priority:
            if min_yr <= target_year <= max_yr and os.path.exists(os.path.join(BASE_DIR, filename)):
                best_bsp = filename; break
        if self.ephemeris_name != best_bsp:
            try:
                self.eph = self.load_obj(best_bsp)
                self.ephemeris_name = best_bsp
            except Exception: pass

    def setup_ui(self):
        self.sidebar = ctk.CTkFrame(self, width=250)
        self.sidebar.pack(side="left", fill="y", padx=10, pady=10)
        
        now = datetime.datetime.now()
        ctk.CTkLabel(self.sidebar, text="PLANETARY TIMES", font=("Segoe UI", 16, "bold")).pack(pady=20)
        
        self.combo_target = ctk.CTkOptionMenu(self.sidebar, values=["Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"])
        self.combo_target.pack(pady=5)
        
        self.entry_y = ctk.CTkEntry(self.sidebar, placeholder_text="Tahun"); self.entry_y.insert(0, str(now.year)); self.entry_y.pack(pady=5)
        self.entry_m = ctk.CTkEntry(self.sidebar, placeholder_text="Bulan"); self.entry_m.insert(0, str(now.month)); self.entry_m.pack(pady=5)
        
        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_elev = ctk.CTkEntry(self.sidebar, placeholder_text="Elevasi (m)"); self.entry_elev.insert(0, "230"); self.entry_elev.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)
        
        ctk.CTkButton(self.sidebar, text="▶ HITUNG JADWAL", command=self.hitung).pack(pady=20)
        
        self.textbox = ctk.CTkTextbox(self, font=("Consolas", 14), wrap="none")
        self.textbox.pack(side="right", fill="both", expand=True, padx=10, pady=10)

    def hitung(self):
        self.textbox.delete("1.0", "end")
        try:
            year, month = int(self.entry_y.get()), int(self.entry_m.get())
            lat, lon = float(self.entry_lat.get()), float(self.entry_lon.get())
            elev, tz = float(self.entry_elev.get()), float(self.entry_tz.get())
            target_name = self.combo_target.get().lower()
        except ValueError:
            messagebox.showerror("Error", "Input tidak valid.")
            return

        self.auto_switch_ephemeris(year)
        target_key = f'{target_name} barycenter' if target_name != 'moon' else 'moon'
        try:
            target_obj = self.eph[target_key]
        except KeyError:
            target_obj = self.eph[target_name] # Fallback
            
        earth = self.eph['earth']
        loc = wgs84.latlon(lat, lon, elevation_m=elev)
        
        _, num_days = safe_monthrange(year, month)
        t0 = self.ts.utc(year, month, 1, -int(tz))
        t1 = self.ts.utc(year, month, num_days, 24 - int(tz))
        
        f_rs = almanac.risings_and_settings(self.eph, target_obj, loc)
        t_rs, y_rs = almanac.find_discrete(t0, t1, f_rs)
        
        report = f"[ Planetary Times: {target_name.upper()} ]\n\n"
        report += f" Bulan/Tahun : {month:02d}/{format_tahun_aman(year)}\n"
        report += f" Lokasi      : Lat {lat}, Lon {lon}, TZ UTC{'+' if tz >= 0 else ''}{tz}\n"
        report += "="*70 + "\n"
        report += " Tanggal            Terbit (Rise)          Terbenam (Set)\n"
        report += "-"*70 + "\n"
        
        # Loop setiap hari
        for d in range(1, num_days + 1):
            found_rise, found_set = "----", "----"
            if t_rs is not None:
                for t_val, y_val in get_safe_events(t_rs, y_rs):
                    t_loc = self.ts.tt_jd(t_val.tt + (tz / 24.0))
                    _, _, dl, hl, mnl, _ = t_loc.utc
                    if int(dl) == d:
                        time_str = f"{int(hl):02d}:{int(mnl):02d}"
                        if y_val == 1: found_rise = time_str
                        else: found_set = time_str
            
            date_display = f"{d:02d}/{month:02d}/{format_tahun_aman(year)}"
            report += f" {date_display:<18} {found_rise:^15}        {found_set:^15}\n"
            
        report += "="*70 + "\n* Calculated using JPL NASA Engine."
        self.textbox.insert("end", report)

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