Please check this code and make the arrow keys move the furniture inside the canvas and not control the dropdown menu, also fix the code and overall structure: # main.py

import sys  # For app execution
import math  # For geometric calculations
from PyQt6.QtCore import Qt, QRectF, QPointF
import json  # For handling embedded database (if using JSON)
from shapely import Point
from shapely.geometry import Polygon, LineString, box  # For geometry manipulation
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsRectItem,
    QPushButton, QComboBox, QFileDialog, QLabel, QGraphicsItem, QGraphicsLineItem,
    QHBoxLayout, QVBoxLayout, QWidget, QSizePolicy
)
from PyQt6.QtGui import QPen, QBrush, QColor, QDrag, QPixmap, QPainter
from itertools import combinations
from ortools.sat.python import cp_model
from collections import namedtuple


# ======= 1. DATACLASS FOR FURNITURE =======
from dataclasses import dataclass
from shapely.geometry import box


@dataclass
class Furniture:
    name: str
    width: int
    height: int
    constraints: dict
    rect: QGraphicsRectItem = None
    geom: box = None

GRID_SIZE = 20
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600

FURNITURE_DB = [
    {"name": "Table", "width": 100, "height": 50, "constraints": {"near_wall": False}},
    {"name": "Sofa", "width": 200, "height": 80, "constraints": {"near_wall": True}},
    {"name": "Chair", "width": 50, "height": 50, "constraints": {"near_wall": False}},
    # ... more furniture to come soon :)
]

# Styles for light mode and dark mode
LIGHT_MODE_STYLESHEET = """
QMainWindow {
    background-color: #ffffff;
}
QPushButton {
    background-color: #f0f0f0;
    border: 1px solid #a0a0a0;
    border-radius: 5px;
    padding: 5px;
}
QPushButton:hover {
    background-color: #e0e0e0;
}
QLabel {
    color: #000000;
    background-color: transparent;
}
"""
DARK_MODE_STYLESHEET = """
QMainWindow {
    background-color: #2c2f33;
}
QPushButton {
    background-color: #4f5d75;
    border: 1px solid #7289da;
    border-radius: 5px;
    padding: 5px;
    color: #ffffff;
}
QPushButton:hover {
    background-color: #536787;
}
QLabel {
    color: #ffffff;
    background-color: transparent;
}
"""

