import tkinter as tk
from tkinter import ttk
import math
from datetime import timedelta
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from ttkthemes import ThemedStyle  # Import the themed style from ttkthemes
import matplotlib.ticker as ticker
from tkinter import font
import customtkinter as ctk


# Main Application Logic

def create_tab(notebook):
    tab = ttk.Frame(notebook)
    notebook.add(tab)
    return tab

root = ctk.CTk()
root.title("HRV LHD CPT Tool")
available_fonts = font.families()


# Create a themed style
style = ThemedStyle(root)
style.set_theme("clearlooks")  # You can choose a theme you prefer

# Create a notebook with tabs on the left side using the themed style
tabControl = ttk.Notebook(root, height=480, width=495)

def conf(event):
    tabControl.config(height=root.winfo_height(), width=root.winfo_width()-145)

root.bind("<Configure>", conf)

# Create tab frames
tab1 = ctk.CTkFrame(tabControl)
tab2 = ctk.CTkFrame(tabControl)
tab3 = ctk.CTkFrame(tabControl)
                 
# Add tabs to the notebook
tabControl.add(tab1, text='Productivity')
tabControl.add(tab2, text='TCO')
tabControl.add(tab3, text='CPT')

# Pack the notebook
tabControl.pack(expand=1, fill=tk.BOTH)



def rgb_to_hex(r, g, b):
    return f'#{r:02x}{g:02x}{b:02x}'
bg_color = rgb_to_hex(255, 205, 17)















#Initialization of core data===============================================================


models_data = {
    "Caterpillar LHD models": {
        "R1300G": ["DB 3.4", "DB 2.8", "DB 2.5", "DB 3.1"],
        "R1600H": ["DB 5.9", "DB 4.8", "DB 4.2", "DB 5.6"],
        "R1700": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5", "DB 8"],
        "R1700XE": ["DB 5.7", "DB 6.1", "DB 6.6", "DB 7.5"],
        "R1700G": ["DB 4.6", "DB 5", "DB 6.6", "DB 5.7", "DB 7.3", "DB 8.8"],
        "R2900G": ["DB 8.9", "DB 7.2", "DB 6.3", "DB 8.3"],
        "R2900": ["DB 6.3", "DB 7.2", "DB 8.3", "DB 8.9"],
        "R2900XE": ["DB 7.4", "DB 8.6", "DB 9.2", "DB 9.8"],
        "R3000H": ["DB 10.5", "DB 8.9", "DB 9.5"]
    },
    "Sandvik LHD models": {
        "SANDVIK LH517i": ["DB 7", "DB 7.6", "DB 8.6", "DB 9.1", "DB 8.4"],
        "SANDVIK LH621i": ["DB 8.0", "DB 9.0", "DB 10.7", "DB 11.2"],
        "SANDVIK LH515i": ["DB 6.3", "DB 6.8", "DB 7.5"],
        "SANDVIK LH514": ["DB 6.2", "DB 7", "DB 5.4"],
        "Toro™ LH625iE": ["DB 10"],
        "Sandvik LH514E": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7"],
        "Toro™ LH514BE": ["DB 4.6", "DB 5", "DB 5.4", "DB 6.2", "DB 7", "DB 7.5"],
        "Sandvik LH409E": ["DB 3.8", "DB 4.3", "DB 4.6"]
    },
    "Epiroc LHD models": {
        "Epiroc ST14 SG": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
        "Epiroc ST18 SG": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"],
        "Epiroc ST14": ["DB 4.7", "DB 5", "DB 5.4", "DB 5.8", "DB 6.4", "DB 7.0", "DB 7.8"],
        "Epiroc ST18 S": ["DB 9.7", "DB 8.8", "DB 7.9", "DB 7.3", "DB 6.7", "DB 6.3"]
    },
    "Komatsu LHD models": {
        "WX18H": ["DB 8.2", "DB 9.2", "DB 10", "DB 11.2"],
        "WX22H": ["DB 10", "DB 11", "DB 12.2", "DB 13.8"]
    }
}

payloads = {
    "R1300G": 6800,
    "R1600H": 10200,
    "R1700": 15000,
    "R1700XE": 15000,
    "R1700G": 12500,
    "R2900G": 17200,
    "R2900": 17200,
    "R2900XE": 18500,
    "R3000H": 20000,
    "SANDVIK LH517i": 17200,
    "SANDVIK LH621i": 21000,
    "SANDVIK LH515i": 15000,
    "SANDVIK LH514": 14000,
    "Toro™ LH625iE": 25000,
    "Sandvik LH514E": 14000,
    "Toro™ LH514BE": 14000,
    "Sandvik LH409E": 9600,
    "Epiroc ST14 SG": 14000,
    "Epiroc ST18 SG": 17500,
    "Epiroc ST14": 14000,
    "Epiroc ST18 S": 17500,
    "WX18H": 18000,
    "WX22H": 22000
}

