# main.py
from functions import *
from funct_inlay import *
from funct_wp import *
from funct_clp import *
from funct_gen import *
from funct_probe import *

import tkinter as tk
from tkinter import ttk
import yaml

folder_parsets = r"\\HOST-BL7\Mabi-Robotic-Produktion\40_PROJEKTE\30_Interne_Projekte\DMU50_Automation\40_SOFTWARE\CONFIGURATOR\DMU50_GUI_PY-main\dmu50_gui_py\_cfg_parsets"
folder_pictures = "cfg_picture"
folder_output = r"\\HOST-BL7\Mabi-Robotic-Produktion\40_PROJEKTE\30_Interne_Projekte\DMU50_Automation\40_SOFTWARE\CONFIGURATOR\DMU50_GUI_PY-main\dmu50_gui_py\_out"
file_def = "def.yml"
file_jobs = "jobs.yml"
name_newparset = "<new parset>"
name_newjob = "<new job>"
pic_sizelimit = 250



def _verify_job(job_name):
    if job_name in ("", name_newjob):
        messagebox.showwarning("No job selected",
                               "Please select (or save) a job first.")
        return 0
    else:
        return 1

def _deep_copy(save_dir, gen_dir):
    if os.path.isdir(save_dir):
        for name in os.listdir(save_dir):
            src = os.path.join(save_dir, name)
            dst = os.path.join(gen_dir, name)
            if os.path.isdir(src):
                shutil.copytree(src, dst, dirs_exist_ok=True)
            else:
                shutil.copy2(src, dst)


