import serial
import csv
import time
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import ttk

# Configure the serial connection to the Arduino
# NOTE: 'timeout=1' means .readline() will wait at most 1 second for a newline
arduino_serial = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
time.sleep(2)  # Allow time for the connection to establish

# Initialize counters for each sensor
data_counts = {
    "Control_Motion": 0,
    "Control_Eating": 0,
    "Experimental_Motion": 0,
    "Experimental_Eating": 0
}

# NEW: Dictionary to store the scheduled on/off times (HH:MM) for each light.
light_schedule = {
    "Control_Dim_On": "06:00",
    "Control_Dim_Off": "10:00",
    "Control_Bright_On": "10:00",
    "Control_Bright_Off": "18:00",
    "Experimental_Dim_On": "06:00",
    "Experimental_Dim_Off": "08:00",
    "Experimental_Bright_On": "08:00",
    "Experimental_Bright_Off": "20:00"
}

# Track the ON/OFF state for each light so we only send commands when changing state
light_states = {
    "Control_Dim": "OFF",
    "Control_Bright": "OFF",
    "Experimental_Dim": "OFF",
    "Experimental_Bright": "OFF"
}

# Define the CSV file name
csv_file = "mouse_activity_log.csv"

# Initialize the last serial read timestamp
last_serial_read = 0

# Initialize the next log time (on the hour)
next_log_time = datetime.now().replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)

# Initialize the CSV file
def initialize_csv():
    try:
        with open(csv_file, "x", newline="") as file:
            writer = csv.writer(file)
            writer.writerow([
                "DateTime", 
                "Control_Motion", 
                "Control_Eating", 
                "Experimental_Motion", 
                "Experimental_Eating",
                "Control_Dim_On",
                "Control_Dim_Off",
                "Control_Bright_On",
                "Control_Bright_Off",
                "Experimental_Dim_On",
                "Experimental_Dim_Off",
                "Experimental_Bright_On",
                "Experimental_Bright_Off"
            ])
    except FileExistsError:
        pass

initialize_csv()

# Read and update sensor counts
def read_and_update_counts():
    global last_serial_read
    # Only attempt a read every 0.5s
    if time.time() - last_serial_read >= 0.5:
        try:
            # Read ALL available lines in a loop
            while arduino_serial.in_waiting > 0:
                line = arduino_serial.readline().decode('utf-8', errors='ignore').strip()
                if line:
                    print(f"Received: {line}")  # Debug print
                    parts = line.split(", ")
                    for part in parts:
                        if "Control Group Motion:" in part:
                            data_counts["Control_Motion"] += int(part.split(":")[1])
                        elif "Control Group Eating:" in part:
                            data_counts["Control_Eating"] += int(part.split(":")[1])
                        elif "Experimental Group Motion:" in part:
                            data_counts["Experimental_Motion"] += int(part.split(":")[1])
                        elif "Experimental Group Eating:" in part:
                            data_counts["Experimental_Eating"] += int(part.split(":")[1])
        except (ValueError, IndexError) as e:
            print("Error processing data line:", e)

        # Update the "last time we read"
        last_serial_read = time.time()

# Log data to CSV
def log_to_csv():
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    with open(csv_file, "a", newline="") as file:
        writer = csv.writer(file)
        writer.writerow([
            timestamp,
            data_counts["Control_Motion"],
            data_counts["Control_Eating"],
            data_counts["Experimental_Motion"],
            data_counts["Experimental_Eating"],
            light_schedule["Control_Dim_On"],
            light_schedule["Control_Dim_Off"],
            light_schedule["Control_Bright_On"],
            light_schedule["Control_Bright_Off"],
            light_schedule["Experimental_Dim_On"],
            light_schedule["Experimental_Dim_Off"],
            light_schedule["Experimental_Bright_On"],
            light_schedule["Experimental_Bright_Off"]
        ])
    # Reset counts after logging
    for key in data_counts:
        data_counts[key] = 0

# Helper function to parse "HH:MM" and return (hour, minute)
def parse_time(timestr):
    try:
        hour, minute = timestr.split(":")
        return int(hour), int(minute)
    except ValueError:
        # If parsing fails, default to midnight
        return 0, 0

# Check if current time is within on->off window for a given pair of "HH:MM"
def is_light_on_now(on_str, off_str):
    now = datetime.now()
    on_hour, on_minute = parse_time(on_str)
    off_hour, off_minute = parse_time(off_str)
    
    on_time = now.replace(hour=on_hour, minute=on_minute, second=0, microsecond=0)
    off_time = now.replace(hour=off_hour, minute=off_minute, second=0, microsecond=0)
    
    if on_time < off_time:
        return on_time <= now < off_time
    else:
        # If the ON time is later than OFF time, assume crossing midnight
        return (now >= on_time) or (now < off_time)

