import tkinter as tk
from tkinter import ttk
import pygame
import time
import os
from tkinter import filedialog
import json
import sys
from tkinter import messagebox
import keyboard

# Getting the application path
def resource_path():
    if getattr(sys, 'frozen', False):
        application_path = os.path.dirname(sys.executable)
    else:
        application_path = os.path.dirname(os.path.abspath(__file__))
    return application_path

# Updating paths to be relative to the application directory
config_file_path = os.path.join(resource_path(), "config.txt")
settings_file_path = os.path.join(resource_path(), "settings.json")

# Initialize pygame mixer with error handling
try:
    pygame.mixer.init()
except pygame.error as e:
    print(f"Failed to initialize pygame mixer: {e}")
    messagebox.showerror("Error", f"Failed to initialize audio system: {e}")

def load_settings():
    try:
        if os.path.exists(settings_file_path):
            with open(settings_file_path, "r") as file:
                return json.load(file)
    except Exception as e:
        print(f"Error loading settings: {e}")
    return {"transparency": 0.2, "volume": 0.8}

def save_settings(settings):
    try:
        settings_dir = os.path.dirname(settings_file_path)
        if not os.path.exists(settings_dir):
            os.makedirs(settings_dir)
        with open(settings_file_path, "w") as file:
            json.dump(settings, file)
    except Exception as e:
        print(f"Error saving settings: {e}")
        messagebox.showerror("Error", f"Failed to save settings: {e}")

#Function to load audio file path from config file
def load_audio_file():
    try:
        if os.path.exists(config_file_path):
            with open(config_file_path, "r") as file:
                return file.readline().strip()
    except Exception as e:
        print(f"Error loading audio file: {e}")
    return None

def save_audio_file(audio_file_path):
    try:
        config_dir = os.path.dirname(config_file_path)
        if not os.path.exists(config_dir):
            os.makedirs(config_dir)
        with open(config_file_path, "w") as file:
            file.write(audio_file_path)
    except Exception as e:
        print(f"Error saving audio file path: {e}")
        messagebox.showerror("Error", f"Failed to save audio file path: {e}")

#Load and initialize audio
audio_file_path = load_audio_file()
try:
    if audio_file_path and os.path.exists(audio_file_path):
        pygame.mixer.music.load(audio_file_path)
    else:
        audio_file_path = filedialog.askopenfilename(
            title="Select an Audio File",
            filetypes=[("MP3 Files", "*.mp3")]
        )
        if audio_file_path:
            pygame.mixer.music.load(audio_file_path)
            save_audio_file(audio_file_path)
        else:
            print("No audio file selected.")
            messagebox.showwarning("Warning", "No audio file selected. Some features may not work.")
except Exception as e:
    print(f"Error loading audio file: {e}")
    messagebox.showerror("Error", f"Failed to load audio file: {e}")

pygame.mixer.music.set_volume(0.8)

try:
    if audio_file_path:
        length_in_s = int(pygame.mixer.Sound(audio_file_path).get_length())
        length_in_ms = length_in_s * 1000
        print(f"Song length in seconds: {length_in_s}")
except Exception as e:
    length_in_s = 0
    print(f"Could not load song length: {e}")

# Global variables
current_state = False
current_timer = None
start_x, start_y = 0, 0

def timer(seconds):
    global current_state, current_timer

    if current_state and seconds > 0:
        seconds -= 1
        converted_time = time_display_converter(seconds)
        counter_label.config(text=f"{converted_time}")
        progress_var.set(seconds)
        current_timer = root.after(1000, timer, seconds)
    elif seconds == 0:
        try:
            pygame.mixer.music.play(start=0.0)
        except Exception as e:
            print(f"Error playing sound: {e}")
            messagebox.showerror("Error", f"Failed to play sound: {e}")
        print("Time is up")
        progress_var.set(0)
        entry.grid(row=2, column=3, sticky="nesw")
    else:
        print("Timer stopped")
        progress_var.set(0)
        entry.grid(row=2, column=3, sticky="nesw")

def time_display_converter(seconds):
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    seconds = seconds % 60
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

def click_start():
    global current_state, current_timer

    try:
        # Function for checking if a timer is already running-stop it
        if current_timer is not None:
            root.after_cancel(current_timer)
            current_state = False
            pygame.mixer.music.stop()

        current_state = True
        seconds = int(entry.get()) * 60
        progress_var.set(seconds)
        progress_bar.config(maximum=seconds)
        entry.grid_forget()
        timer(seconds)
    except Exception as e:
        print(f"Error starting timer: {e}")
        messagebox.showerror("Error", f"Failed to start timer: {e}")

def click_stop():
    global current_state, current_timer
    try:
        current_state = False
        if current_timer is not None:
            root.after_cancel(current_timer)
        root.after(0, recreate_entry)
        pygame.mixer.music.stop()
    except Exception as e:
        print(f"Error stopping timer: {e}")
        messagebox.showerror("Error", f"Failed to stop timer: {e}")

def recreate_entry():
    entry.grid(row=2, column=3, sticky="nesw")

def always_on_top():
    root.attributes("-topmost", 1)
    root.update_idletasks()

# Window drag function
lastClickX = 0
lastClickY = 0

def SaveLastClickPos(event):
    global lastClickX, lastClickY
    lastClickX = event.x
    lastClickY = event.y

# Create main window
root = tk.Tk()
root.title("Timer")
root.geometry("180x70")

def on_validate(text):
    if text.isdigit() or text == "":
        return True
    return False

# Create UI elements
entry = tk.Entry(root, bg="#008000", width=3, validate="key")
entry['validatecommand'] = (entry.register(on_validate), '%S')
entry.grid(row=2, column=3, sticky="nesw")