def setup_group(frame, group_name, data, input_vars, selected_params, group_widgets):
    group_data = data['groups'][group_name]
    picture_name = group_data['cfg']['picture_name']  # Picture name defined in def.yml
    picture_path = os.path.join(folder_parsets, picture_name)  # Full path to the picture

    param_values = get_existing_parameters(group_name, folder_parsets)
    param_values.append(name_newparset)
    param_var = selected_params[group_name]  # Bind the dropdown to the corresponding selected_param entry

    multicolumn_idx = group_data['cfg'].get('multicolumn_idx', [])
    colspan = (len(multicolumn_idx)+1)*2

    # Dropdown for parameter sets
    frame_parset = ttk.Frame(frame)
    frame_parset.grid(row=2, column=0, columnspan=colspan, pady=5, sticky="w")
    param_dropdown = ttk.Combobox(frame_parset, textvariable=param_var, values=param_values, width=14)
    group_widgets[group_name] = {"param_dropdown": param_dropdown}  # Save for later reference
    source_path = f"{folder_pictures}/{data['general']['notavailablepic_name']}"

    # Dropdown field
    ttk.Label(frame_parset, text="Parameter-Set:", width=14).grid(row=0, column=0, padx=5, pady=5, sticky="w")
    param_dropdown.grid(row=0, column=1, padx=5, pady=5, sticky="w")



    # Function to update the picture based on inputs or use the init picture
    def update_inlay_picture():
        grpnm = group_name
        all_group_vars = {group_name: {key: var.get() for key, var in group_vars.items()} for group_name, group_vars in
                          input_vars.items()}

        # Use the source_path if the selected parameter set is the new parameter set placeholder
        # print(param_dropdown.get())                   #DEBUG
        if param_dropdown.get() == name_newparset:
            picture_to_use = source_path
        else:
            picture_to_use = picture_path
            if grpnm == "inlay":
                generate_picture_inlay(all_group_vars, picture_to_use)
            elif grpnm == "wp":
                generate_picture_wp(all_group_vars, picture_to_use)
            elif grpnm == "clp":
                generate_picture_clp(all_group_vars, picture_to_use)
            elif grpnm == "probe":
                generate_picture_probe(all_group_vars, picture_to_use)

        group_image_tk = load_image(picture_to_use, pic_sizelimit)
        image_label.configure(image=group_image_tk)
        image_label.image = group_image_tk  # Keep a reference to avoid garbage collection


    # Function to load a parameter set
    def load_selected_param():
        selected_param = param_dropdown.get()
        if selected_param != name_newparset:
            param_data = load_yaml(group_name, selected_param, folder_parsets)
            for key, value in param_data.items():
                input_vars[group_name][key].set(value)
        update_inlay_picture()

    # Function to save the current parameter set
    def save_current_param():
        param_name = param_dropdown.get()
        if not param_name == name_newparset:
            if param_name not in param_values and param_name.strip():
                param_values.insert(-1, param_name)  # Insert before '<<new parameter set>>'
                param_dropdown['values'] = param_values
                param_dropdown.set(param_name)
            if name_newparset not in param_values:
                param_values.append(name_newparset)  # Ensure it's only appended once
            param_data = {key: var.get() for key, var in input_vars[group_name].items()}
            save_yaml(group_name, param_name, param_data, folder_parsets)
            update_inlay_picture()

    # Function to delete the current parameter set
    def delete_current_param():
        param_name = param_dropdown.get()
        if param_name in param_values and param_name != name_newparset:
            confirm = messagebox.askyesno("Delete Parameter", f"Are you sure you want to delete '{param_name}'?")
            if confirm:
                delete_yaml(group_name, param_name, folder_parsets)
                param_values.remove(param_name)
                if name_newparset not in param_values:
                    param_values.append(name_newparset)  # Ensure it's only appended once

                # If there are any parameter sets left, select the next one; otherwise, show name_newparset
                if param_values and param_values[0] != name_newparset:
                    param_dropdown['values'] = param_values
                    param_dropdown.set(param_values[0])  # Select the first remaining parameter set
                    load_selected_param()  # Load the values of the first remaining parameter set
                else:
                    # If no parameter sets left, show name_newparset
                    param_dropdown['values'] = [name_newparset]
                    param_dropdown.set(name_newparset)

                    # Clear the input fields when no parameter sets are left
                    for key in input_vars[group_name]:
                        input_vars[group_name][key].set('')

                    # Show original picture
                    update_inlay_picture()


    # Detect and save a new parameter set if entered via dropdown (press Enter)
    def on_enter_new_param(event):
        selected_param = param_dropdown.get()
        if selected_param not in param_values and selected_param.strip():
            param_values.insert(-1, selected_param)
            param_dropdown['values'] = param_values
            save_current_param()
            param_dropdown.set(selected_param)
            if name_newparset not in param_values:
                param_values.append(name_newparset)  # Ensure it's only appended once
            update_inlay_picture()

    # Load and display the specific group picture
    group_image_tk = load_image(source_path, pic_sizelimit)
    image_label = ttk.Label(frame, image=group_image_tk)
    image_label.image = group_image_tk  # Keep a reference to avoid garbage collection
    image_label.grid(row=0, column=0, columnspan=colspan, padx=10, pady=10, sticky="")  # Center the image in the middle
    frame.grid_rowconfigure(0, minsize=230)

    # Add horizontal line
    ttk.Separator(frame, orient='horizontal').grid(row=1, column=0, columnspan=colspan, sticky="ew", pady=10)

    # buttons for save/load/delete
    ttk.Button(frame_parset, text="Save", width=12, command=save_current_param).grid(row=1, column=0, padx=5, pady=5, sticky="w")
    ttk.Button(frame_parset, text="Delete", width=12, command=delete_current_param).grid(row=1, column=1, padx=5, pady=5, sticky="w")

    # Add horizontal line
    ttk.Separator(frame, orient='horizontal').grid(row=3, column=0, columnspan=colspan, sticky="ew", pady=10)

    # Setup input fields below dropdown and buttons
    row = 0
    col = 0
    group_vars = {}
    frame_var = ttk.Frame(frame)  # Kein Innenabstand mehr
    frame_var.grid(row=4, column=0, columnspan=colspan, padx=5, pady=5, sticky="nsew")
    for idx, (var_name, _) in enumerate(group_data['parameter'].items()):
        if idx+1 in multicolumn_idx:
            col += 2
            row = 0
        ttk.Label(frame_var, text=VarDescription(var_name), font=("TkDefaultFont", 8)).grid(row=row, column=col, padx=5, pady=5, sticky="e")
        var = tk.StringVar()
        group_vars[var_name] = var
        ttk.Entry(frame_var, textvariable=var, font=("TkDefaultFont", 8), width=7).grid(row=row, column=col + 1, padx=5, pady=5, sticky="w")
        row += 1
    input_vars[group_name] = group_vars

    # Automatically select and load the first parameter set
    if param_values and param_values[0] != name_newparset:
        param_var.set(param_values[0])
        load_selected_param()  # Directly load the first parameter set
    else:
        param_var.set(name_newparset)  # If no parameters available, set to default

    # Bind the dropdown selection event
    param_dropdown.bind("<<ComboboxSelected>>", lambda event: load_selected_param())

    # Bind the Enter key to detect when a new parameter set is typed and saved
    param_dropdown.bind("<Return>", on_enter_new_param)

    # Automatically generate and display the picture at startup
    update_inlay_picture()