fuel_rates = {
    "R1300G": {"Low": "No Data", "Medium": "No Data", "High": "No Data"},
    "R1600H": {"Low": 15.7 , "Medium": 20.9, "High": 26.1},
    "R1700": {"Low": 20.2, "Medium": 26.9, "High": 33.6},
    "R1700XE": {"Low": 0, "Medium": 0, "High": 0},
    "R1700G": {"Low": 19.3, "Medium": 25.7, "High": 32.2},
    "R2900G": {"Low": 23, "Medium": 31.8, "High": 39.8},
    "R2900": {"Low": 24, "Medium": 31.8, "High": 39.8},
    "R2900XE": {"Low": 16.56, "Medium": 21.942, "High": 27.462},
    "R3000H": {"Low": 23, "Medium": 30.6, "High": 38.3},
    "SANDVIK LH517i": {"Low": 21, "Medium": 28, "High": 35},
    "SANDVIK LH621i": {"Low": 27.6, "Medium": 36.8, "High": 46},
    "SANDVIK LH515i": {"Low": 21.75, "Medium": 29, "High": 36.25},
    "SANDVIK LH514": {"Low": 24.75, "Medium": 33, "High": 41.25},
    "Toro™ LH625iE": {"Low": 0, "Medium": 0, "High": 0},
    "Sandvik LH514E": {"Low": 0, "Medium": 0, "High": 0},
    "Toro™ LH514BE": {"Low": 0, "Medium": 0, "High": 0},
    "Sandvik LH409E": {"Low": 0, "Medium": 0, "High": 0},
    "Epiroc ST14 SG": {"Low": 0, "Medium": 0, "High": 0},
    "Epiroc ST18 SG": {"Low": 0, "Medium": 0, "High": 0},
    "Epiroc ST14": {"Low": 20.2, "Medium": 26.9, "High": 33.6},
    "Epiroc ST18 S": {"Low": 23, "Medium": 30.6, "High": 38.3},
    "WX18H": {"Low": 24, "Medium": 32, "High": 40},
    "WX22H": {"Low": 27, "Medium": 36, "High": 45}
}


# Helper functions


def set_widget_bg_color(widget, bg_color, fg_color="black"):
    widget_class = widget.winfo_class()
    
    if widget_class == "Label":
        widget.configure(bg_color=bg_color, fg=fg_color)
    elif widget_class == "Frame" or widget_class == "Toplevel":
        widget.configure(bg_color=bg_color)
    elif widget_class == "Text":
        widget.configure(bg_color=bg_color, fg=fg_color, insertbackground=fg_color)
    elif widget_class == "Entry":
        widget.configure(bg_color=bg_color, fg=fg_color)
    
    for child in widget.winfo_children():
        set_widget_bg_color(child, bg_color, fg_color)



def flash_red(widget, flash_count=5, interval=500):
    original_color = widget.cget("foreground")

    if flash_count % 2 == 0:
        widget.config(foreground=original_color)
    else:
        widget.config(foreground="red")

    if flash_count > 1:
        widget.after(interval, flash_red, widget, flash_count - 1, interval)

def on_group_selected(col):
    selected_group = group_vars[col].get()
    model_dropdowns[col]['values'] = list(models_data[selected_group].keys())
    model_vars[col].set("")
    buckets_dropdowns[col]['values'] = []

def db_value_to_float(db_string):
    """Convert a "DB X.X" string to a float value."""
    return float(db_string.split(" ")[1])


def on_model_selected(col):
    selected_group = group_vars[col].get()
    selected_model = model_vars[col].get()
    if selected_model != "":
        bucket_values = models_data[selected_group][selected_model]
        buckets_dropdowns[col]['values'] = bucket_values
        numeric_bucket_values = [db_value_to_float(value) for value in bucket_values]
        payload_labels[col].configure(text=f"{payloads.get(selected_model, 'N/A')} kg")
        update_model_travel_times(col)
        update_plot2()


def calculate_payload(col):
    # Retrieve and convert the bucket value
    selected_bucket_value = buckets_dropdowns[col].get()
    bucket_value_float = db_value_to_float(selected_bucket_value)
    print(f"Selected Bucket Value: {selected_bucket_value}")
    print(f"Bucket Value as Float: {bucket_value_float}")
    selected_model = model_vars[col].get()
    # Retrieve and convert entry values
    muck_swell_value = float(muck_swell[col].get())
    muck_density_value = float(muck_density[col].get())
    bucket_fill_factor_value = float(bucket_fill_factor[col].get())
    rated_payload = payloads.get(selected_model, 0)  # Get the payload from dictionary or default to 0 if not found

    print(f"Muck Swell Value: {muck_swell_value}")
    print(f"Muck Density Value: {muck_density_value}")
    print(f"Bucket Fill Factor Value: {bucket_fill_factor_value}")

    # Calculate payload
    payload = bucket_value_float / (1 + muck_swell_value/100) * muck_density_value * (bucket_fill_factor_value/100)
    print(f"Calculated Payload: {payload}")
    # Update the payload label for the column
    formatted_payload = f"{payload:.2f}"
    
 # Get the selected machine type
    selected_machine = model_vars[col].get()  # Assuming 'machine_dropdowns' contains the selected machine for each column

    # Check if the calculated payload (multiplied by 1000) exceeds the machine's payload
    if selected_machine in payloads and (payload * 1000) > payloads[selected_machine]:
        payload_labels[col].config(text=f"Exceeds Rated Payload of {rated_payload/1000:.2f} tonnes", bg="red")
    else:
        payload_labels[col].config(text=formatted_payload, bg='#98FB98')  # Assuming '#98FB98' is the normal background color

    update_plot2()


