# Event handler for the execute event.
class MakeGridCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)
        inputs = eventArgs.command.commandInputs
        
        #-----------------------------------------------------
        #             Section A - getting the input
        #-----------------------------------------------------
        columnCount = inputs.itemById("columnCount").value
        rowCount = inputs.itemById("rowCount").value
        defaultRowHeight = inputs.itemById("rowheight").value
        defaultColumnWidth = inputs.itemById("columnWidth").value
        innerOffset = inputs.itemById("innerOffset").value
        outerOffset = inputs.itemById("outerOffset").value
        customSizes = inputs.itemById("customSizes").value
        customGrid = inputs.itemById("customGrid").value
        baseThickness = inputs.itemById("baseThickness").value
        deviderHeight = inputs.itemById("deviderHeight").value
        innerFillet = inputs.itemById("innerFilletRadius").value
        outerFillet = inputs.itemById("outerFilletRadius").value

        #-----------------------------------------------------
        #          Section B - calculating useful info 
        #-----------------------------------------------------

        columnWidthList = [defaultColumnWidth for _ in range(columnCount)]
        rowHeightList = [defaultRowHeight for _ in range(rowCount)]

        if customSizes:
            rowsTable = inputs.itemById("rowsTable")
            columnsTable = inputs.itemById("columnsTable")

            # Update row values from the rows input table
            for i in range(rowsTable.rowCount):
                # Column 0 holds the index, Column 1 holds the distance 
                row_idx_spinner = rowsTable.getInputAtPosition(i, 0)
                row_val_input = rowsTable.getInputAtPosition(i, 1)
                
                if row_idx_spinner and row_val_input:
                    target_row_idx = row_idx_spinner.value - 1 # coulmn 1 is index 0 and so on so -1
                    if 0 <= target_row_idx < rowCount:
                        rowHeightList[target_row_idx] = row_val_input.value

            # Update column values from the columns input table
            for i in range(columnsTable.rowCount):
                col_idx_spinner = columnsTable.getInputAtPosition(i, 0)
                col_val_input = columnsTable.getInputAtPosition(i, 1)
                
                if col_idx_spinner and col_val_input:
                    target_col_idx = col_idx_spinner.value - 1
                    if 0 <= target_col_idx < columnCount:
                        columnWidthList[target_col_idx] = col_val_input.value

        overallWidth_until = []
        current_width_accum = outerOffset
        for i in range(columnCount):
            overallWidth_until.append(current_width_accum)
            current_width_accum += columnWidthList[i] + innerOffset
        
        overallWidth = current_width_accum - innerOffset + outerOffset #the last offset is outer not inner so we change it

        overallHeight_until = []
        current_height_accum = outerOffset
        for j in range(rowCount):
            overallHeight_until.append(current_height_accum)
            current_height_accum += rowHeightList[j] + innerOffset
            
        overallHeight = current_height_accum - innerOffset + outerOffset

        rectanglesType = [[0 for _ in range(columnCount)] for _ in range(rowCount)] #0 normal rectangles, 1 coustome pockets, 2 coustome filled
        coustome_pockets = dict() # (x1,y1) to (x2,y2)
        if customGrid:
            rectanglesTable = inputs.itemById("rectanglesTable")
            for i in range(rectanglesTable.rowCount):
                x1 = rectanglesTable.getInputAtPosition(i, 0).value - 1
                y1 = rectanglesTable.getInputAtPosition(i, 1).value - 1
                x2 = rectanglesTable.getInputAtPosition(i, 2).value - 1
                y2 = rectanglesTable.getInputAtPosition(i, 3).value - 1
                pocketOrFill = rectanglesTable.getInputAtPosition(i, 5)

                if pocketOrFill.selectedItem.name == "pocket":
                    coustome_pockets[(x1, y1)] = (x2, y2)
                
                for x in range(x1, x2 + 1):
                    for y in range(y1, y2 + 1):
                        if pocketOrFill.selectedItem.name == "pocket":
                            rectanglesType[y][x] = 1
                        else:
                            rectanglesType[y][x] = 2

        #generating the part

        #-----------------------------------------------------
        #          Section C - skecthing and extruding
        #-----------------------------------------------------

        desgin = adsk.fusion.Design.cast(app.activeProduct)
        root_Component = desgin.rootComponent
        sketches = root_Component.sketches

        xy_plane = root_Component.xYConstructionPlane
        base_sketch = sketches.add(xy_plane)
        base_lines = base_sketch.sketchCurves.sketchLines
        
        # Draw the base rectangle of the tray
        p_origin = adsk.core.Point3D.create(0, 0, 0)
        p_base_corner = adsk.core.Point3D.create(overallWidth, overallHeight, 0)
        base_lines.addTwoPointRectangle(p_origin, p_base_corner)

        # extrude the base rectangle
        prof = base_sketch.profiles.item(0)
        extrudes = root_Component.features.extrudeFeatures
        ext_input = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
        ext_input.setDistanceExtent(False, adsk.core.ValueInput.createByReal(baseThickness))
        
        base_plate_feature = extrudes.add(ext_input)

        base_body = base_plate_feature.bodies.item(0)
        base_face = base_plate_feature.endFaces.item(0) # the top face of the base rectangle, where the divider will be extruded from

        grid_sketch = sketches.add(base_face)
        grid_lines = grid_sketch.sketchCurves.sketchLines

        for i in range(columnCount):
            for j in range(rowCount):
                if rectanglesType[j][i] == 0:
                    # draw the rectangle
                    x1 = overallWidth_until[i]
                    y1 = overallHeight_until[j]
                    x2 = x1 + columnWidthList[i]
                    y2 = y1 + rowHeightList[j]

                    p1 = adsk.core.Point3D.create(x1, y1, 0)
                    p2 = adsk.core.Point3D.create(x2, y2, 0)
                    grid_lines.addTwoPointRectangle(p1, p2)
                elif coustome_pockets.get((i,j)):
                    # draw the rectangle
                    x1 = overallWidth_until[i]
                    y1 = overallHeight_until[j]
                    i2, j2 = coustome_pockets[(i,j)]
                    x2 = overallWidth_until[i2] + columnWidthList[i2]
                    y2 = overallHeight_until[j2] + rowHeightList[j2]
                    p1 = adsk.core.Point3D.create(x1, y1, 0)
                    p2 = adsk.core.Point3D.create(x2, y2, 0)
                    grid_lines.addTwoPointRectangle(p1, p2)

                # else it's 2 so a filled rectangle so we draw nothing
        
        # so we know that in 0<x,y<outerOffset there is no rectangle so we can chose there an get the outer rectangle / divider
        chosing_point = adsk.core.Point3D.create(outerOffset/2, outerOffset/2, 0)
        #get the outer profile that contains the point
        diveder_profile = next((p for p in grid_sketch.profiles if p.boundingBox.contains(chosing_point)), None)

        #extrude the grid divider
        ext_input = extrudes.createInput(diveder_profile, adsk.fusion.FeatureOperations.JoinFeatureOperation)
        ext_input.setDistanceExtent(False, adsk.core.ValueInput.createByReal(deviderHeight))
        extrudes.add(ext_input)

        #add fillets to all the vertical edges

        #-----------------------------------------------------
        #          Section D - adding the fillets
        #-----------------------------------------------------

        fillets = root_Component.features.filletFeatures
        
        inner_edge_collection = adsk.core.ObjectCollection.create()
        outer_edge_collection = adsk.core.ObjectCollection.create()
        
        for edge in base_body.edges:
            line_geom = edge.geometry
            start_pt = line_geom.startPoint
            end_pt = line_geom.endPoint
            
            direction_x = abs(end_pt.x - start_pt.x)
            direction_y = abs(end_pt.y - start_pt.y)
            direction_z = abs(end_pt.z - start_pt.z)
            
            small_delta = 0.001
            if direction_x < small_delta and direction_y < small_delta and direction_z > small_delta:#check if vertical
                if abs(start_pt.z) < small_delta or abs(end_pt.z) < small_delta:#check if starts from base (so it's an outer edge) so from z=0
                    outer_edge_collection.add(edge)
                else:
                    inner_edge_collection.add(edge)
                        
        if outer_edge_collection.count > 0:
            outer_fillet_input = fillets.createInput()
            outer_radius = adsk.core.ValueInput.createByReal(outerFillet)
            outer_fillet_input.addConstantRadiusEdgeSet(outer_edge_collection, outer_radius, True)
            fillets.add(outer_fillet_input)
            
        if inner_edge_collection.count > 0:
            inner_fillet_input = fillets.createInput()
            inner_radius = adsk.core.ValueInput.createByReal(innerFillet)
            inner_fillet_input.addConstantRadiusEdgeSet(inner_edge_collection, inner_radius, True)
            fillets.add(inner_fillet_input)
        
        adsk.terminate()