import sys
import subprocess
import json
import threading
import time
import math
from datetime import datetime, timedelta
import warnings

import customtkinter as ctk
import astropy.units as u
from astropy.time import Time
from astropy.coordinates import EarthLocation, AltAz, get_body, SkyCoord, solar_system_ephemeris, GeocentricTrueEcliptic
from tkinter import messagebox

# Abaikan peringatan IERS
warnings.filterwarnings('ignore')

# ==========================================
# KONFIGURASI TEMA GLOBAL
# ==========================================
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("dark-blue")

COLORS = {
    "bg": "#101010",            # Background lebih gelap
    "card_bg": "#1E1E1E",       # Card abu-abu gelap
    "sun_accent": "#FFD700",    # Emas cerah
    "sun_glow": "#FFF176",
    "moon_accent": "#00E5FF",   # Cyan neon
    "moon_glow": "#B3E5FC",
    "text": "#FFFFFF",
    "subtext": "#AAAAAA",
    "success": "#00C853",       # Hijau Matrix
    "danger": "#FF1744",        # Merah Alert
    "warning": "#FF9100",
    "header_bg": "#252525",
    "hijri_text": "#E040FB"     # Ungu Neon untuk Tanggal Komariyah
}

# ==========================================
# BAGIAN 1: LOGIKA ASTRONOMIS (SHARED ENGINE)
# ==========================================
class AstroEngine:
    """Kelas inti untuk perhitungan Astronomi yang digunakan oleh kedua Tab"""
    
    @staticmethod
    def calculate_positions(location, obstime):
        """Menghitung posisi Sun & Moon, Elongasi, Iluminasi & Moon Age"""
        try:
            # Frame AltAz Lokal
            frame_topo = AltAz(obstime=obstime, location=location, pressure=0*u.bar)

            results = {}
            
            # Gunakan ephemeris bawaan
            with solar_system_ephemeris.set('builtin'):
                
                # --- A. AMBIL OBJEK GCRS (Geosentris) ---
                sun_gcrs = get_body('sun', obstime)
                moon_gcrs = get_body('moon', obstime)

                # --- B. HITUNG ELONGASI ---
                elongation = sun_gcrs.separation(moon_gcrs).degree
                results['elongation'] = elongation

                # --- C. HITUNG ILUMINASI BULAN ---
                elong_rad = math.radians(elongation)
                illumination_percent = 0.5 * (1 - math.cos(elong_rad)) * 100
                results['illumination'] = illumination_percent

                # --- D. HITUNG MOON AGE (TAMBAHAN FITUR) ---
                ecliptic_frame = GeocentricTrueEcliptic(obstime=obstime)
                sun_ecl = sun_gcrs.transform_to(ecliptic_frame)
                moon_ecl = moon_gcrs.transform_to(ecliptic_frame)
                
                lon_diff = (moon_ecl.lon.degree - sun_ecl.lon.degree) % 360
                moon_age = (lon_diff / 360.0) * 29.53059
                results['moon_age'] = moon_age

                # --- E. HITUNG POSISI SUN & MOON ---
                bodies = {'sun': sun_gcrs, 'moon': moon_gcrs}

                for name, obj_gcrs in bodies.items():
                    # 1. HITUNGAN TOPOSENTRIS (Parallax ON)
                    obj_topo_raw = get_body(name, obstime, location=location)
                    pos_topo = obj_topo_raw.transform_to(frame_topo)

                    # 2. HITUNGAN GEOSENTRIS (Parallax OFF)
                    obj_geo_proxy = SkyCoord(
                        ra=obj_gcrs.ra, 
                        dec=obj_gcrs.dec, 
                        distance=1000*u.AU, 
                        frame='gcrs', 
                        obstime=obstime
                    )
                    pos_geo = obj_geo_proxy.transform_to(frame_topo)

                    results[name] = {
                        "alt_topo": pos_topo.alt.degree,
                        "alt_geo": pos_geo.alt.degree,
                        "az_topo": pos_topo.az.degree,
                        "az_geo": pos_geo.az.degree,
                        "dist": pos_topo.distance.to(u.km).value
                    }
            return results
        except Exception as e:
            print(f"Engine Error: {e}")
            return None

