import tkinter as tk
from tkinter import ttk

# --- Conversion functions ---
def nm_to_kgcm(tau_nm):
    return tau_nm * 10.197

# --- Torque calculation function ---
def calculate():
    # Get user inputs
    mass_total = float(mass_total_var.get())
    legs_supporting = int(legs_supporting_var.get())
    g = 9.81
    coxa_len = float(coxa_len_var.get())
    femur_len = float(femur_len_var.get())
    tibia_len = float(tibia_len_var.get())
    safety_factor = float(safety_factor_var.get())
    servo_torque_max = float(servo_torque_max_var.get())

    # Derived values
    mass_per_leg = mass_total / legs_supporting
    force_per_leg = mass_per_leg * g

    # Torques (Nm)
    tau_tibia = force_per_leg * tibia_len
    tau_femur = force_per_leg * (femur_len + tibia_len)
    tau_coxa = force_per_leg * coxa_len

    # Apply safety factor
    tau_tibia_req = tau_tibia * safety_factor
    tau_femur_req = tau_femur * safety_factor
    tau_coxa_req = tau_coxa * safety_factor

    # Convert to kg·cm
    coxa_kgcm = nm_to_kgcm(tau_coxa)
    femur_kgcm = nm_to_kgcm(tau_femur)
    tibia_kgcm = nm_to_kgcm(tau_tibia)
    coxa_kgcm_req = nm_to_kgcm(tau_coxa_req)
    femur_kgcm_req = nm_to_kgcm(tau_femur_req)
    tibia_kgcm_req = nm_to_kgcm(tau_tibia_req)

    # Update info labels
    info_label.config(
        text=(
            f"Mass per leg: {mass_per_leg:.3f} kg\n"
            f"Force per leg: {force_per_leg:.2f} N\n"
            f"Servo max torque: {servo_torque_max:.2f} kg·cm\n"
            f"Safety Factor: x{safety_factor:.2f}"
        )
    )

    # Update progress bars
    update_bar(coxa_bar, coxa_val, coxa_kgcm, servo_torque_max)
    update_bar(femur_bar, femur_val, femur_kgcm, servo_torque_max)
    update_bar(tibia_bar, tibia_val, tibia_kgcm, servo_torque_max)

    update_bar(coxa_bar_req, coxa_val_req, coxa_kgcm_req, servo_torque_max)
    update_bar(femur_bar_req, femur_val_req, femur_kgcm_req, servo_torque_max)
    update_bar(tibia_bar_req, tibia_val_req, tibia_kgcm_req, servo_torque_max)


def update_bar(bar, label, value, limit):
    ratio = min(value / limit, 1.0)
    percent = (value / limit) * 100
    bar["value"] = percent if percent <= 100 else 100
    label.config(text=f"{value:.2f} / {limit:.2f} kg·cm")
    if value <= limit:
        bar.configure(style="Green.Horizontal.TProgressbar")
    else:
        bar.configure(style="Red.Horizontal.TProgressbar")


# --- Tkinter GUI ---
root = tk.Tk()
root.title("Hexapod Torque Estimator (Live)")
root.geometry("700x700")
root.resizable(False, False)

mainframe = ttk.Frame(root, padding=10)
mainframe.pack(fill="both", expand=True)

# Progress bar styles
style = ttk.Style(root)
style.theme_use("clam")
style.configure("Green.Horizontal.TProgressbar", troughcolor="#333", background="#4CAF50")
style.configure("Red.Horizontal.TProgressbar", troughcolor="#333", background="#F44336")

# Input variables
mass_total_var = tk.DoubleVar(value=1.08)
legs_supporting_var = tk.IntVar(value=3)
coxa_len_var = tk.DoubleVar(value=0.056)
femur_len_var = tk.DoubleVar(value=0.072)
tibia_len_var = tk.DoubleVar(value=0.07)
safety_factor_var = tk.DoubleVar(value=4.5)
servo_torque_max_var = tk.DoubleVar(value=2.3)

# --- Layout ---
def add_input(label, variable, from_, to, step=0.01):
    frame = ttk.Frame(mainframe)
    frame.pack(fill="x", pady=3)
    ttk.Label(frame, text=label, width=25).pack(side="left")
    entry = ttk.Entry(frame, textvariable=variable, width=8)
    entry.pack(side="left")
    scale = ttk.Scale(frame, variable=variable, from_=from_, to=to, command=lambda e: calculate())
    scale.pack(side="left", fill="x", expand=True, padx=10)
    return entry

add_input("Total Mass [kg]", mass_total_var, 0.1, 5.0)

# Integer spinbox for legs supporting
frame = ttk.Frame(mainframe)
frame.pack(fill="x", pady=3)
ttk.Label(frame, text="Legs Supporting", width=25).pack(side="left")
spin = ttk.Spinbox(frame, from_=1, to=6, textvariable=legs_supporting_var, width=8, command=calculate)
spin.pack(side="left")

add_input("Coxa Length [m]", coxa_len_var, 0.03, 0.1)
add_input("Femur Length [m]", femur_len_var, 0.03, 0.15)
add_input("Tibia Length [m]", tibia_len_var, 0.03, 0.15)
add_input("Safety Factor", safety_factor_var, 1.0, 3.0)
add_input("Servo Torque Max [kg·cm]", servo_torque_max_var, 1.0, 5.0)

# Info label
info_label = ttk.Label(mainframe, text="", justify="left", font=("Courier", 10))
info_label.pack(pady=10)

# Function to create a labeled progress bar
def add_progress(label_text):
    frame = ttk.Frame(mainframe)
    frame.pack(fill="x", pady=3)
    ttk.Label(frame, text=label_text, width=10).pack(side="left")
    bar = ttk.Progressbar(frame, length=400, maximum=100)
    bar.pack(side="left", padx=5)
    val_label = ttk.Label(frame, width=20, anchor="w")
    val_label.pack(side="left")
    return bar, val_label

# --- Without Safety Factor ---
ttk.Label(mainframe, text="Without Safety Factor", font=("Helvetica", 11, "bold")).pack(pady=(15, 3))
coxa_bar, coxa_val = add_progress("Coxa")
femur_bar, femur_val = add_progress("Femur")
tibia_bar, tibia_val = add_progress("Tibia")

# --- With Safety Factor ---
ttk.Label(mainframe, text="With Safety Factor", font=("Helvetica", 11, "bold")).pack(pady=(15, 3))
coxa_bar_req, coxa_val_req = add_progress("Coxa")
femur_bar_req, femur_val_req = add_progress("Femur")
tibia_bar_req, tibia_val_req = add_progress("Tibia")

# Auto-update on variable changes
for var in [mass_total_var, legs_supporting_var, coxa_len_var, femur_len_var, tibia_len_var, safety_factor_var, servo_torque_max_var]:
    var.trace_add("write", lambda *args: calculate())

# Initial calculation
calculate()

root.mainloop()