def format_duration(seconds):
    duration = timedelta(seconds=seconds)
    hours, remainder = divmod(duration.seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    return "{:02}:{:02}:{:02}".format(int(duration.days * 24 + hours), minutes, seconds)

stored_value = None
def store_selected_value(event, combobox):
    grad_sel = combobox.get()
    global stored_value
    stored_value = grad_sel
    print(f"Gradient: '{stored_value}'")





def calculate_travel_time(travel_distance, selected_model, grad_sel):
    if selected_model =="R2900XE":
        if grad_sel =="0%":
            travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )    
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            ) 
        
    elif selected_model =="R2900":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.828072838
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.760515021
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.772454261
    
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.936216111
 
    elif selected_model =="R3000H":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.949165402
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.766094421
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.778708952
    
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.941082099
    elif selected_model =="R2900G":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.815781487
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.840772532
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.844383201
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.039375059
    elif selected_model =="R1700G":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.845675266
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.895708155
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.906930104
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.094847323
    elif selected_model =="R1700XE":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.546282246
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.86695279
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.875656653
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.070517382      
    elif selected_model =="R1700":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.846130501
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.081974249
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.080185028
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.39459219
    elif selected_model =="R1600H":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.912898331
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.212875536
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.268451208
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.508456312    
    elif selected_model =="R1300G":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.804704097
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.817167382
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.829997413
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.987795585
    elif selected_model =="SANDVIK LH517i":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.893778452
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.931330472
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.013259841
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.206765049      
    elif selected_model =="SANDVIK LH621i":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.790591806
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.836909871
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.906930104
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.274888883
    elif selected_model =="SANDVIK LH515i":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.886191199
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.939914163
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.025769222
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.206765049        
    elif selected_model =="SANDVIK LH514":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.983308042
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.935622318
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.013259841
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.216497026
    elif selected_model =="Toro™ LH625iE":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.55538695
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.493562232
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.544158063
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.642310429 
    elif selected_model =="Sandvik LH514E":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.634294385
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.536480687
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.569176824
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.76882612
    elif selected_model =="Toro™ LH514BE":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.640364188
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.274678112
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.407305334
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.664167931
    elif selected_model =="Sandvik LH409E":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.361153263
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.635193133
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.713034703
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.944001692
    elif selected_model =="Epiroc ST14 SG":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.902883156
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.034334764
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.094570816
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.245692954
    elif selected_model =="Epiroc ST18 SG":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.957511381
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.995708155
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.082061435
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.294352835
    elif selected_model =="Epiroc ST14":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.902883156
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.034334764
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.094570816
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.245692954
    elif selected_model =="Epiroc ST18 S":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.819423369
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/1.072961373
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/1.125844268
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/1.167837145
    elif selected_model =="WX18H":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.9
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.9
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.9
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.9
    elif selected_model =="WX22H":
        if grad_sel == "0%":
                travel_time = (
                (0.00185 * travel_distance + 0.00317 * (0.7717 + 4.004 * math.log(travel_distance)) + 0.0476) +
                (0.00184 * travel_distance + 0.00183 * (2.2157 + 3.8958 * math.log(travel_distance)) + 0.05467)
            )/0.85
        elif grad_sel == "10%":
            travel_time = (
                (0.005 * travel_distance + 0.0006 * (7.5048 + 0.6049 * math.log(travel_distance)) + 0.03449) +
                (0.00179 * travel_distance + 0.0012 * (2.067 + 4.0489 * math.log(travel_distance)) + 0.06085)
            )/0.85
        elif grad_sel == "15%":
            travel_time = (
                (0.00702 * travel_distance-0.00063 * (6.7281+0.2472 * math.log(travel_distance)) + 0.03387) +
                (0.00179 * travel_distance + 0.00127 * (0.5172+4.227 * math.log(travel_distance)) + 0.07385)
            )/0.85
        elif grad_sel == "20%":
            travel_time = (
                (0.00919 * travel_distance+0.00391* (5.6793+0.1159 * math.log(travel_distance)) -0.00334) +
                (0.00179 * travel_distance + 0.00225 * (-2.0806+4.5094 * math.log(travel_distance)) + 0.0815)
            )/0.85
    return travel_time





def update_model_travel_times(col):
    raw_distance = travel_distance_entries[col].get()
    selected_model = model_vars[col].get().strip()
    grad_sel = gradient_dropdowns[col].get()

    load_time_empty = not load_time_entries[col].get().strip()
    dump_time_empty = not dump_time_entries[col].get().strip()
    delay_time_empty = not delay_time_entries[col].get().strip()

    if not raw_distance.strip():
        if load_time_empty and dump_time_empty and delay_time_empty:
            model_travel_time_labels[col].config(text="Input Required")
  
        else:
            model_travel_time_labels[col].config(text="")
            # Reset the color to the original color if it was flashed
            model_travel_time_labels[col].config(foreground="black")
    else:
        try:
            travel_distance = float(raw_distance)
            travel_time = calculate_travel_time(travel_distance, selected_model, grad_sel)
            formatted_travel_time = format_duration(travel_time * 60)   
            model_travel_time_labels[col].config(text=formatted_travel_time)
        except ValueError:
            model_travel_time_labels[col].config(text="Invalid Distance")
            flash_red(model_travel_time_labels[col])



