# functions.py

import yaml
from PIL import Image, ImageTk
from tkinter import ttk, filedialog, messagebox
import tkinter as tk
import os
import trimesh
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import re
import hashlib



# Load a specific parameter set from a YAML file for a group
def load_yaml(group_name, param_name, folder_parsets):
    yml_file = os.path.join(folder_parsets, f"{group_name}.yml")
    if os.path.exists(yml_file):
        with open(yml_file, 'r') as file:
            data = yaml.safe_load(file)
        return data.get(param_name, {})
    return {}

# Save a parameter set to a YAML file for a group
def save_yaml(group_name, param_name, param_data, folder_parsets):
    yml_file = os.path.join(folder_parsets, f"{group_name}.yml")
    all_data = load_existing_data(yml_file)
    all_data[param_name] = param_data
    os.makedirs(folder_parsets, exist_ok=True)
    with open(yml_file, 'w') as file:
        yaml.dump(all_data, file)
    # messagebox.showinfo("Success", f"Parameter set '{param_name}' saved to {group_name}.yml")

def delete_yaml(group_name, param_name, folder_parsets):
    yml_file = os.path.join(folder_parsets, f"{group_name}.yml")
    if os.path.exists(yml_file):
        with open(yml_file, 'r') as file:
            data = yaml.safe_load(file)
        if param_name in data:
            del data[param_name]  # Remove the specific parameter set
        with open(yml_file, 'w') as file:
            yaml.safe_dump(data, file)

# Load existing data from a YAML file
def load_existing_data(yml_file):
    if os.path.exists(yml_file):
        with open(yml_file, 'r') as file:
            return yaml.safe_load(file) or {}
    return {}

# Fetch existing parameter sets from a group-specific YAML file
def get_existing_parameters(group_name, folder_parsets):
    yml_file = os.path.join(folder_parsets, f"{group_name}.yml")
    data = load_existing_data(yml_file)
    return list(data.keys())

# Load the image for the UI
def load_image(image_file, sizelimit=200):
    image = Image.open(image_file)
    w, h = image.size
    if w > h:
        new_w, new_h = sizelimit, int(sizelimit * h / w)
    else:
        new_h, new_w = sizelimit, int(sizelimit * w / h)
    return ImageTk.PhotoImage(image.resize((new_w, new_h), Image.Resampling.LANCZOS))

# Create a new parameter set and save it
def create_new_parameter_set(group_name, param_name, input_vars, folder_parsets):
    if not param_name:
        messagebox.showerror("Error", "Parameter set name cannot be empty.")
        return
    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 the dropdown after saving new parameter set
def update_dropdown(param_values, param_name, param_dropdown):
    param_values.insert(-1, param_name)
    param_dropdown['values'] = param_values

# Create a popup window for creating a new parameter set
def create_new_param_popup(root, group_name, input_vars, param_values, param_dropdown, folder_parsets):
    new_window = tk.Toplevel(root)
    new_window.title(f"Create New Parameter Set for {group_name}")
    tk.Label(new_window, text="Parameter Set Name:").pack(pady=10)
    param_name_entry = tk.Entry(new_window)
    param_name_entry.pack(pady=10)

    def create_param_set():
        param_name = param_name_entry.get()
        create_new_parameter_set(group_name, param_name, input_vars, folder_parsets)
        update_dropdown(param_values, param_name, param_dropdown)
        new_window.destroy()

    tk.Button(new_window, text="Create", command=create_param_set).pack(pady=10)


def make_windows_safe(name: str) -> str:
    suffix = hashlib.sha1(name.encode("utf-8")).hexdigest()[:4]
    safe = re.sub(r'[<>:"/\\\\|?*]', "_", name).strip()
    safe = safe.rstrip(". ")
    return f"{safe}_{suffix}"