# ==========================================
# BAGIAN 2: LOGIKA GPS (SHARED)
# ==========================================
class GPSBackend:
    def __init__(self):
        self.location = None
        self.lat = 0
        self.lon = 0
        self.source = "Mencari..."
        self.is_ready = False

    def get_windows_gps(self):
        """Mengakses Sensor Lokasi via PowerShell"""
        ps_script = """
        Add-Type -AssemblyName System.Device
        $watcher = New-Object System.Device.Location.GeoCoordinateWatcher
        $watcher.Start()
        $start = Get-Date
        while (($watcher.Status -ne 'Ready') -and ((Get-Date) -lt $start.AddSeconds(3))) {
            Start-Sleep -Milliseconds 500
        }
        if ($watcher.Status -eq 'Ready') {
            $loc = $watcher.Position.Location
            if ($loc.IsUnknown -ne $true) {
                $result = @{lat=$loc.Latitude; lon=$loc.Longitude}
                Write-Output ($result | ConvertTo-Json)
            }
        }
        """
        try:
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            process = subprocess.run(
                ["powershell", "-Command", ps_script], 
                capture_output=True, text=True, startupinfo=si
            )
            output = process.stdout.strip()
            if not output: return None
            data = json.loads(output)
            return float(data['lat']), float(data['lon'])
        except:
            return None

    def initialize_location(self):
        gps = self.get_windows_gps()
        if gps:
            self.lat, self.lon = gps
            self.source = "GPS Satelit"
        else:
            # Fallback Jakarta
            self.lat, self.lon = -6.1754, 106.8272
            self.source = "Default (Jakarta)"
        
        self.location = EarthLocation(lat=self.lat*u.deg, lon=self.lon*u.deg, height=20*u.m)
        self.is_ready = True