def calculate_cycle_time(col, update_label=True):
    try:
        # Fetching the selected model and gradient value
        selected_model = model_vars[col].get().strip()
        grad_sel = gradient_dropdowns[col].get()


        
        travel_distance = float(travel_distance_entries[col].get())
        travel_time = calculate_travel_time(travel_distance, selected_model, grad_sel)
        
        load_time = float(load_time_entries[col].get())
        dump_time = float(dump_time_entries[col].get())
        delay_time = float(delay_time_entries[col].get())
        
        # Calculate total cycle time in hours
        cycle_time = travel_time + load_time/60 + dump_time/60 + delay_time/60 
        
        # Convert cycle time from hours to seconds
        seconds_total = cycle_time * 60
        
        # Update the label if necessary
        if update_label:
            formatted_cycle_time = format_duration(seconds_total)
            cycle_time_labels[col].config(text=formatted_cycle_time)
        
        return seconds_total

    except ValueError:
        if update_label:
            cycle_time_labels[col].config(text="Input Required")
        return 0


    
def update_trips_per_day(col):
    group = group_vars[col].get()
    model = model_vars[col].get()

    print(f"update_trips_per_day called with col={col}")

    availability = float(LHD_Availability[0].get()) if LHD_Availability[0].get() else 0
    utilisation = float(Stope_Utilisation[0].get()) if Stope_Utilisation[0].get() else 0
    print(f"availability={availability}, utilisation={utilisation}, group={group}, model={model}")


    cycle_time = calculate_cycle_time(col, update_label=False)


    if cycle_time == 0:
        Trips_labels[col].config(text="N/A")



    trips_per_day = (24*60*60 * availability/100 * utilisation/100) / cycle_time
    Trips_labels[col].config(text=f"{round(trips_per_day)}")  # Rounding to the nearest whole number


def update_mucking(col):
    try:
        trips_per_day = float(Trips_labels[col].cget("text"))
        payload_tonnes = float(payload_labels[col].cget("text"))    

        result = trips_per_day * payload_tonnes

        # Update the Mucking_label for the column
        Mucking_labels[col].config(text=f"{format(round(result), ',')}")


        # Retrieve operation hours
        operation_hours_value = Operation_Hours[0].get()
        operation_hours = float(operation_hours_value) if operation_hours_value else 0
        
        # Calculate annual production: (result/24) * operation hours
        annual_production = (result / 24) * operation_hours
        Annual_labels[col].config(text=f"{format(round(annual_production), ',')}")


    except ValueError:
        # This block will run if the conversion to float fails, i.e., the input isn't a number.
        Mucking_labels[col].config(text="Invalid input!")
        Annual_labels[col].config(text="Invalid input!")



def update_fuel_rate(col):
    # Retrieve the entered distance
    distance_entry_value = travel_distance_entries[col].get()
    distance = float(distance_entry_value) if distance_entry_value else 0



    # Determine the fuel rate category
    if distance < 100:
        category = "Low"
    elif distance <= 200:
        category = "Medium"
    else:
        category = "High"

    # Retrieve the selected model from your GUI. Assuming you have a mechanism like model_vars[column].get()
    selected_model = model_vars[col].get()

    # Get the fuel rate from the dictionary
    fuel_rate = fuel_rates.get(selected_model, {}).get(category, "No Data")

    # Update the Fuel_label
    Fuel_labels[col].config(text=f"{fuel_rate}")

def update_tonnes_per_liter(col):
    try:
        print("Annual Production:", Annual_labels[col].cget("text"))
        annual_production_str = Annual_labels[col].cget("text").replace(",", "")
        annual_production = float(annual_production_str)

        operation_hours_value = Operation_Hours[0].get()
        operation_hours = float(operation_hours_value) if operation_hours_value else 0
        
        lhd_availability  = float(LHD_Availability[0].get())/100 if LHD_Availability[0].get() else 0
        stope_utilisation = float(Stope_Utilisation[0].get())/100 if Stope_Utilisation[0].get() else 0

        fuel_rate = float(Fuel_labels[col].cget("text"))

        # If fuel_rate is 0, update the label and return
        if fuel_rate == 0:
            Tonnes_L_labels[col].config(text="Battery LHD N/A")
            return
        
        # Calculate the denominator for Tonnes per Liter
        denominator = operation_hours * lhd_availability * stope_utilisation * fuel_rate
        
        # Calculate the Tonnes per Liter
        if denominator != 0:  # To avoid division by zero
            tonnes_per_liter = annual_production / denominator
        else:
            tonnes_per_liter = 0

        # Update the Tonnes_L_label
        Tonnes_L_labels[col].config(text=f"{tonnes_per_liter:.2f}")  # Rounded to 2 decimal places

    except Exception as e:
        print(f"Exception occurred: {e}")
        Tonnes_L_labels[col].config(text="Invalid input!")



def update_lhd_required(col):
    try:
        # Retrieve values from the widgets
        target_production_str = Target_Production[0].get().replace(",", "")
        target_production = float(target_production_str) if target_production_str else 0
        
        annual_production_str = Annual_labels[col].cget("text").replace(",", "")
        annual_production = float(annual_production_str) if annual_production_str else 0
        
        operation_years_str = Operation_Years[0].get().replace(",", "")
        operation_years = float(operation_years_str) if operation_years_str else 0

        # Perform calculations
        if annual_production != 0:  # Avoid division by zero
            division_result = target_production / annual_production
        else:
            division_result = 0

        if operation_years != 0:  # Avoid division by zero
            lhd_required = division_result / operation_years
        else:
            lhd_required = 0

        # Round up the lhd_required value
        lhd_required_rounded = math.ceil(lhd_required)

        # Update the Number_LHD_label
        Number_LHD_labels[col].config(text=str(lhd_required_rounded))

    except ValueError:
        # Handle invalid input
        Number_LHD_labels[col].config(text="Invalid input!")