def setup_jobs(frame, data, selected_job, selected_params, input_vars, group_widgets):
    # Load existing jobs from the jobs.yml file
    job_values = get_existing_parameters("jobs", folder_parsets)
    job_values.append(name_newjob)

    # Function to load selected job and update each group
    def load_selected_job():
        job_name = selected_job.get()
        if job_name != name_newjob:
            job_data = load_yaml("jobs", job_name, folder_parsets)
            # Update the parameter sets for each group according to the job
            for group_name, param_set in job_data.items():
                if group_name in selected_params:
                    # Check if the parameter set exists for the group
                    param_values = get_existing_parameters(group_name, folder_parsets)
                    if param_set not in param_values:
                        # If parameter set is missing, set to <<new par set>> and clear fields
                        selected_params[group_name].set(name_newparset)
                        clear_input_fields(group_name)
                    else:
                        selected_params[group_name].set(param_set)
                        # Manually trigger the parameter dropdown change for the group
                        param_dropdown = group_widgets[group_name]["param_dropdown"]
                        param_dropdown.set(param_set)  # Update dropdown to show selected set
                        param_dropdown.event_generate("<<ComboboxSelected>>")

    # Function to clear the input fields when a parameter set is missing
    def clear_input_fields(group_name):
        for key in input_vars[group_name]:
            input_vars[group_name][key].set('')  # Clear the input fields

    # Function to save the current job
    def save_current_job():
        job_name = selected_job.get()
        if not job_name == name_newjob:
            if job_name not in job_values and job_name.strip():
                job_values.insert(-1, job_name)  # Insert before '<<new job>>'
                selected_job.set(job_name)
            job_data = {group_name: selected_params[group_name].get() for group_name in data['groups']}
            save_yaml("jobs", job_name, job_data, folder_parsets)
            # Update job dropdown after saving a new job
            job_dropdown['values'] = job_values
            job_dropdown.set(job_name)

    # Function to delete the selected job
    def delete_current_job():
        job_name = selected_job.get()
        if job_name in job_values and job_name != name_newjob:
            confirm = messagebox.askyesno("Delete Job", f"Are you sure you want to delete '{job_name}'?")
            if confirm:
                delete_yaml("jobs", job_name, folder_parsets)
                job_values.remove(job_name)
                selected_job.set(job_values[0] if job_values else name_newjob)
                # Update job dropdown after deletion
                job_dropdown['values'] = job_values
                load_selected_job()

    def upload_job_files():
        job_name = selected_job.get()
        if not _verify_job(job_name):
            return

        job_safe   = make_windows_safe(job_name)
        save_dir   = os.path.join(folder_parsets, "files", job_safe)
        os.makedirs(save_dir, exist_ok=True)

        paths = filedialog.askopenfilenames(
            parent=frame, title=f"Attach file(s) to job: {job_name}"
        )
        if not paths:
            return

        copied = 0
        for src in paths:
            try:
                shutil.copy2(src, save_dir)
                copied += 1
            except Exception as exc:
                messagebox.showerror("Copy failed",
                                     f"Could not copy:\n{src}\n\n{exc}")
                return

        messagebox.showinfo("Upload complete",
                            f"{copied} file(s) copied to:\n{save_dir}")

    def show_job_files():
        job_name = selected_job.get()
        if not _verify_job(job_name):
            return

        job_safe = make_windows_safe(job_name)
        target   = os.path.join(folder_parsets, "files", job_safe)

        if not os.path.isdir(target) or not os.listdir(target):
            messagebox.showinfo("No files",
                                f"No files found for job “{job_name}”.")
            return

        os.startfile(target)


    # Job dropdown and buttons for save/load/delete
    ttk.Label(frame, text="Job:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
    job_dropdown = ttk.Combobox(frame, textvariable=selected_job, values=job_values)
    job_dropdown.grid(row=0, column=1, padx=5, pady=5)

    ttk.Button(frame, text="Save", command=save_current_job) \
        .grid(row=0, column=2, padx=5, pady=5, sticky="w")
    ttk.Button(frame, text="Delete", command=delete_current_job) \
        .grid(row=0, column=3, padx=5, pady=5, sticky="w")

    sep = ttk.Separator(frame, orient="vertical")
    sep.grid(row=0, column=4, sticky="ns", padx=8)  # full height of the cell
    frame.grid_columnconfigure(4, minsize=15)  # width of the line

    ttk.Button(frame, text="Upload Job-Files", command=upload_job_files) \
        .grid(row=0, column=5, padx=5, pady=5, sticky="w")
    ttk.Button(frame, text="Show Job-Files", command=show_job_files) \
        .grid(row=0, column=6, padx=5, pady=5, sticky="w")


    # Bind job selection to loading the selected job and updating the groups
    job_dropdown.bind("<<ComboboxSelected>>", lambda event: load_selected_job())
    job_dropdown.bind("<Return>", lambda event: save_current_job())

    # After setting up all UI components
    if job_values and job_values[0] != name_newjob:
        selected_job.set(job_values[0])  # Select the first job automatically
        load_selected_job()  # Load the first job's parameters and update the corresponding parameter sets

    return load_selected_job



def setup_evaluation(root, folder_pictures, data, folder_output, input_vars, selected_params, selected_job):
    # Create a frame for the EVALUATION section
    frame = ttk.LabelFrame(root, text="EVALUATION", padding=(10, 10))
    frame.grid(row=2, column=0, columnspan=5, padx=10, pady=10, sticky="nsew")

    # Load and display the specific initial picture
    picture_path = f"{folder_pictures}/{data['general']['generatorpic_name']}"
    group_image_tk = load_image(picture_path, 100)  # Assuming a function load_image exists and works as expected
    image_label = ttk.Label(frame, image=group_image_tk)
    image_label.image = group_image_tk  # Keep a reference to avoid garbage collection
    image_label.grid(row=0, column=0, rowspan=2, padx=10, pady=10)


    # Function to generate configuration file based on current parameters
    def btn_gen_cfg_dmuaut1():
        job_name = selected_job.get()
        if not _verify_job(job_name):
            return
        job_safe   = make_windows_safe(job_name)
        save_dir   = os.path.join(folder_parsets, "files", job_safe)

        gen_dir = gen_cfg_dmuaut(folder_output, input_vars, selected_params)
        print(save_dir)
        print(gen_dir)
        _deep_copy(save_dir, gen_dir)

#    def btn_gen_cfg_dmuaut2():
#       gen_cfg_dmuaut(folder_output, input_vars, selected_params, filetransfer=True)
#    def btn_gen_cfg_inlay1():
#        gen_cfg_inlay(folder_output, input_vars, selected_params)
#   def btn_gen_cfg_inlay2():
#        gen_cfg_inlay(folder_output, input_vars, selected_params, filetransfer=True)

    # Place buttons in the vertical frame for operations
    button_frame = ttk.Frame(frame)
    button_frame.grid(row=0, column=1, padx=10, pady=10, sticky="n")
    button1 = ttk.Button(button_frame, text="GEN CFG_DMUAUT.SPF FILE", width=45, command=btn_gen_cfg_dmuaut1)
    button1.grid(row=0, column=0, padx=10, pady=5)

#    button2 = ttk.Button(button_frame, text="GEN+TRANSFER CFG_DMUAUT.SPF FILE", width=45, command=btn_gen_cfg_dmuaut2)
    #button2.grid(row=0, column=1, padx=10, pady=5)
    #button3 = ttk.Button(button_frame, text="GEN CFG_INLAY.SPF FILE", width=45, command=btn_gen_cfg_inlay1)
    #button3.grid(row=1, column=0, padx=5, pady=5)
    #button4 = ttk.Button(button_frame, text="GEN+TRANSFER CFG_INLAY.SPF FILE", width=45, command=btn_gen_cfg_inlay2)
    #button4.grid(row=1, column=1, padx=5, pady=5)

        # Drag-and-Drop File Field

def main():
    # Tkinter root setup
    root = tk.Tk()
    root.title("DMU50")
    root.geometry("+0+0")

    # Load object definitions from YAML
    with open(file_def, 'r') as file:
        data = yaml.safe_load(file)

    input_vars = {}
    selected_params = {}  # Dictionary to track selected parameters for each group
    selected_job = tk.StringVar()
    group_widgets = {}  # Dictionary to store widgets related to groups

    def setup_ui():
        # ---- Setup Jobs UI
        frame = ttk.LabelFrame(root, text="Jobs", padding=(10, 10))
        frame.grid(row=0, column=0, columnspan=len(data['groups']), padx=10, pady=10, sticky="nsew")
        load_selected_job = setup_jobs(frame, data, selected_job, selected_params, input_vars, group_widgets)

        # ---- Create the UI for each group
        for col_idx, group_name in enumerate(data['groups']):
            frame_group = ttk.LabelFrame(root, text=VarDescription(group_name), padding=(10, 10))
            frame_group.grid(row=1, column=col_idx, padx=10, pady=10, sticky="nsew")

            selected_params[group_name] = tk.StringVar()
            setup_group(frame_group, group_name, data, input_vars, selected_params, group_widgets)

            root.grid_columnconfigure(list(range(col_idx + 1)), weight=1)
            root.grid_rowconfigure(0, weight=1)

        # ---- Setup the EVALUATION section
        setup_evaluation(root, folder_pictures, data, folder_output, input_vars, selected_params, selected_job)

        # After the UI is fully set up, load the selected job to update parameter lists
        load_selected_job()  # Ensure this is called to load the job parameters

        # Show the window after setup is complete
        root.deiconify()

    # Initially hide the root window
    root.withdraw()

    # Delay UI setup by 1000 milliseconds (1 second)
    root.after(300, setup_ui)

    # Start the Tkinter event loop
    root.mainloop()


if __name__ == "__main__":
    main()