# ==========================================
# BAGIAN 3: GUI - TAB 1 (REAL-TIME TRACKER)
# ==========================================
class TabTracker(ctk.CTkFrame):
    def __init__(self, master):
        super().__init__(master, fg_color="transparent")
        
        self.backend = GPSBackend()
        self.engine = AstroEngine()

        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(2, weight=1)

        # --- HEADER ---
        self.header_frame = ctk.CTkFrame(self, corner_radius=15, fg_color=COLORS["header_bg"])
        self.header_frame.grid(row=0, column=0, columnspan=2, padx=15, pady=(15, 10), sticky="ew")
        self.header_frame.columnconfigure(0, weight=1)
        self.header_frame.columnconfigure(1, weight=1)

        self.title_box = ctk.CTkFrame(self.header_frame, fg_color="transparent")
        self.title_box.grid(row=0, column=0, padx=20, pady=10, sticky="w")
        ctk.CTkLabel(self.title_box, text="ASTRO MONITOR", font=("Montserrat", 22, "bold"), text_color="white").pack(anchor="w")
        self.lbl_loc = ctk.CTkLabel(self.title_box, text="Mencari Lokasi...", font=("Consolas", 11), text_color=COLORS["subtext"])
        self.lbl_loc.pack(anchor="w")

        self.time_box = ctk.CTkFrame(self.header_frame, fg_color="transparent")
        self.time_box.grid(row=0, column=1, padx=20, pady=10, sticky="e")
        self.lbl_time = ctk.CTkLabel(self.time_box, text="--:--:--", font=("Consolas", 24, "bold"), text_color=COLORS["moon_accent"])
        self.lbl_time.pack(anchor="e")
        self.lbl_elong = ctk.CTkLabel(self.time_box, text="Elongation: --", font=("Segoe UI", 12, "bold"), text_color=COLORS["sun_accent"])
        self.lbl_elong.pack(anchor="e")

        # --- CARDS ---
        self.sun_frame = self.create_instrument_card(0, "SUN", "☀", COLORS["sun_accent"], COLORS["sun_glow"])
        self.moon_frame = self.create_instrument_card(1, "MOON", "☾", COLORS["moon_accent"], COLORS["moon_glow"], is_moon=True)

        # --- STATUS ---
        self.status_frame = ctk.CTkFrame(self, fg_color="transparent")
        self.status_frame.grid(row=3, column=0, columnspan=2, pady=10, sticky="ew")
        self.lbl_status = ctk.CTkLabel(self.status_frame, text="Initializing System...", text_color="gray", font=("Consolas", 10))
        self.lbl_status.pack()

        threading.Thread(target=self.start_backend, daemon=True).start()
        self.update_ui()

    def create_instrument_card(self, col, title, icon, accent_color, glow_color, is_moon=False):
        frame = ctk.CTkFrame(self, corner_radius=20, fg_color=COLORS["card_bg"], border_width=1, border_color="#333")
        frame.grid(row=1, column=col, padx=10, pady=10, sticky="nsew")
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)

        header = ctk.CTkFrame(frame, fg_color="transparent")
        header.grid(row=0, column=0, columnspan=2, pady=(15, 5), padx=15, sticky="ew")
        ctk.CTkLabel(header, text=icon, font=("Segoe UI", 24), text_color=accent_color).pack(side="left")
        ctk.CTkLabel(header, text=f"  {title}", font=("Montserrat", 16, "bold"), text_color="white").pack(side="left")
        
        status_pill = ctk.CTkLabel(header, text="...", font=("Segoe UI", 10, "bold"), text_color="white", fg_color="#333", corner_radius=8, width=80, height=24)
        status_pill.pack(side="right")

        alt_frame = ctk.CTkFrame(frame, fg_color="transparent")
        alt_frame.grid(row=1, column=0, columnspan=2, pady=(10, 5))
        alt_val = ctk.CTkLabel(alt_frame, text="--°", font=("Consolas", 48, "bold"), text_color=accent_color)
        alt_val.pack()
        ctk.CTkLabel(alt_frame, text="TOPO ALTITUDE", font=("Segoe UI", 9, "bold"), text_color="gray").pack()

        prog_bar = ctk.CTkProgressBar(frame, height=6, progress_color=accent_color, fg_color="#333")
        prog_bar.set(0)
        prog_bar.grid(row=2, column=0, columnspan=2, padx=20, pady=(5, 15), sticky="ew")

        data_grid = ctk.CTkFrame(frame, fg_color="#252525", corner_radius=10)
        data_grid.grid(row=3, column=0, columnspan=2, padx=15, pady=(0, 15), sticky="ew")
        data_grid.columnconfigure((0, 1), weight=1)

        labels = {}
        def add_data_row(parent, r, label_txt, key, val_color="white"):
            ctk.CTkLabel(parent, text=label_txt, font=("Segoe UI", 12, "bold"), text_color="gray").grid(row=r, column=0, padx=15, pady=3, sticky="w")
            lbl_val = ctk.CTkLabel(parent, text="--", font=("Consolas", 20, "bold"), text_color=val_color)
            lbl_val.grid(row=r, column=1, padx=15, pady=3, sticky="e")
            labels[key] = lbl_val

        add_data_row(data_grid, 0, "AZIMUTH", "az")
        add_data_row(data_grid, 1, "GEO ALTITUDE", "geo_alt", val_color=COLORS["subtext"])
        add_data_row(data_grid, 2, "DISTANCE (km)", "dist")

        if is_moon:
            ctk.CTkFrame(data_grid, height=1, fg_color="#444").grid(row=3, column=0, columnspan=2, sticky="ew", padx=5, pady=5)
            add_data_row(data_grid, 4, "ILLUMINATION", "illum", val_color=COLORS["moon_accent"])
            add_data_row(data_grid, 5, "MOON AGE", "age", val_color="#E1BEE7")
            add_data_row(data_grid, 6, "EST. KOMARIYAH", "hijri", val_color=COLORS["hijri_text"])

        labels['main_alt'] = alt_val
        labels['status'] = status_pill
        labels['bar'] = prog_bar
        frame.labels = labels
        return frame

    def start_backend(self):
        self.backend.initialize_location()

    def update_ui(self):
        current_time = datetime.now().strftime("%H:%M:%S")
        if self.backend.is_ready:
            self.lbl_loc.configure(text=f"📍 {self.backend.lat:.4f}, {self.backend.lon:.4f} | {datetime.now().strftime('%d %b %Y')}")
            try:
                now = Time.now()
                data = self.engine.calculate_positions(self.backend.location, now)
                if data:
                    self.lbl_elong.configure(text=f"∠ Elongation: {data['elongation']:.2f}°")
                    self.update_card(self.sun_frame, data['sun'])
                    self.update_card(self.moon_frame, data['moon'])
                    
                    self.moon_frame.labels['illum'].configure(text=f"{data.get('illumination', 0):.1f}%")
                    age = data.get('moon_age', 0)
                    self.moon_frame.labels['age'].configure(text=f"{age:.2f} d")
                    
                    komariyah = int(age)
                    self.moon_frame.labels['hijri'].configure(text="New Moon" if komariyah == 0 else f"Tgl {komariyah}")
                    self.lbl_status.configure(text=f"Sync: {now.iso} (UTC) | Source: {self.backend.source}")
            except Exception as e:
                self.lbl_status.configure(text=f"Error: {e}")
        else:
            self.lbl_status.configure(text="Mencari sinyal GPS...")
        
        self.lbl_time.configure(text=current_time)
        self.after(500, self.update_ui)

    def update_card(self, frame, data):
        alt = data['alt_topo']
        frame.labels['main_alt'].configure(text=f"{alt:.2f}°")
        frame.labels['az'].configure(text=f"{data['az_topo']:.2f}°")
        frame.labels['geo_alt'].configure(text=f"{data['alt_geo']:.2f}°")
        frame.labels['dist'].configure(text=f"{data['dist']:,.0f}")
        frame.labels['bar'].set(max(0, min(1, alt / 90)))
        
        if alt > 0: frame.labels['status'].configure(text="VISIBLE", fg_color=COLORS["success"])
        elif alt > -18: frame.labels['status'].configure(text="TWILIGHT", fg_color=COLORS["warning"])
        else: frame.labels['status'].configure(text="SET", fg_color=COLORS["danger"])