# Dynamic UI Creation
num_columns = len(models_data) # This line needs to be here, before any access to num_columns
group_vars = []
model_vars = []
model_dropdowns = []
buckets_dropdowns = []
travel_distance_entries = []
calculate_buttons = []
payload_labels = []
model_travel_time_labels = []
load_time_entries = []
dump_time_entries = []
delay_time_entries = []
cycle_time_labels = []
payload_titles = []
raw_distance =[]
travel_distance =[]
gradient_dropdowns = []
muck_swell = []
muck_density = []
bucket_fill_factor = []
payload_labels = []
Stope_Utilisation = []
LHD_Availability = []
Operation_Years = []
Operation_Hours = []
Target_Production = []
Trips_labels =[]
Mucking_labels =[]
Fuel_labels = []
Annual_labels =[]
Tonnes_L_labels = []
Number_LHD_labels = []



# Create a frame to hold both canvases
canvas_frame = tk.Frame(tab1)
canvas_frame.pack(side=ctk.RIGHT, padx=10, pady=10, fill=ctk.Y)  # This frame is packed to the right

# Creating the first figure and axis object
fig, ax = plt.subplots(figsize=(7, 5))

# Create the first canvas for plotting and add it to the canvas_frame
canvas = FigureCanvasTkAgg(fig, master=canvas_frame)
canvas_widget = canvas.get_tk_widget()
canvas_widget.pack(anchor="n", side=ctk.TOP, pady=0, fill=ctk.X)  # Pack the canvas to the top of canvas_frame

# Creating a second identical figure and axis object
fig2, ax2 = plt.subplots(figsize=(7, 5))

# Create the second canvas for plotting and add it to the canvas_frame
canvas2 = FigureCanvasTkAgg(fig2, master=canvas_frame)
canvas_widget2 = canvas2.get_tk_widget()
canvas_widget2.pack(anchor="n", side=ctk.TOP, pady=0, fill=ctk.X)  # Pack this canvas below the first one in canvas_frame

# Content frame (existing code)
content_frame = ctk.CTkFrame(tab1)
content_frame.pack(padx=10, pady=10, expand=True, fill=ctk.BOTH)

# Create a canvas for the entire content frame
main_canvas = ctk.CTkCanvas(content_frame)
main_canvas.pack(side=ctk.LEFT, fill=ctk.BOTH, expand=True)

# Add a Scrollbar to the main_canvas
scrollbar = ctk.CTkScrollbar(content_frame, orientation="vertical", command=main_canvas.yview)
scrollbar.pack(side=ctk.RIGHT, fill=ctk.Y)
main_canvas.configure(yscrollcommand=scrollbar.set)

