import pymeshlab
import os
import dearpygui.dearpygui as dpg
from datos.Config import ConfigManager
from tkinter import Tk, filedialog
from logica.utilidades.Output import Output
import logica.utilidades.mesh_view as mv
from log_register.Register import Register

registro = Register()
config = ConfigManager()
output = Output("meshlab")

hist_filter = []

is_updating = True

ms = pymeshlab.MeshSet()
list_filters = []

def load_mesh_callback():
    Tk().withdraw()  # Oculta la ventana principal de tkinter
    archivo_malla = filedialog.askopenfilename(title="Select Mesh")

    if not archivo_malla.endswith(".obj"):
        output.set_output("This file type cannot be loaded. Please select an .obj file.")
        return

    if archivo_malla:
        output.set_output("Loading mesh... Please wait.")
        dpg.configure_item("selected_mesh", default_value=archivo_malla)
        config.set_selected_mesh(archivo_malla)
        ms.load_new_mesh(archivo_malla)
        output.set_output("Mesh loaded successfully.")

        registro.append_config("SELECTED_MESH", archivo_malla)

def save_mesh_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return
    
    output.set_output("Saving mesh...")
    sobreescribir = dpg.get_value("sobreescribir_malla")

    if sobreescribir:
        ms.save_current_mesh(config.get_selected_mesh())
        output.set_output("Mesh saved successfully.")
        return
    
    ms.save_current_mesh(os.path.join(config.DIR_ACT, "new_mesh.obj"))
    output.set_output("Mesh saved successfully.")

def apply_filter_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return

    if len(list_filters) == 0:
        output.set_output("No filter selected.")
        return

    output.set_output("Applying filter...")
    for selected_filter in list_filters:
        hist_filter.append(selected_filter)

        ms.apply_filter(selected_filter)
        output.set_output("Filter " + selected_filter + " applied successfully.")

    registro.append_config("FILTROS", list(hist_filter))

def select_options(sender):
    global list_filters

    if dpg.get_value(sender):
        list_filters.append(sender)
        registro.append_config("FILTROS", list_filters)
        
    else:
        list_filters.remove(sender)
        registro.append_config("FILTROS", list_filters)

def update_data(sender):
    dpg.configure_item("selected_mesh", default_value=config.SELECTED_MESH)

    global is_updating

    if is_updating == False:

        for filter in config.FILTROS:
            dpg.set_value(filter, True)

        dpg.set_value("lambda", config.LAMBDA)
        dpg.set_value("mu", config.MU)
        dpg.set_value("smooth_steps", config.SMOOTH_STEPS)
        dpg.set_value("min_component_size", config.NUMBER_FACES)
        dpg.set_value("max_hole_size", config.MAX_HOLE_SIZE)
        dpg.set_value("refine_hole_edge_length", config.EDGE_LENGTH)
        dpg.set_value("max_distance", config.MERGE_THRESHOLD)

def mesh_view_callback(sender):
    output.set_output("Viewing mesh...")
    result = mv.main_view(config.get_selected_mesh())

    output.set_output(result)

def iniciar_instant_nerf(sender):
    nerf_exe = config.get_rutaNGP()

    if not config.is_base_folder_set():
        output.set_output("No working directory selected.")
        return
    
    output.set_output("Starting Instant NGP...")

    result = output.init_process(nerf_exe + " " + config.DIR_ACT)
    if result != 0:
        output.set_output("Error starting Instant NGP.")
    else:
        output.set_output("Instant NGP started successfully.")

def apply_filter_taubin_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return

    output.set_output("Applying Taubin filter...")
    
    lambda_ = dpg.get_value("lambda")
    mu = dpg.get_value("mu")
    smooth_steps = dpg.get_value("smooth_steps")

    ms.apply_coord_taubin_smoothing(lambda_=lambda_, mu=mu, stepsmoothnum=smooth_steps)
    output.set_output("Taubin filter applied successfully.")

def isolated_components_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return

    output.set_output("Applying isolated components filter...")
    min_component_size = dpg.get_value("min_component_size")

    ms.meshing_remove_connected_component_by_face_number(mincomponentsize=min_component_size)
    output.set_output("Isolated components filter applied successfully.")

