import uuid

import names
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QSizePolicy, QInputDialog
from Gui import Ui_fluencyCAD  # Import the generated GUI module
from drawing_modules.gl_widget import OpenGLWidget
from drawing_modules.draw_widget2d import SketchWidget
from sdf import *
from python_solvespace import SolverSystem, ResultFlag

# main, draw_widget, gl_widget

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # Set up the UI from the generated GUI module
        self.ui = Ui_fluencyCAD()
        self.ui.setupUi(self)

        self.openGLWidget = OpenGLWidget()
        layout = self.ui.gl_box.layout()
        layout.addWidget(self.openGLWidget)
        size_policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        #self.openGLWidget.setSizePolicy(size_policy)

        self.sketchWidget = SketchWidget()
        layout2 = self.ui.sketch_tab.layout()  # Get the layout of self.ui.gl_canvas
        layout2.addWidget(self.sketchWidget)
        size_policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.sketchWidget.setSizePolicy(size_policy)

        ### Main Model
        self.model = {
            'sketch': {},
            'operation': {},
        }
        self.list_selected = []

        #self.ui.pb_apply_code.pressed.connect(self.check_current_tab)
        self.ui.sketch_list.currentItemChanged.connect(self.on_item_changed)
        self.ui.sketch_list.itemChanged.connect(self.view_update)

        ### Sketches
        self.ui.pb_origin_wp.pressed.connect(self.add_wp_origin)

        self.ui.pb_nw_sktch.pressed.connect(self.add_sketch)
        self.ui.pb_del_sketch.pressed.connect(self.del_sketch)
        self.ui.pb_edt_sktch.pressed.connect(self.edit_sketch)

        ###Modes
        self.ui.pb_linetool.pressed.connect(self.act_line_mode)
        self.ui.pb_con_ptpt.pressed.connect(self.act_constrain_pt_pt_mode)
        self.ui.pb_con_line.pressed.connect(self.act_constrain_pt_line_mode)
        self.ui.pb_con_horiz.pressed.connect(self.act_constrain_horiz_line_mode)
        self.ui.pb_con_vert.pressed.connect(self.act_constrain_vert_line_mode)
        self.ui.pb_con_dist.pressed.connect(self.act_constrain_distance_mode)

        ### Operations
        self.ui.pb_extrdop.pressed.connect(self.send_extrude)
        self.ui.pb_cutop.pressed.connect(self.send_cut)
        self.ui.pb_del_body.pressed.connect(self.del_body)

        self.sketchWidget.constrain_done.connect(self.constrain_finished)

    def add_wp_origin(self):
        #Select orientation  
        #orientation, ok = Q .getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
        self.sketchWidget.create_worplane()

    def act_line_mode(self):
        if not self.ui.pb_linetool.isChecked():
            self.sketchWidget.mouse_mode = 'line'
            #self.sketchWidget.points = []
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None
            self.sketchWidget.points = []

    def act_constrain_pt_pt_mode(self):
        if not self.ui.pb_linetool.isChecked():
            self.sketchWidget.mouse_mode = 'pt_pt'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def act_constrain_pt_line_mode(self):
        if not self.ui.pb_con_line.isChecked():
            self.sketchWidget.mouse_mode = 'pt_line'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def act_constrain_horiz_line_mode(self):
        if not self.ui.pb_con_horiz.isChecked():
            self.sketchWidget.mouse_mode = 'horiz'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def act_constrain_vert_line_mode(self):
        if not self.ui.pb_con_vert.isChecked():
            self.sketchWidget.mouse_mode = 'vert'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def act_constrain_distance_mode(self):
        if not self.ui.pb_con_dist.isChecked():
            self.sketchWidget.mouse_mode = 'distance'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def act_constrain_mid_point_mode(self):
        if not self.ui.pb_con_mid.isChecked():
            self.sketchWidget.mouse_mode = 'pb_con_mid'
        else:
            self.sketchWidget.mouse_mode = None
            self.sketchWidget.line_buffer = None

    def constrain_finished(self):
        self.ui.pb_con_ptpt.setChecked(False)
        self.ui.pb_con_line.setChecked(False)
        self.ui.pb_con_dist.setChecked(False)

    def view_update(self):
        print("Update")
        name = self.ui.body_list.currentItem().text()
        print("selected_for disp", name)
        model = self.model['operation'][name]['sdf_object']
        mesh = model.generate(samples=2**12)
        self.openGLWidget.load_mesh_direct(mesh)

    def on_item_changed(self, current_item, previous_item):
        if current_item:
            name = current_item.text()
            #self.view_update()
            print(f"Selected item: {name}")

    def add_sketch(self):
        name = f"sketch-{str(names.get_first_name())}"
        points = self.sketchWidget.points

        element = {
            'id': name,
            'type': 'polygon',
            'sketch_points': points,
        }

        self.model['sketch'][element['id']] = element
        print(self.model)

        self.ui.sketch_list.addItem(name)
        self.ui.pb_linetool.setChecked(False)
        self.sketchWidget.line_mode = False

        items = self.ui.sketch_list.findItems(name, Qt.MatchExactly)[0]
        self.ui.sketch_list.setCurrentItem(items)

    def edit_sketch(self):
        name = self.ui.sketch_list.currentItem().text()
        self.sketchWidget.clear_sketch()
        points = self.model['sketch'][name]['sketch_points']
        print("points", points)
        self.sketchWidget.set_points(points)

    def del_sketch(self):
        print("Deleting")
        name = self.ui.sketch_list.currentItem()  # Get the current item

        print(self.model)

        if name is not None:
            item_name = name.text()
            print("obj_name", item_name)

            # Check if the 'sketch' key exists in the model dictionary
            if 'sketch' in self.model and item_name in self.model['sketch']:
                if self.model['sketch'][item_name]['id'] == item_name:
                    row = self.ui.sketch_list.row(name)  # Get the row of the current item
                    self.ui.sketch_list.takeItem(row)  # Remove the item from the list widget
                    self.sketchWidget.clear_sketch()
                    self.model['sketch'].pop(item_name)  # Remove the item from the sketch dictionary
                    print(f"Removed sketch: {item_name}")

            # Check if the 'operation' key exists in the model dictionary
            elif 'operation' in self.model and item_name in self.model['operation']:
                if self.model['operation'][item_name]['id'] == item_name:
                    row = self.ui.sketch_list.row(name)  # Get the row of the current item
                    self.ui.sketch_list.takeItem(row)  # Remove the item from the list widget
                    self.sketchWidget.clear_sketch()
                    self.model['operation'].pop(item_name)  # Remove the item from the operation dictionary
                    print(f"Removed operation: {item_name}")

            else:
                print(f"Item '{item_name}' not found in either 'sketch' or 'operation' dictionary.")
        else:
            print("No item selected.")
    def update_body(self):
        pass

    def del_body(self):
        print("Deleting")
        name = self.ui.body_list.currentItem()  # Get the current item

        if name is not None:
            item_name = name.text()
            print("obj_name", item_name)
            # Check if the 'operation' key exists in the model dictionary

            if 'operation' in self.model and item_name in self.model['operation']:
                if self.model['operation'][item_name]['id'] == item_name:
                    row = self.ui.body_list.row(name)  # Get the row of the current item
                    self.ui.body_list.takeItem(row)  # Remove the item from the list widget
                    self.model['operation'].pop(item_name)  # Remove the item from the operation dictionary
                    print(f"Removed operation: {item_name}")
                    self.openGLWidget.clear_mesh()

    def translate_points_tup(self, points):
        """QPoints from Display to mesh data
        input: Qpoints
        output: Tuple X,Y
        """
        p_list = []

        for ps in points:
            p_list.append((ps.x(), ps.y()))

        return p_list

    def send_extrude(self):
        selected = self.ui.sketch_list.currentItem()
        name = selected.text()
        points = self.model['sketch'][name]['sketch_points']

        # UI to mesh
        points = self.translate_points_tup(points)
        if points[-1] == points[0]:
            result = points.pop()
            print("removed last point for mesh")

        length , ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
        #TODO : Implement cancel
        geo = Geometry()
        f = geo.extrude_shape(points, length)

        name_op = f"extrd-{name}"

        element = {
            'id': name_op,
            'type': 'extrude',
            'sdf_object': f,
        }


        self.model['operation'][name_op] = element

        self.ui.body_list.addItem(name_op)
        items = self.ui.body_list.findItems(name_op, Qt.MatchExactly)[0]
        self.ui.body_list.setCurrentItem(items)
        self.view_update()


    def send_cut(self):
        name = self.ui.sketch_list.currentItem().text()
        points = self.model['operation'][name]['sdf_object']
        self.list_selected.append(points)
        print(self.list_selected)

        if len(self.list_selected) > 1:
            geo = Geometry()
            f = geo.cut_shapes(self.list_selected[0], self.list_selected[1] )

            element = {
                'id': name,
                'type': 'cut',
                'sdf_object': f,
            }

            name_op = f"cut-{name}"
            self.model['operation'][name_op] = element
            self.ui.body_list.addItem(name_op)
            items = self.ui.sketch_list.findItems(name_op, Qt.MatchExactly)
            self.ui.body_list.setCurrentItem(items[0])
            self.view_update()

        else:
            print("mindestens 2!")

    def load_and_render(self, file):
        self.openGLWidget.load_stl(file)
        self.openGLWidget.update()

    """ def check_current_tab(self):
        if self.ui.InputTab.currentIndex() == 0:
            geo = Geometry()
            geo.generate_mesh_from_draw(self.sketchWidget.points)
            self.load_and_render("out.stl")

        elif self.ui.InputTab.currentIndex() == 1:
            code_bytes = self.ui.textEdit.toPlainText().encode('utf-8')
            code_text = code_bytes.decode('utf-8')
            save_string = "\nf.save('out.stl', samples=2**12)"
            code_text += save_string

            geo = Geometry()
            geo.generate_mesh_from_code(code_text)
            self.load_and_render("out.stl")"""



class Geometry:
    def distance(self, p1, p2):
        """Calculate the distance between two points."""
        print("p1", p1)
        print("p2", p2)
        return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)

    def extrude_shape(self, points, length: float):
        """2D to 3D sdf always first"""

        f = polygon(points).extrude(length)

        return f

    def cut_shapes(self, sdf_object1, sdf_object2):
        f = difference(sdf_object1, sdf_object2) # equivalent

        return f

    def export_mesh(self, sdf_object):
        """FINAL EXPORT"""
        result_points = sdf_object.generate()
        write_binary_stl('out.stl', result_points)

    def generate_mesh_from_code(self, code_text: str):
        local_vars = {}

        try:
            print(code_text)
            exec(code_text, globals(), local_vars)

            # Retrieve the result from the captured local variables
            result = local_vars.get('result')
            print("Result:", result)

        except Exception as e:
            print("Error executing code:", e)


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

    #pyside6-uic gui.ui > Gui.py -g python