def on_mousewheel(event):
    main_canvas.yview_scroll(-1*(event.delta//120), "units")

root.bind("<MouseWheel>", on_mousewheel)




def on_frame_configure(event, canvas=main_canvas):
    """Reset the scroll region to encompass the inner frame"""
    canvas.configure(scrollregion=canvas.bbox("all"))



def create_and_configure_frame(parent, num_columns):
    if num_columns == 1:
        bg = "green"
    else:
        bg = bg_color

    column_frame = ctk.CTkFrame(parent)
    column_frame.pack(side=ctk.LEFT, padx=10, pady=10, fill=ctk.BOTH)
    set_widget_bg_color(column_frame,bg_color)
    return column_frame
# Create a frame inside the main canvas to place all column frames

all_columns_frame = ctk.CTkFrame(main_canvas)
all_columns_frame_window = main_canvas.create_window((0,0), window=all_columns_frame, anchor="nw")
all_columns_frame.bind("<Configure>", on_frame_configure)


fcolumn_frame = create_and_configure_frame(all_columns_frame, 1)
	    # Operation_Hours entry
Operation_Hours_col = ctk.StringVar()
Operation_Hours_label = ctk.CTkLabel(fcolumn_frame, text="Operation Hours (An.)", font=("Univers-Light-Normal", 10))
Operation_Hours_label.pack(pady=0)
Operation_Hours_entry = ctk.CTkEntry(fcolumn_frame, textvariable=Operation_Hours_col, justify=ctk.CENTER, width=100)
Operation_Hours_entry.pack(pady=10)
Operation_Hours.append(Operation_Hours_entry)

	    # Operation_Years entry
Operation_Years_col = ctk.StringVar()
Operation_Years_label = ctk.CTkLabel(fcolumn_frame, text="Operation Length (Years)", font=("Univers-Light-Normal", 10))
Operation_Years_label.pack(pady=0)
Operation_Years_entry = ctk.CTkEntry(fcolumn_frame, textvariable=Operation_Years_col, justify=ctk.CENTER, width=100)
Operation_Years_entry.pack(pady=10)
Operation_Years.append(Operation_Years_entry)

    	    # Target_Production entry
Target_Production_col = ctk.StringVar()
Target_Production_label = ctk.CTkLabel(fcolumn_frame, text="Target Production (tonnes)", font=("Univers-Light-Normal", 10))
Target_Production_label.pack(pady=0)
Target_Production_entry = ctk.CTkEntry(fcolumn_frame, textvariable=Target_Production_col, justify=ctk.CENTER, width=100)
Target_Production_entry.pack(pady=10)
Target_Production.append(Target_Production_entry)

    # LHD_Availability entry
LHD_Availability_col = ctk.StringVar()
LHD_Availability_label = ctk.CTkLabel(fcolumn_frame, text="Loader Availability %", font=("Univers-Light-Normal", 10))
LHD_Availability_label.pack(pady=0)
LHD_Availability_entry = ctk.CTkEntry(fcolumn_frame, textvariable=LHD_Availability_col, justify=ctk.CENTER, width=100)
LHD_Availability_entry.pack(pady=10)
LHD_Availability.append(LHD_Availability_entry)
	
	    # Stope_Utilisation entry
Stope_Utilisation_col = ctk.StringVar()
Stope_Utilisation_label = ctk.CTkLabel(fcolumn_frame, text="Stope Utilisation %", font=("Univers-Light-Normal", 10))
Stope_Utilisation_label.pack(pady=0)
Stope_Utilisation_entry = ctk.CTkEntry(fcolumn_frame, textvariable=Stope_Utilisation_col, justify=ctk.CENTER, width=100)
Stope_Utilisation_entry.pack(pady=10)
Stope_Utilisation.append(Stope_Utilisation_entry)

  

# Now, create your column frames inside this all_columns_frame
for col in range(num_columns):
    column_frame = create_and_configure_frame(all_columns_frame, num_columns)
    # Now add your other widgets to this column_frame



    # Group dropdown
    group_var = ctk.StringVar()
    group_var.set(list(models_data.keys())[col])
    group_vars.append(group_var)
    group_dropdown = ctk.CTkComboBox(column_frame, variable=group_var, values=list(models_data.keys()),font=("Univers-Light-Normal", 10), width=200, justify="center", command=on_model_selected)
    group_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_group_selected(col))
    group_dropdown.pack(pady=10)

    # Model dropdown
    model_var = ctk.StringVar()
    model_var.set("Select Model")
    model_vars.append(model_var)
    model_dropdown = ctk.CTkComboBox(column_frame, variable=model_var, values=[], font=("Univers-Light-Normal", 10), width=200)
    model_dropdown.bind("<<ComboboxSelected>>", lambda event, col=col: on_model_selected(col))
    model_dropdown.pack(pady=10)
    model_dropdowns.append(model_dropdown)

    # Buckets dropdown
    buckets_var = ctk.StringVar()
    buckets_var.set("")
    buckets_dropdown = ctk.CTkComboBox(column_frame, variable=buckets_var, values=[],font=("Univers-Light-Normal", 10)  ,width=80)
    buckets_dropdown.pack(pady=10)
    buckets_dropdowns.append(buckets_dropdown)






    # Travel Distance Entry
    travel_distance_var_col = ctk.StringVar()
    travel_distance = ctk.CTkLabel(column_frame, text="Distance (m)", font=("Univers-Light-Normal", 10),width=80)
    travel_distance.pack(pady=5)
    travel_distance_entry_col = ctk.CTkEntry(column_frame, textvariable=travel_distance_var_col, justify=ctk.CENTER, width=8)
    travel_distance_entry_col.pack(pady=5)
    travel_distance_entries.append(travel_distance_entry_col)



    # Adding title for Travel Time
    travel_time_title = ctk.CTkLabel(column_frame, text="Travel Time (hh:mm:ss)", font=("Univers-Light-Normal", 10),fg_color = "orange")
    travel_time_title.pack(pady=5, fill=ctk.X)

    # Travel Time label for the model
    travel_time_label = ctk.CTkLabel(column_frame, text="Input Required", font=("Univers-Light-Normal", 10))
    travel_time_label.pack(pady=5)
    model_travel_time_labels.append(travel_time_label)

    # Load Time Entry
    load_time_var_col = ctk.StringVar()
    load_time_label = ctk.CTkLabel(column_frame, text="Load Time (seconds)",width=200, font=("Univers-Light-Normal", 10),fg_color = "orange")
    load_time_label.pack(pady=5)
    load_time_entry = ctk.CTkEntry(column_frame, textvariable=load_time_var_col, justify=ctk.CENTER, width=50)
    load_time_entry.pack(pady=5)
    load_time_entries.append(load_time_entry)

    # Dump Time Entry
    dump_time_var_col = ctk.StringVar()
    dump_time_label = ctk.CTkLabel(column_frame, text="Dump Time (seconds)",width=200, font=("Univers-Light-Normal", 10),fg_color = "orange")
    dump_time_label.pack(pady=5)
    dump_time_entry = ctk.CTkEntry(column_frame, textvariable=dump_time_var_col, justify=ctk.CENTER, width=50)
    dump_time_entry.pack(pady=5)
    dump_time_entries.append(dump_time_entry)

    # Delay Time Entry
    delay_time_var_col = ctk.StringVar()
    delay_time_label = ctk.CTkLabel(column_frame, text="Delay Time (seconds)",width=200,font=("Univers-Light-Normal", 10),fg_color = "orange")
    delay_time_label.pack(pady=5)
    delay_time_entry = ctk.CTkEntry(column_frame, textvariable=delay_time_var_col, justify=ctk.CENTER, width=50)
    delay_time_entry.pack(pady=5)
    delay_time_entries.append(delay_time_entry)

    # Cycle Time display
    cycle_time_title = ctk.CTkLabel(column_frame, text="Cycle Time (hh:mm:ss)", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    cycle_time_title.pack(pady=5)

    cycle_time_label = ctk.CTkLabel(column_frame, text="Input Required", font=("Univers-Light-Normal", 10))
    cycle_time_label.pack(pady=5)
    cycle_time_labels.append(cycle_time_label)

# Create Combobox
    gradient_value = ["0%", "10%", "15%", "20%"]
    gradient_cb = ctk.CTkComboBox(column_frame, values=gradient_value, state="readonly", width=100,justify="center")
    gradient_cb.pack(pady=5)
    gradient_cb.bind("<<ComboboxSelected>>", lambda event, cb=gradient_cb: store_selected_value(event, cb))
    
    gradient_dropdowns.append(gradient_cb)



    # muck_swell entry
    muck_swell_col = ctk.StringVar()
    muck_swell_label = ctk.CTkLabel(column_frame, text="Muck Swell %",width=200, font=("Univers-Light-Normal", 10),fg_color = "orange")
    muck_swell_label.pack(pady=5)
    muck_swell_entry = ctk.CTkEntry(column_frame, textvariable=muck_swell_col, justify=ctk.CENTER, width=8)
    muck_swell_entry.pack(pady=5)
    muck_swell.append(muck_swell_entry)

    # muck_density entry
    muck_density_col = ctk.StringVar()
    muck_density_label = ctk.CTkLabel(column_frame, text="Muck Density t/m³)",width=200, font=("Univers-Light-Normal", 10),fg_color = "orange")
    muck_density_label.pack(pady=5)
    muck_density_entry = ctk.CTkEntry(column_frame, textvariable=muck_density_col, justify=ctk.CENTER, width=8)
    muck_density_entry.pack(pady=5)
    muck_density.append(muck_density_entry)

    # bucket_fill_factor entry
    bucket_fill_factor_col = ctk.StringVar()
    bucket_fill_factor_label = ctk.CTkLabel(column_frame, text="Bucket FF %",width=200, font=("Univers-Light-Normal", 10),fg_color = "orange")
    bucket_fill_factor_label.pack(pady=5)
    bucket_fill_factor_entry = ctk.CTkEntry(column_frame, textvariable=bucket_fill_factor_col, justify=ctk.CENTER, width=8)
    bucket_fill_factor_entry.pack(pady=5)
    bucket_fill_factor.append(bucket_fill_factor_entry)


    payload_title = ctk.CTkLabel(column_frame, text="Payload (tonnes)", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    payload_title.pack(pady=5)
    payload_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    payload_label.pack(pady=5)
    payload_labels.append(payload_label)


      # Trips per day
    Trips_title = ctk.CTkLabel(column_frame, text="Trips per Day", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Trips_title.pack(pady=5)
    Trips_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Trips_label.pack(pady=5)
    Trips_labels.append(Trips_label)


# Mucking
    Mucking_title = ctk.CTkLabel(column_frame, text="Mucking (tonnes/day)", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Mucking_title.pack(pady=5)
    Mucking_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Mucking_label.pack(pady=5)
    Mucking_labels.append(Mucking_label)

# Fuel
    Fuel_title = ctk.CTkLabel(column_frame, text="Fuel Rate (L/hr)", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Fuel_title.pack(pady=5)
    Fuel_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Fuel_label.pack(pady=5)
    Fuel_labels.append(Fuel_label)

# Annual Prod
    Annual_title = ctk.CTkLabel(column_frame, text="Annual Production (tonnes)", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Annual_title.pack(pady=5)
    Annual_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Annual_label.pack(pady=5)
    Annual_labels.append(Annual_label)

# Tonnes per Liter
    Tonnes_L_title = ctk.CTkLabel(column_frame, text="Tonnes per Liter", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Tonnes_L_title.pack(pady=5)
    Tonnes_L_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Tonnes_L_label.pack(pady=5)
    Tonnes_L_labels.append(Tonnes_L_label)

# LHDS REQUIRED
    Number_LHD_title = ctk.CTkLabel(column_frame, text="Number of Loaders Required", font=("Univers-Light-Normal", 10),fg_color = "orange",width=200)
    Number_LHD_title.pack(pady=5)
    Number_LHD_label = ctk.CTkLabel(column_frame, text="", font=("Univers-Light-Normal", 10))
    Number_LHD_label.pack(pady=5)
    Number_LHD_labels.append(Number_LHD_label)


#calculator button
    calculate_button_col = ctk.CTkButton(column_frame, text="Calculate", command=lambda col=col: update_all_timings(col))
    calculate_button_col.pack(pady=10)
    calculate_buttons.append(calculate_button_col)

# Set default options for the remaining columns
for col in range(num_columns):
    group_vars[col].set("Select OEM Group")
    model_vars[col].set("Select LHD Model")
    buckets_dropdowns[col].set("Bucket")
    model_travel_time_labels[col].configure(text="")
    load_time_entries[col].delete(0, ctk.END)
    dump_time_entries[col].delete(0, ctk.END)
    delay_time_entries[col].delete(0, ctk.END)
    cycle_time_labels[col].configure(text="")
    gradient_dropdowns[col].set("Gradient")








    




# Set default options for the remaining columns
for col in range(num_columns):
    group_vars[col].set("Select OEM Group")
    model_vars[col].set("Select LHD Model")
    buckets_dropdowns[col].set("Bucket")
    model_travel_time_labels[col].configure(text="")
    load_time_entries[col].delete(0, ctk.END)
    dump_time_entries[col].delete(0, ctk.END)
    delay_time_entries[col].delete(0, ctk.END)
    cycle_time_labels[col].configure(text="")
    gradient_dropdowns[col].set("Gradient")


for dropdown in [group_dropdown] + model_dropdowns + buckets_dropdowns + gradient_dropdowns:
    dropdown['style'] = 'TCombobox'
    dropdown['justify'] = 'center'


# Event Callbacks...
def on_group_selected(column):
    selected_group = group_vars[column].get()
    model_dropdowns[column]['values'] = list(models_data[selected_group].keys())
    model_vars[column].set("")
    buckets_dropdowns[column]['values'] = []

def on_model_selected(column):
    selected_group = group_vars[column].get()
    selected_model = model_vars[column].get()
    if selected_model != "":
        buckets_dropdowns[column]['values'] = models_data[selected_group][selected_model]
        update_model_travel_times(column)





# Determining Colours for each OEM Group================================================================
group_colors = {
    "Caterpillar LHD models": bg_color,
    "Sandvik LHD models": "#00B0F0",
    "Epiroc LHD models": "#C0C0C0",
    "Komatsu LHD models": "blue"
}
def determine_bar_color(models):
    colors = []
    for model in models:
        for group, group_models in models_data.items():
            if model in group_models:
                colors.append(group_colors[group])
                break
    return colors

#=======================================================================================================

# Extract cycle times and convert them to numeric values
def time_string_to_seconds(time_string):
    try:
        hours, minutes, seconds = map(int, time_string.split(':'))
        total_seconds = hours * 3600 + minutes * 60 + seconds
        return total_seconds
    except ValueError:  # Catch ValueError specifically
        return None

def seconds_to_hms(seconds):
    hours, remainder = divmod(seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"

def update_plot():
    # 1. Extract selected models
    selected_models = [model_var.get() for model_var in model_vars]
    
    # 2. Extract cycle times and convert them to numeric values
    numeric_cycle_times = [time_string_to_seconds(label.cget("text")) for label in cycle_time_labels]
    # Ensure that there's no None value in numeric_cycle_times
    valid_indices = [i for i, time in enumerate(numeric_cycle_times) if time is not None]
    valid_models = [selected_models[i] for i in valid_indices]
    valid_times = [numeric_cycle_times[i] for i in valid_indices]

    # 3. Clear previous plot
    ax.clear()

    # 4. Determine bar colors based on the valid models
    bar_colors = determine_bar_color(valid_models)
    
    # 5. Plot the bars
    ax.bar(valid_models, valid_times, color=bar_colors)
    # [Add other plot formatting as needed]

# 6. Set y-ticks format to "hh:mm:ss"
    ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: seconds_to_hms(x)))

# Plotting

    ax.set_xlabel('', fontsize=7,fontweight='bold')
    ax.set_ylabel('', fontsize=7,fontweight='bold')
    ax.set_title('Cycle Time', fontsize=9,fontweight='bold')
    ax.set_xticks(range(len(valid_models)))
    ax.set_xticklabels(valid_models, rotation=0, ha='center')

# Set tick label size   
    ax.tick_params(axis='x', labelsize=6)
    ax.tick_params(axis='y', labelsize=6)  # If you also want to adjust y-axis tick labels



    canvas.draw()


def update_plot2():
    # 1. Extract selected models but exclude placeholders like "Select Model"
    selected_models = [model_var.get() for model_var in model_vars if model_var.get() != "Select Model"]

    # 2. Extract trips per day corresponding to valid models. 
    # Make sure to only extract numeric values and ignore empty or non-numeric entries.
    valid_tonnes = []
    for label in Tonnes_L_labels:
        try:
            tonnes_value = float(label.cget("text"))
            valid_tonnes.append(tonnes_value)
        except ValueError:
            pass  # Skip non-numeric or empty entries

    # Only use models for which there's a valid trips_per_day value
    models_to_plot = [model for model, trips in zip(selected_models, valid_tonnes) if trips is not None]

    # 3. Clear previous plot
    ax2.clear()

    # 4. Determine bar colors based on the models to be plotted (assuming you have a function for this)
    bar_colors = determine_bar_color(models_to_plot)
    
    # 5. Plot the bars
    ax2.bar(models_to_plot, valid_tonnes[:len(models_to_plot)], color=bar_colors)
    
    # 6. Format the plot
    ax2.set_xlabel('', fontsize=7, fontweight='bold')
    ax2.set_ylabel('', fontsize=7, fontweight='bold')
    ax2.set_title('Trips per Liter', fontsize=9, fontweight='bold')
    ax2.set_xticks(range(len(models_to_plot)))
    ax2.set_xticklabels(models_to_plot, rotation=0, ha='center')

    # Set tick label size
    ax2.tick_params(axis='x', labelsize=6)
    ax2.tick_params(axis='y', labelsize=6)

    # Finally, redraw the canvas
    canvas2.draw()









def update_all_timings(col):
    update_model_travel_times(col)
    calculate_cycle_time(col)
    update_plot()
    calculate_payload(col)
    update_plot2()
    update_trips_per_day(col)
    update_mucking(col)
    update_fuel_rate(col)   
    update_tonnes_per_liter(col)
    update_lhd_required(col)
# Before drawing the updated plot on the canvas:
fig.subplots_adjust(bottom=0.25)  # Adjusting the top value as well







# Run main loop
root.mainloop()