def close_holes_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return

    output.set_output("Applying hole closing filter...")
    max_hole_size = dpg.get_value("max_hole_size")
    refine_hole_edge_length = dpg.get_value("refine_hole_edge_length")

    ms.meshing_close_holes(maxholesize=max_hole_size, refineholeedgelen=pymeshlab.PercentageValue(refine_hole_edge_length))
    output.set_output("Hole closing filter applied successfully.")

def merge_close_vertices_callback():
    if ms.mesh_number() == 0:
        output.set_output("No mesh loaded.")
        return

    output.set_output("Applying merge close vertices filter...")
    max_distance = dpg.get_value("max_distance")

    ms.meshing_merge_close_vertices(threshold=pymeshlab.PercentageValue(max_distance))
    output.set_output("Merge close vertices filter applied successfully.")

def taubin_options_callback(sender):
    lambda_ = dpg.get_value("lambda")
    mu = dpg.get_value("mu")
    smooth_steps = dpg.get_value("smooth_steps")

    config.LAMBDA = lambda_
    config.MU = mu
    config.SMOOTH_STEPS = smooth_steps

    registro.append_config("LAMBDA", lambda_)
    registro.append_config("MU", mu)
    registro.append_config("SMOOTH_STEPS", smooth_steps)

def isolated_callback(sender):
    min_component_size = dpg.get_value("min_component_size")

    config.NUMBER_FACES = min_component_size
    registro.append_config("NUMBER_FACES", min_component_size)

def close_hole_callback(sender):
    max_hole_size = dpg.get_value("max_hole_size")
    refine_hole_edge_length = dpg.get_value("refine_hole_edge_length")

    config.MAX_HOLE_SIZE = max_hole_size
    config.EDGE_LENGTH = refine_hole_edge_length
    registro.append_config("MAX_HOLE_SIZE", max_hole_size)
    registro.append_config("EDGE_LENGTH", refine_hole_edge_length)

def close_callback(sender):
    max_distance = dpg.get_value("max_distance")

    config.MERGE_THRESHOLD = max_distance
    registro.append_config("MERGE_THRESHOLD", max_distance)