# ======= 3. GUI CLASSES =======
class RoomEditor(QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.mainWindow = parent
        self.room_boundary = []  # List of points forming the room boundary
        self.is_drawing = False  # For wall drawing
        self.furniture_items = []  # List of placed furniture
        self.current_furniture = None  # Furniture currently being placed
        self.undo_stack = []  # Stack for undo operations
        self.redo_stack = []  # Stack for redo operations
        self.current_line = None  # Temporary line for drawing previews
        self.current_furniture_data = None  # Data for current furniture placement

    def mousePressEvent(self, event):
        if self.mainWindow and self.mainWindow.mode == "room":  # Check mode safely
            if event.button() == Qt.MouseButton.LeftButton:
                if self.parent().mode == "room":  # Check current mode
                    pos = event.scenePos()
                    snapped_pos = self.snap_to_grid(pos.x(), pos.y())
                    if self.current_furniture: # this will never be true in room mode now
                        self.finalizeFurniturePlacement(snapped_pos)
                    else:
                        self.addWall(snapped_pos)
                        if len(self.room_boundary) >= 2:
                            self.is_drawing = True
            elif event.button() == Qt.MouseButton.RightButton: # Undo on Right Click
                self.undo()

    def mouseMoveEvent(self, event):
        if self.mainWindow and self.mainWindow.mode == "room":  # Check mode
            if self.parent().mode == "room":
                pos = event.scenePos()
                if self.is_drawing and len(self.room_boundary) > 0:
                    if self.current_line:
                        self.removeItem(self.current_line)

                    last_point = self.room_boundary[-1] 
                    snapped_pos = self.snap_to_grid(pos.x(), pos.y())
                    self.current_line = self.addLine(
                        last_point[0], last_point[1],
                        snapped_pos.x(), snapped_pos.y(),
                        QPen(Qt.GlobalColor.blue, 1)
                    )

    def mouseReleaseEvent(self, event):
        if self.mainWindow and event.button() == Qt.MouseButton.LeftButton and self.mainWindow.mode == "room":
            if event.button() == Qt.MouseButton.LeftButton and self.parent().mode == "room":
                if self.is_drawing:
                    pos = event.scenePos()
                    snapped_pos = self.snap_to_grid(pos.x(), pos.y())
                    if len(self.room_boundary) >= 1 and snapped_pos != self.room_boundary[-1]:
                        self.addWall(snapped_pos)
                    self.is_drawing = False
                    if len(self.room_boundary) >= 2:
                        self.finalizeWall()

    def addWall(self, pos):
        """Add a wall point and prevent duplicates."""
        pos_tuple = (pos.x(), pos.y())

        # Add only if this point is not the same as the last added point
        if len(self.room_boundary) == 0 or self.room_boundary[-1] != pos_tuple:
            self.room_boundary.append(pos_tuple)
            print(f"[INFO] Wall point added: {pos_tuple}")
        else:
            print("[DEBUG] Duplicate wall point ignored:", pos_tuple)

        # Additional step: Ensure the room does not have duplicate midpoints
        if len(self.room_boundary) > 1 and self.room_boundary[-1] == self.room_boundary[-2]:
            self.room_boundary.pop()
            print("[DEBUG] Consecutive duplicate wall point removed.")

    def finalizeWall(self):
        """Finalizes a wall segment."""
        if len(self.room_boundary) > 1:
            start_point = self.room_boundary[-2]  # Tuple: (x, y)
            end_point = self.room_boundary[-1]    # Tuple: (x, y)

            self.addLine(
                start_point[0], start_point[1],  # Use tuple indexing for x, y
                end_point[0], end_point[1],
                QPen(Qt.GlobalColor.black, 3)   # Black color for finalized wall
            )

    def startFurniturePlacement(self, name, width, height, constraints=None):
        """Creates a furniture item in a draggable mode."""
        rect = self.addRect(0, 0, width, height, QPen(Qt.GlobalColor.green), QBrush(Qt.GlobalColor.lightGray))
        rect.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
        # Save current furniture as a Furniture dataclass instance
        self.current_furniture_data = Furniture(
            name=name,
            width=width,
            height=height,
            constraints=constraints or {},
            rect=rect,
            geom=None  # No geometry yet
        )

    def toJSON(self):
        """Serialize scene furniture and room boundary to JSON."""
        def serialize_geometry(geom):
            return geom.bounds if geom else None

        def serialize_rect(rect):
            if rect is None:
                return None
            return {
                "x": rect.rect().x(),
                "y": rect.rect().y(),
                "width": rect.rect().width(),
                "height": rect.rect().height(),
            }

        layout = {
            "room_boundary": self.room_boundary,
            "furniture_items": [
                {
                    "name": furniture.name,
                    "width": furniture.width,
                    "height": furniture.height,
                    "constraints": furniture.constraints,
                    "rect": serialize_rect(furniture.rect),
                    "geom": serialize_geometry(furniture.geom),
                }
                for furniture in self.furniture_items
            ],
        }

        return json.dumps(layout, indent=4)

    def fromJSON(self, json_data):

        layout = json.loads(json_data) # Corrected json loading here
        self.clear()
        self.room_boundary = layout.get("room_boundary", [])
        furniture_items_data = layout.get("furniture_items", [])

        for item_data in furniture_items_data:
            rect_data = item_data.get("rect")  # Check if rect_data exists before accessing
            if rect_data:
                rect = self.addRect(
                    rect_data["x"], rect_data["y"], rect_data["width"], rect_data["height"],
                    QPen(Qt.GlobalColor.green), QBrush(Qt.GlobalColor.lightGray)
                )
                geom = box(rect.x(), rect.y(), rect.x() + rect.rect().width(), rect.y() + rect.rect().height())

                furniture = Furniture(
                    name=item_data["name"],
                    width=item_data["width"],
                    height=item_data["height"],
                    constraints=item_data["constraints"],
                    rect=rect,
                    geom=geom
                )
                self.furniture_items.append(furniture)

        self.updateRoom()  # Redraw walls

    def is_room_closed(self):
        """Check if the room boundary forms a closed, valid polygon."""
        if len(self.room_boundary) < 3:
            print("[ERROR] Room boundary must have at least 3 points.")
            return False

        # Ensure the room boundary starts and ends at the same point (explicit closure)
        if self.room_boundary[0] != self.room_boundary[-1]:
            self.room_boundary.append(self.room_boundary[0])
            print(f"[INFO] Closing the room boundary by adding the starting point: {self.room_boundary[0]}")

        # Create a Shapely polygon for validation
        polygon = Polygon(self.room_boundary)
        if not polygon.is_simple:
            print("[ERROR] Room boundary has self-intersecting edges.")
            return False
        if not polygon.is_valid:
            print("[ERROR] Room boundary is invalid (e.g., duplicate points or other issues).")
            return False

        print("[DEBUG] Room boundary is valid and closed.")
        return True

    def drawBackground(self, painter, rect):
        """Draw a simple grid as the background."""
        painter.fillRect(rect, QBrush(QColor("white")))
        painter.setPen(QPen(QColor(220, 220, 220), 0.5))
        left = int(rect.left()) - (int(rect.left()) % GRID_SIZE)
        top = int(rect.top()) - (int(rect.top()) % GRID_SIZE)

        grid_lines = []
        for x in range(left, int(rect.right()), GRID_SIZE):
            grid_lines.append(((x, rect.top()), (x, rect.bottom())))
        for y in range(top, int(rect.bottom()), GRID_SIZE):
            grid_lines.append(((rect.left(), y), (rect.right(), y)))

        for line in grid_lines:
            painter.drawLine(int(line[0][0]), int(line[0][1]), int(line[1][0]), int(line[1][1]))

    def snap_to_grid(self, x, y):
        snapped_x = round(x / GRID_SIZE) * GRID_SIZE
        snapped_y = round(y / GRID_SIZE) * GRID_SIZE
        return QPointF(snapped_x, snapped_y)

    def undo(self):
        """Undo the last action."""
        if self.room_boundary:
            self.redo_stack.append(self.room_boundary.pop())
            self.updateRoom()
        elif self.furniture_items:
            item = self.furniture_items.pop()
            self.removeItem(item.rect)  # Use Furniture's rect attribute
            self.redo_stack.append(item)

    def redo(self):
        """Redo the last undone action."""
        if not self.redo_stack:
            return

        item = self.redo_stack.pop()
        if isinstance(item, tuple):  # Handle room boundary
            self.room_boundary.append(item)
            self.updateRoom()
        elif isinstance(item, Furniture):  # Handle furniture
            rect = self.addRect(
                item.rect.x(), item.rect.y(),
                item.width, item.height,
                QPen(Qt.GlobalColor.green),
                QBrush(Qt.GlobalColor.lightGray)
            )
            rect.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
            item.rect = rect  # Restore Furniture's rect attribute
            self.furniture_items.append(item)

    def updateRoom(self):
        """Redraw walls from the room boundary."""
        # Clear only wall lines, leave furniture untouched
        for item in self.items():
            if isinstance(item, QGraphicsLineItem):
                self.removeItem(item)

        for i in range(len(self.room_boundary) - 1):
            start = self.room_boundary[i]        # Tuple: (x, y)
            end = self.room_boundary[i + 1]     # Tuple: (x, y)

            self.addLine(
                start[0], start[1],
                end[0], end[1],
                QPen(Qt.GlobalColor.black, 3)
            )

    def validateConstraintPlacement(self, furniture_geometry):
        """
        Validate if furniture placement satisfies overlap and near-wall constraints.
        """
        constraints = self.current_furniture_data.constraints
        if constraints.get("near_wall", False) and not self.is_near_wall(furniture_geometry, distance=20):
            print("[ERROR] Furniture must be near a wall!")
            return False

        for item in self.furniture_items:
            if item.geom.distance(furniture_geometry) < 20:  # Correctly access `geom` from Furniture
                print("[ERROR] Furniture is too close to another item!")
                return False

        return True

    def is_near_wall(self, furniture_geometry, distance=20):
        """
        Checks if any part of the furniture geometry is within `distance` pixels of a wall.
        Returns True if near a wall, False otherwise.
        """
        wall_lines = self.get_wall_lines()
        for wall in wall_lines:
            if wall.distance(furniture_geometry) <= distance:
                return True
        return False

    def get_wall_lines(self):
        """
        Returns the walls (edges) of the room as Shapely LineStrings for proximity testing.
        """
        wall_lines = []
        for i in range(len(self.room_boundary) - 1):
            start_point = self.room_boundary[i]
            end_point = self.room_boundary[i + 1]
            wall_lines.append(LineString([(start_point.x(), start_point.y()), (end_point.x(), end_point.y())]))
        return wall_lines

    def finalizeFurniturePlacement(self, pos):
        snapped_pos = self.snap_to_grid(pos.x(), pos.y())
        rect = self.current_furniture_data.rect
        rect.setPos(snapped_pos)

        # Create geometry for validation
        furniture_geometry = box(
            snapped_pos.x(), snapped_pos.y(),
            snapped_pos.x() + self.current_furniture_data.width,
            snapped_pos.y() + self.current_furniture_data.height
        )

        if not self.validateConstraintPlacement(furniture_geometry):
            print("[ERROR] Furniture placement failed due to invalid constraints!")
            self.removeItem(rect)  # Remove invalid furniture
            self.current_furniture_data = None
            return

        # Finalize and add to self.furniture_items
        self.current_furniture_data.geom = furniture_geometry
        self.furniture_items.append(self.current_furniture_data)  # Add to list
        print(f"[DEBUG] Furniture added: {self.current_furniture_data.name}, "
            f"Position: ({snapped_pos.x()}, {snapped_pos.y()})")
        self.current_furniture_data = None

    def applyRoomBoundaryConstraints(self, model, x_var, y_var, width, height, room_polygon, placed_var, name, i):
        """
        Enforce room boundary constraints by checking each grid cell of the furniture.
        """
        for gx in range(0, width, GRID_SIZE):
            for gy in range(0, height, GRID_SIZE):
                # Calculate grid cell center
                grid_center_x = x_var + gx + GRID_SIZE // 2
                grid_center_y = y_var + gy + GRID_SIZE // 2

                inside_room = model.NewBoolVar(f"inside_room_{name}_{i}_{gx}_{gy}")

                # Enforce that grid center points are inside the room polygon
                model.Add(room_polygon.contains(Point(grid_center_x, grid_center_y)) == inside_room).OnlyEnforceIf(placed_var)

                # Enforce that "inside_room" must be True if the furniture is placed
                model.AddBoolOr([inside_room]).OnlyEnforceIf(placed_var)

    def optimizeFurniturePlacement(self):
        """Optimize furniture placement."""
        print("[INFO] Starting optimization...")
        if not self.is_room_closed():  # Ensure the room boundary is ready
            print("[ERROR] Room boundary is not closed or invalid. Cannot optimize.")
            return

        # Generate room polygon
        room_polygon = Polygon(self.room_boundary)
        if not room_polygon.is_valid:
            print("[ERROR] Room polygon is invalid (e.g., self-intersecting). Check points.")
            return
        print(f"[DEBUG] Room polygon successfully created with {len(self.room_boundary)} points.")
        model = cp_model.CpModel()
        furniture_vars = []

        # Verify furniture items before optimization
        print(f"[DEBUG] Total furniture items: {len(self.furniture_items)}")
        for i, furniture in enumerate(self.furniture_items):
            print(f"[DEBUG] Furniture {i}: {furniture.name}, Width: {furniture.width}, Height: {furniture.height}")

            # Decision variables
            x_var = model.NewIntVar(0, WINDOW_WIDTH - furniture.width, f"x_{furniture.name}_{i}")
            y_var = model.NewIntVar(0, WINDOW_HEIGHT - furniture.height, f"y_{furniture.name}_{i}")
            placed_var = model.NewBoolVar(f"placed_{furniture.name}_{i}")

            # Apply room boundary constraints
            self.applyRoomBoundaryConstraints(model, x_var, y_var, furniture.width, furniture.height, room_polygon, placed_var, furniture.name, i)

            # Add furniture variables for optimization
            furniture_vars.append({
                "furniture": furniture,
                "x_var": x_var,
                "y_var": y_var,
                "placed_var": placed_var,
            })

        # Add non-overlap constraints
        for fi, fj in combinations(furniture_vars, 2):
            model.AddBoolOr([
                fi["x_var"] + fi["furniture"].width <= fj["x_var"],  # fi left of fj
                fj["x_var"] + fj["furniture"].width <= fi["x_var"],  # fj left of fi
                fi["y_var"] + fi["furniture"].height <= fj["y_var"],  # fi above fj
                fj["y_var"] + fj["furniture"].height <= fi["y_var"],  # fj above fi
            ]).OnlyEnforceIf(fi["placed_var"]).OnlyEnforceIf(fj["placed_var"])

        # Maximize the number of placed items
        model.Maximize(sum([var["placed_var"] for var in furniture_vars]))

        # Solve the optimization problem
        solver = cp_model.CpSolver()
        solver_status = solver.Solve(model)
        if solver_status in {cp_model.FEASIBLE, cp_model.OPTIMAL}:
            print("[INFO] Solution found. Updating positions...")
            for var in furniture_vars:
                if solver.BooleanValue(var["placed_var"]):
                    x = solver.Value(var["x_var"])
                    y = solver.Value(var["y_var"])
                    furniture = var["furniture"]

                    # Debug: Report positions from the solver
                    print(f"[DEBUG] Placing {furniture.name} at ({x}, {y})")

                    furniture.rect.setPos(x, y)  # Update position in scene

                    # Debug: Verify position
                    print(f"[DEBUG] Updated rect position for {furniture.name}: "
                        f"({furniture.rect.x()}, {furniture.rect.y()})")

                    # Update geometry
                    furniture.geom = box(x, y, x + furniture.width, y + furniture.height)

            # Repaint scene after updating positions
            self.update()
        else:
            print("[ERROR] No feasible solution found!")

    def addFurnitureToScene(self, x, y, width, height):
        """Add furniture item to the scene at a given position."""
        rect = self.addRect(x, y, width, height, QPen(Qt.GlobalColor.green), QBrush(Qt.GlobalColor.lightGray))
        self.furniture_items.append({
            "rect": rect,
            "geom": box(x, y, x + width, y + height)
        })

    def clearFurniture(self):
        """Clear all placed furniture from the scene."""
        for item in self.furniture_items:
            self.removeItem(item["rect"])
        self.furniture_items = []

    def keyPressEvent(self, event):
        if self.mainWindow and self.mainWindow.mode == "furniture" and self.current_furniture:
                dx, dy = 0, 0
                if event.key() == Qt.Key.Key_Up:
                    dy = -GRID_SIZE
                elif event.key() == Qt.Key.Key_Down:
                    dy = GRID_SIZE
                elif event.key() == Qt.Key.Key_Left:
                    dx = -GRID_SIZE
                elif event.key() == Qt.Key.Key_Right:
                    dx = GRID_SIZE

                # Update position
                rect = self.current_furniture.rect
                rect.setPos(rect.x() + dx, rect.y() + dy)

                # Snap position to grid
                snapped_pos = self.snap_to_grid(rect.x(), rect.y())
                rect.setPos(snapped_pos)

                # Validate the new position
                geometry = box(
                    snapped_pos.x(), snapped_pos.y(),
                    snapped_pos.x() + self.current_furniture.width,
                    snapped_pos.y() + self.current_furniture.height,
                )

                if not self.validateConstraintPlacement(geometry):
                    print("[ERROR] Invalid positioning!")
                    rect.setBrush(QBrush(Qt.GlobalColor.red))
                else:
                    rect.setBrush(QBrush(Qt.GlobalColor.green))

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("OPtiPlace")
        self.setGeometry(100, 100, WINDOW_WIDTH, WINDOW_HEIGHT)

        self.scene = RoomEditor(self)  # Pass 'self' (MainWindow) as parent
        self.view = QGraphicsView(self.scene)  # Initialize after setting the scene
        self.view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) # Allow view to expand

        self.view.setFocusPolicy(Qt.FocusPolicy.NoFocus)      # Disable focus for the view
        self.scene.setFocus()                        # Set focus for the scene.

        self.mode = "room"  # Initial mode
        self.dark_mode_enabled = False
        self.createUI()
        self.applyDarkMode()  # Apply initial theme

    def createUI(self):
        """Creates and organizes the user interface."""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        main_layout = QVBoxLayout()
        central_widget.setLayout(main_layout)

        # Graphics View (Expands to fill available space)
        main_layout.addWidget(self.view)

        # Controls Layout
        controls_layout = QHBoxLayout()
        main_layout.addLayout(controls_layout)

        # Furniture Selection
        furniture_label = QLabel("Furniture:")
        self.furniture_dropdown = QComboBox()
        self.furniture_dropdown.addItem("Select Furniture")
        for item in FURNITURE_DB:
            self.furniture_dropdown.addItem(f"{item['name']} ({item['width']}x{item['height']})")
        self.furniture_dropdown.currentIndexChanged.connect(self.selectFurniture)

        controls_layout.addWidget(furniture_label)
        controls_layout.addWidget(self.furniture_dropdown)

        # Status Label
        self.status_label = QLabel("Ready")
        controls_layout.addWidget(self.status_label)

        # Action Buttons (Grouped)
        actions_layout = QVBoxLayout()
        controls_layout.addLayout(actions_layout)

        # Mode Toggle Button
        self.mode_button = QPushButton("Switch to Furniture Mode")  # Initial text
        self.mode_button.clicked.connect(self.toggleMode)
        actions_layout.addWidget(self.mode_button) # Add to actions layout

        button_row_1 = QHBoxLayout()
        actions_layout.addLayout(button_row_1)
        clear_button = QPushButton("Clear")
        clear_button.clicked.connect(self.clearScene)
        save_button = QPushButton("Save")
        save_button.clicked.connect(self.saveLayout)
        load_button = QPushButton("Load")
        load_button.clicked.connect(self.loadLayout)

        button_row_1.addWidget(clear_button)
        button_row_1.addWidget(save_button)
        button_row_1.addWidget(load_button)

        button_row_2 = QHBoxLayout()
        actions_layout.addLayout(button_row_2)
        auto_place_button = QPushButton("Auto Place")  # Shorter label
        auto_place_button.clicked.connect(self.scene.optimizeFurniturePlacement)
        undo_button = QPushButton("Undo")
        undo_button.clicked.connect(self.scene.undo)
        redo_button = QPushButton("Redo")
        redo_button.clicked.connect(self.scene.redo)
        theme_button = QPushButton("Toggle Theme")
        theme_button.clicked.connect(self.toggleTheme)

        button_row_2.addWidget(auto_place_button)
        button_row_2.addWidget(undo_button)
        button_row_2.addWidget(redo_button)
        button_row_2.addWidget(theme_button)

    def toggleMode(self):
        if self.mode == "room":
            self.mode = "furniture"
            self.mode_button.setText("Switch to Room Mode")
            self.furniture_dropdown.setEnabled(True)  # Enable dropdown in furniture mode

            # Clear selection of the room boundary
            if self.scene.current_line:
                self.scene.removeItem(self.scene.current_line)

        elif self.mode == "furniture":
            self.mode = "room"
            self.mode_button.setText("Switch to Furniture Mode")
            self.furniture_dropdown.setEnabled(False)  # Disable dropdown
            self.scene.current_furniture = None

    def toggleTheme(self):
        """Switch between light and dark themes."""
        if self.dark_mode_enabled:
            self.applyLightMode()
        else:
            self.applyDarkMode()

    def applyLightMode(self):
        """Apply light mode theme."""
        self.setStyleSheet(LIGHT_MODE_STYLESHEET)
        self.dark_mode_enabled = False

    def applyDarkMode(self):
        """Apply dark mode theme."""
        self.setStyleSheet(DARK_MODE_STYLESHEET)
        self.dark_mode_enabled = True

    def selectFurniture(self):
        index = self.furniture_dropdown.currentIndex()
        if index > 0:  # 0 is "Select Furniture"
            furniture_data = FURNITURE_DB[index - 1]
            self.scene.startFurniturePlacement(
                furniture_data["name"],
                furniture_data["width"],
                furniture_data["height"],
                furniture_data["constraints"],
            )

    def clearScene(self):
        """Clear the entire scene and reset state."""
        print("[INFO] Clearing the scene...")
        self.scene.clear()
        self.scene.room_boundary = []
        self.scene.furniture_items = []
        self.furniture_dropdown.setCurrentIndex(0)

    def saveLayout(self):
        """Save the current layout to a JSON file."""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(self, "Save Layout", "", "JSON Files (*.json)", options=options)
        if file_path:
            with open(file_path, "w") as file:
                file.write(self.scene.toJSON())
            print(f"[INFO] Layout saved to: {file_path}")

    def loadLayout(self):
        """Load a layout from a JSON file."""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(self, "Load Layout", "", "JSON Files (*.json)", options=options)
        if file_path:
            with open(file_path, "r") as file:
                data = json.load(file)
                self.scene.fromJSON(data)
            print(f"[INFO] Layout loaded from: {file_path}")

# ======= 4. COMPUTATIONAL GEOMETRY =======
def check_furniture_placement(furniture, room_boundary):
    """
    Checks if a furniture item can be placed inside the room.
    - `furniture`: A dictionary (e.g., an entry from FURNITURE_DB).
    - `room_boundary`: List of (x, y) tuples representing the room.
    """
    room_polygon = Polygon(room_boundary)
    furniture_polygon = Polygon([
        (furniture["x"], furniture["y"]),
        (furniture["x"] + furniture["width"], furniture["y"]),
        (furniture["x"] + furniture["width"], furniture["y"] + furniture["height"]),
        (furniture["x"], furniture["y"] + furniture["height"])
    ])
    if not room_polygon.contains(furniture_polygon):
        print("[ERROR] Furniture does not fit the room constraints.")
        return False
    return True

# ======= 6. MAIN APP LOGIC =======
if __name__ == "__main__":
    # Create the PyQt application
    app = QApplication(sys.argv)

    # Initialize the main window
    main_window = MainWindow()
    main_window.show()

    # Execute the application
    sys.exit(app.exec())