progress_var = tk.IntVar()
style = ttk.Style()
style.theme_use('default')
style.configure("Custom.Horizontal.TProgressbar", 
                troughcolor='#c0c0c0', 
                background='#14b009', 
                thickness=20)

progress_bar = ttk.Progressbar(root, 
                              variable=progress_var, 
                              maximum=60, 
                              style="Custom.Horizontal.TProgressbar")
progress_bar.grid(row=1, column=0, columnspan=4, sticky="nesw")

options_window = None

def update_transparency(value):
    try:
        inverted_value = 1.2 - float(value)
        root.attributes('-alpha', inverted_value)
        if options_window:
            options_window.attributes('-alpha', inverted_value)
        settings = load_settings()
        settings["transparency"] = float(value)
        save_settings(settings)
    except Exception as e:
        print(f"Error updating transparency: {e}")
        messagebox.showerror("Error", f"Failed to update transparency: {e}")

def update_volume(value):
    try:
        volume = float(value)
        pygame.mixer.music.set_volume(volume)
        settings = load_settings()
        settings["volume"] = volume
        save_settings(settings)
    except Exception as e:
        print(f"Error updating volume: {e}")
        messagebox.showerror("Error", f"Failed to update volume: {e}")

def show_options():
    global options_window
    
    try:
        # Coordinates of current position of the main window
        x = root.winfo_x()
        y = root.winfo_y()
        
        options_window = tk.Toplevel(root)
        options_window.title("Options")
        options_window.geometry("250x160")
        
        # Position the options window at the same coordinates as the main window
        options_window.geometry(f"+{x}+{y}")

        settings = load_settings()

        transparency_frame = tk.Frame(options_window)
        transparency_frame.pack(pady=0)

        label = tk.Label(transparency_frame, text="Transparency:")
        label.pack(side="left", padx=(10, 5), pady=(5,0))
        
        style = ttk.Style()
        style.theme_use('aqua')
        style.configure('Custom.Horizontal.TScale', 
                       troughcolor='#c0c0c0', 
                       background='#14b009', 
                       thickness=20)

        transparency_slider = ttk.Scale(
            transparency_frame, 
            from_=0.2, 
            to=1, 
            command=update_transparency, 
            orient="horizontal", 
            style="Custom.Horizontal.TScale"
        )
        transparency_slider.pack(side="left", fill="x", expand=True, padx=(0, 10), pady=(5,0))
        transparency_slider.set(settings["transparency"])

        volume_frame = tk.Frame(options_window)
        volume_frame.pack(pady=5)

        volume_label = tk.Label(volume_frame, text="Volume:")
        volume_label.pack(side="left", padx=(10, 5))

        volume_slider = ttk.Scale(
            volume_frame,
            from_=0,
            to=1,
            command=update_volume,
            orient="horizontal",
            style="Custom.Horizontal.TScale"
        )
        volume_slider.pack(side="left", fill="x", expand=True, padx=(0, 10))
        volume_slider.set(settings["volume"])

        audio_frame = tk.Frame(options_window)
        audio_frame.pack(pady=5)
        label = tk.Label(audio_frame, text="Select an audio file:")
        label.pack(side="left", padx=(10, 5))

        browse_button = tk.Button(audio_frame, text="Browse", command=select_audio_file)
        browse_button.pack(side="left", padx=(0, 10), pady=(0,0))

        back_button = tk.Button(options_window, text="Back", command=lambda: go_back(options_window))
        back_button.pack(fill=tk.X, pady=(3, 0), padx=(30,30))

        root.withdraw()
    except Exception as e:
        print(f"Error showing options: {e}")
        messagebox.showerror("Error", f"Failed to show options window: {e}")

def select_audio_file():
    global audio_file_path
    try:
        file_path = filedialog.askopenfilename(
            title="Select Audio File", 
            filetypes=[("MP3 Files", "*.mp3")]
        )
        if file_path:
            audio_file_path = file_path
            pygame.mixer.music.load(audio_file_path)
            save_audio_file(audio_file_path)
    except Exception as e:
        print(f"Error selecting audio file: {e}")
        messagebox.showerror("Error", f"Failed to select audio file: {e}")

def go_back(window):
    try:
        window.destroy()
        root.deiconify()
        style.theme_use('default')
    except Exception as e:
        print(f"Error closing options window: {e}")
        messagebox.showerror("Error", f"Failed to close options window: {e}")

# Create buttons and widgets
start_button = tk.Button(root, text="Start", command=click_start)
stop_button = tk.Button(root, text="Stop", command=click_stop)
aot_button = tk.Button(root, text="->", command=always_on_top)
counter_label = tk.Label(root, text="Set timer to start")
options_button = tk.Button(root, text="⚙️", command=show_options)

# Configure grid
root.rowconfigure([0, 1, 2], minsize=10, weight=1)
root.columnconfigure([0, 1, 2, 3], minsize=10, weight=1)

# Place widgets in grid
entry.grid(row=2, column=3, sticky="nesw")
progress_bar.grid(row=1, column=0, columnspan=4, sticky="nesw")
stop_button.grid(row=2, column=1, sticky="nesw")
start_button.grid(row=2, column=0, sticky="nesw")
aot_button.grid(row=2, column=2, sticky="nesw")
counter_label.grid(row=0, column=0, columnspan=4, sticky="nesw")
options_button.grid(row=0, column=3, sticky="nesw")

# Load initial settings
try:
    initial_settings = load_settings()
    root.attributes('-alpha', 1.2 - initial_settings["transparency"])
    pygame.mixer.music.set_volume(initial_settings["volume"])
except Exception as e:
    print(f"Error loading initial settings: {e}")
    messagebox.showerror("Error", f"Failed to load initial settings: {e}")

# Bind events
root.bind('<Button-1>', SaveLastClickPos)
root.mainloop()