def main_view(parent):
    output.main_view()

    grupo_general = dpg.add_group(parent=parent, tag="meshlab", show=True) 

    dpg.add_spacer(height=20, parent=grupo_general)

    with dpg.group(horizontal=True, show=True, parent=grupo_general):
        dpg.add_button(label="Start Instant NGP", callback=iniciar_instant_nerf, show=True)

    with dpg.item_handler_registry(show=False, tag="handler_meshlab") as handler_meshlab:
        dpg.add_item_visible_handler(callback=update_data)

    dpg.add_spacer(height=20, parent=grupo_general)

    dpg.add_separator(show=True, parent=grupo_general)

    with dpg.group(horizontal=False, show=True, tag="main_options_mesh", parent=grupo_general):
        dpg.add_text("Simple filters", show=True)

        dpg.add_checkbox(label="Remove Duplicate Faces", default_value=is_filter_selected("meshing_remove_duplicate_faces"), show=True, callback=select_options, tag="meshing_remove_duplicate_faces")
        dpg.add_checkbox(label="Remove Folded Faces", default_value=is_filter_selected("meshing_remove_folded_faces"), show=True, callback=select_options, tag="meshing_remove_folded_faces")
        dpg.add_checkbox(label="Remove Null Faces", default_value=is_filter_selected("meshing_remove_null_faces"), show=True, callback=select_options, tag="meshing_remove_null_faces")
        dpg.add_checkbox(label="Remove Duplicate Vertices", default_value=is_filter_selected("meshing_remove_duplicate_vertices"), show=True, callback=select_options, tag="meshing_remove_duplicate_vertices")
        dpg.add_checkbox(label="Remove Unreferenced Vertices", default_value=is_filter_selected("meshing_remove_unreferenced_vertices"), show=True, callback=select_options, tag="meshing_remove_unreferenced_vertices")
        dpg.add_checkbox(label="Repair Non Mainfold Edges", default_value=is_filter_selected("meshing_repair_non_manifold_edges"), show=True, callback=select_options, tag="meshing_repair_non_manifold_edges")
        
    dpg.add_spacer(height=10, show=True, parent=grupo_general)

    with dpg.group(horizontal=False, show=True, tag="main_options_mesh_filter", parent=grupo_general):
        dpg.add_text("Advanced filters", show=True)

        # ms.apply_coord_taubin_smoothing(lambda_ = 0.600000, mu = -0.534000, stepsmoothnum = 11)
        dpg.add_text(default_value="Apply Taubin Smoothing", show=True)
        with dpg.group(horizontal=False, show=True):
            dpg.add_input_float(label="Lambda", default_value=config.LAMBDA, show=True, tag="lambda", min_value=0.5, max_value=1.0, callback=taubin_options_callback)
            dpg.add_input_float(label="Mu", default_value=config.MU, show=True, tag="mu", min_value=-1.0, max_value=1.0, callback=taubin_options_callback)
            dpg.add_input_int(label="Smooth Steps", default_value=config.SMOOTH_STEPS, show=True, tag="smooth_steps", min_value=1, max_value=100, callback=taubin_options_callback)

            dpg.add_button(label="Apply", show=True, callback=apply_filter_taubin_callback, width=150, height=30)

        dpg.add_spacer(height=10, show=True)

        # ms.meshing_remove_connected_component_by_face_number(mincomponentsize = 27)
        dpg.add_text(default_value="Remove Isolated Components by Faces Number", show=True)
        with dpg.group(horizontal=False, show=True):
            dpg.add_input_int(label="Min Component Size", default_value=config.NUMBER_FACES, show=True, tag="min_component_size", min_value=1, max_value=100, callback=isolated_callback)
            dpg.add_button(label="Apply", show=True, callback=isolated_components_callback, width=150, height=30)

        dpg.add_spacer(height=10, show=True)

        # ms.meshing_close_holes(maxholesize = 40, refineholeedgelen = pymeshlab.PercentageValue(3.999994)
        dpg.add_text(default_value="Close Holes", show=True)
        with dpg.group(horizontal=False, show=True):
            dpg.add_input_int(label="Max Hole Size", default_value=config.MAX_HOLE_SIZE, show=True, tag="max_hole_size", min_value=1, max_value=100, callback=close_hole_callback)
            dpg.add_input_float(label="Refine Hole Edge Length", default_value=config.EDGE_LENGTH, show=True, tag="refine_hole_edge_length", min_value=1.0, max_value=100.0, callback=close_hole_callback)

            dpg.add_button(label="Apply", show=True, callback=close_holes_callback, width=150, height=30)
        
        dpg.add_spacer(height=10, show=True)

        #ms.meshing_merge_close_vertices(threshold = pymeshlab.PercentageValue(2.000006))
        dpg.add_text(default_value="Merge Close Vertices", show=True)
        with dpg.group(horizontal=False, show=True):
            dpg.add_input_float(label="Threshold", default_value=config.MERGE_THRESHOLD, show=True, tag="max_distance", min_value=1.0, max_value=10.0, callback=close_callback)

            dpg.add_button(label="Apply", show=True, callback=merge_close_vertices_callback, width=150, height=30)

    dpg.add_spacer(height=10, show=True, parent=grupo_general)

    dpg.add_input_text(label="Selected Mesh", show=True, default_value=config.SELECTED_MESH, enabled=False, tag="selected_mesh", parent=grupo_general)
    dpg.add_checkbox(label="Overwrite Mesh", show=True, default_value=False, parent=grupo_general, tag="sobreescribir_malla")
    dpg.bind_item_handler_registry("selected_mesh", "handler_meshlab")

    dpg.add_spacer(height=5, show=True, parent=grupo_general)

    with dpg.group(horizontal=True, show=True, tag="main_buttons_mesh", parent=grupo_general):
        dpg.add_button(label="Load Mesh", show=True, callback=load_mesh_callback, width=150, height=30)
        dpg.add_button(label="Save Mesh", show=True, callback=save_mesh_callback, width=150, height=30)
        dpg.add_button(label="Apply Filter", show=True, callback=apply_filter_callback, width=150, height=30)
        dpg.add_button(label="View Mesh", show=True, callback=mesh_view_callback, width=150, height=30)

def is_filter_selected(filter):
    return filter in config.FILTROS

def show_view():
    global is_updating
    is_updating = False
    
    update_data(None)
    is_updating = True
    dpg.show_item("meshlab")