# Light control logic (based on specific on/off times)
def manage_lights():
    # CONTROL DIM
    desired_state = "ON" if is_light_on_now(light_schedule["Control_Dim_On"],
                                           light_schedule["Control_Dim_Off"]) else "OFF"
    if light_states["Control_Dim"] != desired_state:
        light_states["Control_Dim"] = desired_state
        if desired_state == "ON":
            arduino_serial.write(b"CTRL_DIM_ON\n")
        else:
            arduino_serial.write(b"CTRL_DIM_OFF\n")

    # CONTROL BRIGHT
    desired_state = "ON" if is_light_on_now(light_schedule["Control_Bright_On"],
                                           light_schedule["Control_Bright_Off"]) else "OFF"
    if light_states["Control_Bright"] != desired_state:
        light_states["Control_Bright"] = desired_state
        if desired_state == "ON":
            arduino_serial.write(b"CTRL_BRIGHT_ON\n")
        else:
            arduino_serial.write(b"CTRL_BRIGHT_OFF\n")

    # EXPERIMENTAL DIM
    desired_state = "ON" if is_light_on_now(light_schedule["Experimental_Dim_On"],
                                           light_schedule["Experimental_Dim_Off"]) else "OFF"
    if light_states["Experimental_Dim"] != desired_state:
        light_states["Experimental_Dim"] = desired_state
        if desired_state == "ON":
            arduino_serial.write(b"EXP_DIM_ON\n")
        else:
            arduino_serial.write(b"EXP_DIM_OFF\n")

    # EXPERIMENTAL BRIGHT
    desired_state = "ON" if is_light_on_now(light_schedule["Experimental_Bright_On"],
                                           light_schedule["Experimental_Bright_Off"]) else "OFF"
    if light_states["Experimental_Bright"] != desired_state:
        light_states["Experimental_Bright"] = desired_state
        if desired_state == "ON":
            arduino_serial.write(b"EXP_BRIGHT_ON\n")
        else:
            arduino_serial.write(b"EXP_BRIGHT_OFF\n")

# GUI Update Functions
def update_ui():
    control_motion_var.set(data_counts["Control_Motion"])
    control_eating_var.set(data_counts["Control_Eating"])
    experimental_motion_var.set(data_counts["Experimental_Motion"])
    experimental_eating_var.set(data_counts["Experimental_Eating"])
    root.after(1000, update_ui)  # Update every second

# Apply new schedule
def apply_light_schedule():
    light_schedule["Control_Dim_On"] = ctrl_dim_on_var.get()
    light_schedule["Control_Dim_Off"] = ctrl_dim_off_var.get()
    light_schedule["Control_Bright_On"] = ctrl_bright_on_var.get()
    light_schedule["Control_Bright_Off"] = ctrl_bright_off_var.get()
    light_schedule["Experimental_Dim_On"] = exp_dim_on_var.get()
    light_schedule["Experimental_Dim_Off"] = exp_dim_off_var.get()
    light_schedule["Experimental_Bright_On"] = exp_bright_on_var.get()
    light_schedule["Experimental_Bright_Off"] = exp_bright_off_var.get()

    print(f"Updated light schedule: {light_schedule}")

# Test light functionality
def test_light(light):
    global last_test_command
    last_test_command = time.time()
    arduino_serial.write(f"TEST_LIGHT,{light}\n".encode('utf-8'))

# Manual log
def log_csv_now():
    log_to_csv()

# GUI Setup
root = tk.Tk()
root.title("Mouse Activity Monitor")

# Sensor Count Display
ttk.Label(root, text="Control Group Motion: ").grid(row=0, column=0, sticky="W")
control_motion_var = tk.StringVar(value="0")
ttk.Label(root, textvariable=control_motion_var).grid(row=0, column=1, sticky="W")

ttk.Label(root, text="Control Group Eating: ").grid(row=1, column=0, sticky="W")
control_eating_var = tk.StringVar(value="0")
ttk.Label(root, textvariable=control_eating_var).grid(row=1, column=1, sticky="W")

ttk.Label(root, text="Experimental Group Motion: ").grid(row=2, column=0, sticky="W")
experimental_motion_var = tk.StringVar(value="0")
ttk.Label(root, textvariable=experimental_motion_var).grid(row=2, column=1, sticky="W")