# ==========================================
# BAGIAN 4: GUI - TAB 2 (MANUAL ALASKA + 2 BUTTONS)
# ==========================================
class TabAlaska(ctk.CTkFrame):
    def __init__(self, master):
        super().__init__(master, fg_color="transparent")
        self.engine = AstroEngine()
        self.gps_backend = GPSBackend()

        # Konfigurasi Default Data Alaska
        self.ALASKA_DEFAULTS = {
            "Latitude": "56.8135",
            "Longitude": "-158.8622",
            "Tanggal (YYYY-MM-DD)": "2026-02-17",
            "Waktu (HH:MM:SS)": "16:42:21",
            "UTC Offset": "-11"
        }

        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=3)
        self.grid_rowconfigure(0, weight=1)

        self.setup_left_panel()
        self.setup_right_panel()

    def setup_left_panel(self):
        input_frame = ctk.CTkFrame(self, corner_radius=0, fg_color=COLORS["card_bg"])
        input_frame.grid(row=0, column=0, sticky="nsew")

        ctk.CTkLabel(input_frame, text="MANUAL INPUT", font=("Segoe UI", 16, "bold")).pack(pady=(20, 10))

        self.entries = {}
        for lbl_txt, val in self.ALASKA_DEFAULTS.items():
            ctk.CTkLabel(input_frame, text=lbl_txt, anchor="w", font=("Arial", 11)).pack(fill="x", padx=15, pady=(5,0))
            ent = ctk.CTkEntry(input_frame, height=30)
            ent.insert(0, val)
            ent.pack(fill="x", padx=15, pady=(0,5))
            self.entries[lbl_txt] = ent

        # --- CONTAINER TOMBOL ISI DATA ---
        btn_box = ctk.CTkFrame(input_frame, fg_color="transparent")
        btn_box.pack(fill="x", padx=10, pady=(20, 5))

        # Tombol 1: Real Time (Kiri)
        self.btn_realtime = ctk.CTkButton(btn_box, text="📍 REAL TIME", 
                                          command=self.fill_realtime_data, 
                                          fg_color=COLORS["success"], width=100)
        self.btn_realtime.pack(side="left", padx=5, expand=True, fill="x")

        # Tombol 2: Default Alaska (Kanan)
        self.btn_default = ctk.CTkButton(btn_box, text="❄ DEFAULT", 
                                         command=self.fill_default_data, 
                                         fg_color="#546E7A", width=100)
        self.btn_default.pack(side="right", padx=5, expand=True, fill="x")

        # Label Status GPS/Reset
        self.lbl_gps_status = ctk.CTkLabel(input_frame, text="", font=("Arial", 10), text_color="gray")
        self.lbl_gps_status.pack(pady=2)

        # Tombol Hitung (Bawah)
        ctk.CTkButton(input_frame, text="HITUNG POSISI", command=self.on_calculate, 
                      fg_color=COLORS["moon_accent"], text_color="black", height=40).pack(padx=15, pady=(10, 20), fill="x")

    def setup_right_panel(self):
        output_frame = ctk.CTkFrame(self, fg_color="transparent")
        output_frame.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)
        
        self.lbl_result_title = ctk.CTkLabel(output_frame, text="HASIL KALKULASI", font=("Segoe UI", 20, "bold"))
        self.lbl_result_title.pack(pady=(10, 5))
        self.lbl_result_time = ctk.CTkLabel(output_frame, text="-- Menunggu Input --", font=("Consolas", 12), text_color="gray")
        self.lbl_result_time.pack(pady=(0, 15))

        self.grid_frame = ctk.CTkFrame(output_frame, fg_color="transparent")
        self.grid_frame.pack()

        headers = ["OBJEK", "ALT (TOPO)", "ALT (GEO)", "AZIMUTH", "STATUS"]
        for i, h in enumerate(headers):
            ctk.CTkLabel(self.grid_frame, text=h, font=("Segoe UI", 13, "bold"), 
                         width=140, height=40, fg_color=COLORS["header_bg"], corner_radius=5).grid(row=0, column=i, padx=3, pady=3)

        self.cells = {}
        for row, name in enumerate(["sun", "moon"], start=1):
            disp_name = "MATAHARI" if name == "sun" else "BULAN"
            name_color = COLORS["sun_accent"] if name == "sun" else COLORS["moon_accent"]
            ctk.CTkLabel(self.grid_frame, text=disp_name, font=("Consolas", 14, "bold"), 
                         text_color=name_color, width=140, height=50, fg_color="#3a3a3a", corner_radius=5).grid(row=row, column=0, padx=3, pady=3)
            for col, key in enumerate(["alt_topo", "alt_geo", "az", "stat"], start=1):
                lbl = ctk.CTkLabel(self.grid_frame, text="--", font=("Consolas", 20, "bold"), 
                                   width=140, height=50, fg_color="#3a3a3a", corner_radius=5)
                lbl.grid(row=row, column=col, padx=3, pady=3)
                self.cells[f"{name}_{key}"] = lbl

        self.elong_frame = ctk.CTkFrame(output_frame, fg_color=COLORS["card_bg"])
        self.elong_frame.pack(pady=20, fill="x", padx=20)
        for i in range(4): self.elong_frame.columnconfigure(i, weight=1)

        self.create_stat_box(0, "ELONGASI", "elong", "#FFA726")
        self.create_stat_box(1, "ILUMINASI", "illum", "#4FC3F7")
        self.create_stat_box(2, "UMUR BULAN", "age", "#E1BEE7")
        self.create_stat_box(3, "EST. KOMARIYAH", "hijri", COLORS["hijri_text"])

    def create_stat_box(self, col, title, suffix, color):
        ctk.CTkLabel(self.elong_frame, text=title, font=("Arial", 12, "bold"), text_color="gray").grid(row=0, column=col, pady=(15,0))
        lbl = ctk.CTkLabel(self.elong_frame, text="--", font=("Consolas", 26, "bold"), text_color=color)
        lbl.grid(row=1, column=col, pady=(0,15))
        setattr(self, f"lbl_{suffix}_val", lbl)

    def fill_realtime_data(self):
        self.btn_realtime.configure(state="disabled", text="Scan GPS...")
        self.lbl_gps_status.configure(text="Mendeteksi...", text_color=COLORS["warning"])
        
        now = datetime.now()
        utc_offset_hr = int(now.astimezone().utcoffset().total_seconds() / 3600)
        
        self.set_entry("Tanggal (YYYY-MM-DD)", now.strftime("%Y-%m-%d"))
        self.set_entry("Waktu (HH:MM:SS)", now.strftime("%H:%M:%S"))
        self.set_entry("UTC Offset", str(utc_offset_hr))

        threading.Thread(target=self._fetch_gps_thread, daemon=True).start()

    def fill_default_data(self):
        """Reset input ke data asli Alaska"""
        for key, val in self.ALASKA_DEFAULTS.items():
            self.set_entry(key, val)
        self.lbl_gps_status.configure(text="Data di-reset ke Default (Alaska)", text_color=COLORS["moon_accent"])

    def _fetch_gps_thread(self):
        gps_data = self.gps_backend.get_windows_gps()
        lat, lon = gps_data if gps_data else (-6.1754, 106.8272)
        msg = "Lokasi: GPS Satelit" if gps_data else "GPS Gagal (Pakai Default JKT)"
        col = COLORS["success"] if gps_data else COLORS["warning"]
        
        self.after(0, lambda: self._update_gps_ui(lat, lon, msg, col))

    def _update_gps_ui(self, lat, lon, msg, color):
        self.set_entry("Latitude", str(lat))
        self.set_entry("Longitude", str(lon))
        self.lbl_gps_status.configure(text=msg, text_color=color)
        self.btn_realtime.configure(state="normal", text="📍 REAL TIME")

    def set_entry(self, key, value):
        self.entries[key].delete(0, "end")
        self.entries[key].insert(0, value)

    def on_calculate(self):
        try:
            lat = float(self.entries["Latitude"].get())
            lon = float(self.entries["Longitude"].get())
            date_s = self.entries["Tanggal (YYYY-MM-DD)"].get()
            time_s = self.entries["Waktu (HH:MM:SS)"].get()
            utc_off = float(self.entries["UTC Offset"].get())

            dt = datetime.strptime(f"{date_s} {time_s}", "%Y-%m-%d %H:%M:%S")
            astro_time = Time(dt - timedelta(hours=utc_off))
            loc = EarthLocation(lat=lat*u.deg, lon=lon*u.deg, height=0*u.m)
            data = self.engine.calculate_positions(loc, astro_time)
            
            if data:
                self.lbl_result_time.configure(text=f"Ref: {dt} (UTC{'+' if utc_off>=0 else ''}{utc_off})")
                self.lbl_elong_val.configure(text=f"{data['elongation']:.4f}°")
                self.lbl_illum_val.configure(text=f"{data.get('illumination', 0):.1f}%")
                
                age = data.get('moon_age', 0)
                self.lbl_age_val.configure(text=f"{age:.2f} hr")
                self.lbl_hijri_val.configure(text="New Moon" if int(age) == 0 else f"Tgl {int(age)}")
                
                for name in ['sun', 'moon']:
                    d = data[name]
                    self.cells[f"{name}_alt_topo"].configure(text=f"{d['alt_topo']:.4f}°")
                    self.cells[f"{name}_alt_geo"].configure(text=f"{d['alt_geo']:.4f}°", text_color=COLORS["subtext"])
                    self.cells[f"{name}_az"].configure(text=f"{d['az_topo']:.4f}°")
                    
                    stat_lbl = self.cells[f"{name}_stat"]
                    if d['alt_topo'] > 0: stat_lbl.configure(text="TERLIHAT", text_color=COLORS["success"])
                    elif d['alt_topo'] > -18: stat_lbl.configure(text="TWILIGHT", text_color=COLORS["warning"])
                    else: stat_lbl.configure(text="TERBENAM", text_color=COLORS["danger"])
        except Exception as e:
            messagebox.showerror("Error", f"Input error: {e}")

# ==========================================
# MAIN APP
# ==========================================
class IntegratedAstroApp(ctk.CTk):
    def __init__(self):
        super().__init__()
        self.title("Integrated Astro Tracker Pro")
        self.geometry("900x650")

        self.tabview = ctk.CTkTabview(self, width=880, height=630, fg_color=COLORS["bg"], 
                                     segmented_button_selected_color=COLORS["moon_accent"],
                                     segmented_button_selected_hover_color=COLORS["moon_glow"],
                                     segmented_button_unselected_color=COLORS["card_bg"])
        self.tabview.pack(padx=10, pady=10, expand=True, fill="both")
        self.tabview.add("Astro Tracker")
        self.tabview.add("Astro Alaska")

        TabTracker(self.tabview.tab("Astro Tracker")).pack(expand=True, fill="both")
        TabAlaska(self.tabview.tab("Astro Alaska")).pack(expand=True, fill="both")

if __name__ == "__main__":
    app = IntegratedAstroApp()
    app.mainloop()