# -------------------------------------------------------------------------------------------------------
def plot_clamping(ax, x, y, z, dx, dy1, dy2, dz1, dz2, color='blue', alpha=0.5, linewidth=1, inverty=False):
    """Efficiently plots an L-shaped merged cube structure and draws only axis-aligned edges."""

    # Compute Y offsets
    dyy1, dyy2 = (y - dy1 / 2, y - (dy1 - dy2) / 2) if not inverty else (y + dy1 / 2, y + (dy1 - dy2) / 2)

    # Create and position both cubes
    cubes = [
        trimesh.creation.box(extents=[dx, dy1, dz1]).apply_translation([x + dx / 2, dyy1, z + dz1 / 2]),
        trimesh.creation.box(extents=[dx, dy1 + dy2, dz2]).apply_translation([x + dx / 2, dyy2, z - dz2 / 2])
    ]

    try:
        # Perform boolean union on merged cubes
        merged = trimesh.boolean.union(cubes)

        # Extract vertices and edges
        vertices = np.array(merged.vertices)
        edges = merged.edges_unique

        # Add merged faces without visible edges
        ax.add_collection3d(Poly3DCollection(
            vertices[merged.faces], facecolors=color, alpha=alpha, edgecolors=None, linewidth=0
        ))

        # Draw only axis-aligned edges
        for edge in edges:
            v1, v2 = vertices[edge]
            if np.isclose(v1, v2, atol=1e-6).sum() == 2:  # Only one coordinate changes
                ax.plot(*zip(*[v1, v2]), color='black', linewidth=linewidth)

    except Exception as e:
        print(f"Trimesh union failed: {e}")

def VarDescription(Name):

    #Workpiece
    if Name == "wp":
        return "Rohmaterial"

    elif Name == "WPR_X":
     return "Rohmass X"
    
    elif Name == "WPR_Y":
        return "Rohmass Y"
    
    elif Name == "WPR_Z":
        return "Rohmass Z"
    
    elif Name == "WPF_X":
        return "Griffversatz fertig X"
    
    elif Name == "WPF_Y":
        return "Griffversatz fertig Y"
    
    elif Name == "WPF_Z":
        return "Griffversatz fertig Z"
    
    elif Name == "WP_CLP_OFFSET_X":
        return "Einlegeposition X"
    
    elif Name == "WP_CLP_OFFSET_Y":
        return "Einlegeposition Y"
    
    elif Name == "WP_GRP_OFFSET_Z":
        return "Greiftiefe"

        #CLP
    elif Name == "clp":
        return "Schraubstock Backen"
    
    elif Name == "CLP_BACKENWEITE":
        return "Backenbreite"
    
    elif Name == "CLP_BACKENHOHE":
        return "Backenhöhe"
    
    elif Name == "CLP_SPANNHOHE":
        return "Backen Spanntiefe"
    
    elif Name == "CLP_AUFLAGETIEFE":
        return "Auflagebreite"
    
    elif Name == "MESSPOS_X":
        return "Messposition X"
    
    elif Name == "MESSPOS_Y":
        return "Messposition Y"
    
    elif Name == "MESSPOS_Z":
        return "Abstand Teil-Backe"
    
    elif Name == "MESSPOS_TOL":
        return "Toleranz Spannen"

    #PROBE
    elif Name == "probe":
        return "Messen"
    
    elif Name == "PRB_BOD_RE_X":
        return "Boden Rechts X"
    
    elif Name == "PRB_BOD_RE_Y":
        return "Boden Rechts Y"
    
    elif Name == "PRB_BOD_LI_X":
        return "Boden Links X"
    
    elif Name == "PRB_BOD_LI_Y":
        return "Boden Links Y"
    
    elif Name == "PRB_SEI_HI_X":
        return "Hinten X"
    
    elif Name == "PRB_SEI_HI_Z":
        return "Hinten Z"
    
    elif Name == "PRB_SEI_VO_X":
        return "Vorne X"
    
    elif Name == "PRB_SEI_VO_Z":
        return "Vorne Z"
    
    elif Name == "PRB_VERS_X_Y":
        return "X Mess Pos Y"
    
    elif Name == "PRB_VERS_X_Z":
        return "X Mess Pos Z"
    
    elif Name == "PRB_VERS_X_TOL":
        return "Pos Toleranz X" 
    
    #INL
    elif Name == "inlay":
        return "Inlay" 
    
    elif Name == "INL_X_NUM":
        return "Felder in X" 
    
    elif Name == "INL_X_LENGTH":
        return "Feldlänge X" 
    
    elif Name == "INL_X_OFFSET":
        return "Abstand Felder  X" 
    
    elif Name == "INL_Y_NUM":
        return "Felder in Y" 
    
    elif Name == "INL_Y_LENGTH":
        return "Feldlänge in Y" 
    
    elif Name == "INL_Y_OFFSET":
        return "Abstand Felder Y" 
    
    elif Name == "PRT_NUM":
        return "Teile pro Feld" 


    elif Name != "":
        return Name