ttk.Label(root, text="Experimental Group Eating: ").grid(row=3, column=0, sticky="W")
experimental_eating_var = tk.StringVar(value="0")
ttk.Label(root, textvariable=experimental_eating_var).grid(row=3, column=1, sticky="W")

# Light Schedule Configuration
ttk.Label(root, text="Control Dim Light ON (HH:MM): ").grid(row=4, column=0, sticky="W")
ctrl_dim_on_var = tk.StringVar(value=light_schedule["Control_Dim_On"])
ttk.Entry(root, textvariable=ctrl_dim_on_var).grid(row=4, column=1, sticky="W")

ttk.Label(root, text="Control Dim Light OFF (HH:MM): ").grid(row=5, column=0, sticky="W")
ctrl_dim_off_var = tk.StringVar(value=light_schedule["Control_Dim_Off"])
ttk.Entry(root, textvariable=ctrl_dim_off_var).grid(row=5, column=1, sticky="W")

ttk.Label(root, text="Control Bright Light ON (HH:MM): ").grid(row=6, column=0, sticky="W")
ctrl_bright_on_var = tk.StringVar(value=light_schedule["Control_Bright_On"])
ttk.Entry(root, textvariable=ctrl_bright_on_var).grid(row=6, column=1, sticky="W")

ttk.Label(root, text="Control Bright Light OFF (HH:MM): ").grid(row=7, column=0, sticky="W")
ctrl_bright_off_var = tk.StringVar(value=light_schedule["Control_Bright_Off"])
ttk.Entry(root, textvariable=ctrl_bright_off_var).grid(row=7, column=1, sticky="W")

ttk.Label(root, text="Experimental Dim Light ON (HH:MM): ").grid(row=8, column=0, sticky="W")
exp_dim_on_var = tk.StringVar(value=light_schedule["Experimental_Dim_On"])
ttk.Entry(root, textvariable=exp_dim_on_var).grid(row=8, column=1, sticky="W")

ttk.Label(root, text="Experimental Dim Light OFF (HH:MM): ").grid(row=9, column=0, sticky="W")
exp_dim_off_var = tk.StringVar(value=light_schedule["Experimental_Dim_Off"])
ttk.Entry(root, textvariable=exp_dim_off_var).grid(row=9, column=1, sticky="W")

ttk.Label(root, text="Experimental Bright Light ON (HH:MM): ").grid(row=10, column=0, sticky="W")
exp_bright_on_var = tk.StringVar(value=light_schedule["Experimental_Bright_On"])
ttk.Entry(root, textvariable=exp_bright_on_var).grid(row=10, column=1, sticky="W")

ttk.Label(root, text="Experimental Bright Light OFF (HH:MM): ").grid(row=11, column=0, sticky="W")
exp_bright_off_var = tk.StringVar(value=light_schedule["Experimental_Bright_Off"])
ttk.Entry(root, textvariable=exp_bright_off_var).grid(row=11, column=1, sticky="W")

# Apply Button
apply_button = ttk.Button(root, text="Apply Light Schedule", command=apply_light_schedule)
apply_button.grid(row=12, column=0, columnspan=2)

# Test Light Buttons
ttk.Label(root, text="Test Lights: ").grid(row=13, column=0, sticky="W")
control_dim_test = ttk.Button(root, text="Test CTRL Dim", command=lambda: test_light("CTRL_DIM"))
control_dim_test.grid(row=14, column=0, sticky="W")
control_bright_test = ttk.Button(root, text="Test CTRL Bright", command=lambda: test_light("CTRL_BRIGHT"))
control_bright_test.grid(row=14, column=1, sticky="W")
exp_dim_test = ttk.Button(root, text="Test EXP Dim", command=lambda: test_light("EXP_DIM"))
exp_dim_test.grid(row=15, column=0, sticky="W")
exp_bright_test = ttk.Button(root, text="Test EXP Bright", command=lambda: test_light("EXP_BRIGHT"))
exp_bright_test.grid(row=15, column=1, sticky="W")

# Log Now Button
ttk.Button(root, text="Log CSV Now", command=log_csv_now).grid(row=16, column=0, columnspan=2)

# Main GUI Loop
def background_task():
    global next_log_time
    read_and_update_counts()
    manage_lights()  # Manage light transitions
    if datetime.now() >= next_log_time:
        log_to_csv()
        next_log_time = datetime.now().replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
    root.after(500, background_task)

# Start background task and UI update
background_task()
update_ui()
root.mainloop()
