Option Explicit On
Option Strict On

Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.IO
Imports System.Text.Json
Imports System.Linq

Public Class frmDesigner
Inherits Form

#Region "Variables"
Private Const INDENT As Integer = 20
Private Const SQCOUNTX As Integer = 15
Private Const SQCOUNTY As Integer = 11
Private Const WALL_THICKNESS As Integer = 10

Friend wallsHatchStyle As HatchStyle = HatchStyle.Cross
Friend wallsColor1 As Color = Color.Gray
Friend wallsColor2 As Color = Color.DarkGray
Friend floorsHatchStyle As HatchStyle = HatchStyle.SmallCheckerBoard
Friend floorsColor1 As Color = Color.LightGray
Friend floorsColor2 As Color = Color.White

Friend floorsUseEdgeStyle()() As Boolean ' New array to track edge style usage

Private SQSizeX As Integer
Private SQSizeY As Integer

Private isRightMouseDown As Boolean = False
Private isLeftMouseDown As Boolean = False
Private isMouseInsideGrid As Boolean = False

Private highlightedX As Integer
Private highlightedY As Integer
Private highlightedWallX As Boolean = False
Private highlightedWallY As Boolean = False
Private highlightColor As Color = Color.Black
Private highlightTimer As Timer

Private isRestoringState As Boolean = False
Private isActionInProgress As Boolean = False
Private undoStack As New Stack(Of RoomState)
Private redoStack As New Stack(Of RoomState)

Private ReadOnly projectDir As String = GetProjectDirectory()
Private ReadOnly premadesFolder As String = Path.Combine(projectDir, "Data", "Premades")
Private ReadOnly savesFolder As String = Path.Combine(projectDir, "Data", "Saves")
Private ReadOnly musicFolder As String = Path.Combine(projectDir, "Data", "Music")
Private currentFilePath As String = ""
Friend SelectedMusic As String = ""

Friend verticalWalls()() As Boolean
Friend horizontalWalls()() As Boolean

Friend placedItems()() As PlacedItem
Friend verticalWallItems()() As PlacedItem
Friend horizontalWallItems()() As PlacedItem

Private animatedImages As New List(Of Image)
Private animationTimer As Timer

Private isDraggingItem As Boolean = False
Private draggedItem As PlacedItem = Nothing
Private draggedItemIsWall As Boolean = False
Private draggedItemIsVerticalWall As Boolean = False
Private draggedItemOriginalX As Integer
Private draggedItemOriginalY As Integer

Private mouseX As Integer
Private mouseY As Integer

Private rand As New Random()

Private rightMouseDownPosition As Point
Private rightMouseDownTime As DateTime
Private Const CLICK_DRAG_THRESHOLD As Integer = 5 ' pixels
Private Const CLICK_DRAG_THRESHOLD_SQUARED As Integer = CLICK_DRAG_THRESHOLD * CLICK_DRAG_THRESHOLD

Private isSelecting As Boolean = False
Private isInSelectionMode As Boolean = False
Private selectionStartPoint As Point
Private selectionEndPoint As Point
Private selectionRectangle As Rectangle

' Unique ID Counters
Private itemCounters As New Dictionary(Of String, Integer)()

' Events for Selection
Public Event ItemSelected(selectedID As String)
Public Event TargetSelected(selectedID As String)

' Selection Mode Enumeration
Public Enum SelectionModeType
    None
    SelectItem
    SelectTarget
End Enum

Private currentSelectionMode As SelectionModeType = SelectionModeType.None

#End Region

#Region "Constructor and Initialization"

Public Sub New()
    InitializeComponent()
    InitializeWalls()
    InitializeTimers()
    InitializeEventHandlers()
    LoadPremades()
    LoadMusic()
    InitializeAnimationForImages()
    If Not isRestoringState Then SaveStateForUndo()
End Sub

Private Function GetProjectDirectory() As String
    Dim dir As DirectoryInfo = New DirectoryInfo(Application.StartupPath)
    For i As Integer = 1 To 4 ' Adjust this number based on your directory structure
        If dir.Parent IsNot Nothing Then
            dir = dir.Parent
        End If
    Next
    Return dir.FullName
End Function

Private Sub InitializeWalls()
    verticalWalls = New Boolean(SQCOUNTX)() {}
    verticalWallItems = New PlacedItem(SQCOUNTX)() {}
    For x As Integer = 0 To SQCOUNTX
        verticalWalls(x) = New Boolean(SQCOUNTY - 1) {}
        verticalWallItems(x) = New PlacedItem(SQCOUNTY - 1) {}
        For y As Integer = 0 To SQCOUNTY - 1
            verticalWalls(x)(y) = True
            verticalWallItems(x)(y) = Nothing
        Next
    Next

    horizontalWalls = New Boolean(SQCOUNTX - 1)() {}
    horizontalWallItems = New PlacedItem(SQCOUNTX - 1)() {}
    For x As Integer = 0 To SQCOUNTX - 1
        horizontalWalls(x) = New Boolean(SQCOUNTY) {}
        horizontalWallItems(x) = New PlacedItem(SQCOUNTY) {}
        For y As Integer = 0 To SQCOUNTY
            horizontalWalls(x)(y) = True
            horizontalWallItems(x)(y) = Nothing
        Next
    Next

    ' Initialize the outer walls (borders)
    For y As Integer = 0 To SQCOUNTY - 1
        verticalWalls(0)(y) = True
        verticalWalls(SQCOUNTX)(y) = True
    Next

    For x As Integer = 0 To SQCOUNTX - 1
        horizontalWalls(x)(0) = True
        horizontalWalls(x)(SQCOUNTY) = True
    Next

    ' Initialize placed items
    placedItems = New PlacedItem(SQCOUNTX - 1)() {}
    For x As Integer = 0 To SQCOUNTX - 1
        placedItems(x) = New PlacedItem(SQCOUNTY - 1) {}
        For y As Integer = 0 To SQCOUNTY - 1
            placedItems(x)(y) = Nothing
        Next
    Next

    ' Initialize floorsUseEdgeStyle
    floorsUseEdgeStyle = New Boolean(SQCOUNTX - 1)() {}
    For x As Integer = 0 To SQCOUNTX - 1
        floorsUseEdgeStyle(x) = New Boolean(SQCOUNTY - 1) {}
        For y As Integer = 0 To SQCOUNTY - 1
            floorsUseEdgeStyle(x)(y) = False
        Next
    Next
End Sub

Private Sub InitializeTimers()
    highlightTimer = New Timer() With {.Interval = 500}
    AddHandler highlightTimer.Tick, AddressOf HighlightTimer_Tick
    highlightTimer.Start()

    animationTimer = New Timer() With {.Interval = 100}
    AddHandler animationTimer.Tick, AddressOf OnAnimationTick
    animationTimer.Start()
End Sub

Private Sub InitializeEventHandlers()
    AddColorPictureBoxHandlers(Me)

    ' Add handlers for item picture boxes
    AddHandler picBlock1.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBaddy1.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBaddy2.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picLazer1.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picLazer2.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picFlipper1.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picFlipper2.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picFlipper3.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picFlipper4.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBaddy3.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBaddy4.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picCoin.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picGate.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picKey.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picStart.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picFinish.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBlockStopper1.MouseDown, AddressOf PicBlock_MouseDown
    AddHandler picBall.MouseDown, AddressOf PicBlock_MouseDown

    ' Add handlers for form-level mouse events
    AddHandler Me.MouseMove, AddressOf Form_MouseMove
    AddHandler Me.MouseUp, AddressOf Form_MouseUp

    ' Add handlers for picGame
    AddHandler picGame.MouseDown, AddressOf PicGame_MouseDown
    AddHandler picGame.MouseUp, AddressOf PicGame_MouseUp
    AddHandler picGame.MouseMove, AddressOf PicGame_MouseMove
    AddHandler picGame.Paint, AddressOf PicGame_Paint

    ' Add handlers for buttons
    AddHandler btnUndo.Click, AddressOf btnUndo_Click
    AddHandler btnRedo.Click, AddressOf btnRedo_Click
    AddHandler btnAll.Click, AddressOf btnAll_Click
    AddHandler btnWall.Click, AddressOf btnWalls_Click
    AddHandler btnFloor.Click, AddressOf btnFloors_Click
    AddHandler btnSavePremade.Click, AddressOf btnSavePremade_Click

    ' Add handlers for menu items
    AddHandler mnuNew.Click, AddressOf mnuNew_Click
    AddHandler mnuLoad.Click, AddressOf mnuLoad_Click
    AddHandler mnuSave.Click, AddressOf mnuSave_Click
    AddHandler mnuSaveAs.Click, AddressOf mnuSaveAs_Click

    ' Add handler for premades list
    AddHandler lstPremades.SelectedIndexChanged, AddressOf lstPremades_SelectedIndexChanged

    ' Add handler for hatch styles list
    AddHandler lstHatchstyles.SelectedIndexChanged, AddressOf lstHatchstyles_SelectedIndexChanged
    AddHandler cboMusic.SelectedIndexChanged, AddressOf cboMusic_SelectedIndexChanged

End Sub

Private Sub InitializeAnimationForImages()
    animatedImages.Add(picBaddy1.Image)
    animatedImages.Add(picBaddy2.Image)
    animatedImages.Add(picBaddy3.Image)
    animatedImages.Add(picBaddy4.Image)
    animatedImages.Add(picLazer1.Image)
    animatedImages.Add(picLazer2.Image)
    animatedImages.Add(picFlipper1.Image)
    animatedImages.Add(picFlipper2.Image)
    animatedImages.Add(picFlipper3.Image)
    animatedImages.Add(picFlipper4.Image)
    animatedImages.Add(picCoin.Image)
    animatedImages.Add(picGate.Image)
    animatedImages.Add(picKey.Image)
    animatedImages.Add(picStart.Image)
    animatedImages.Add(picFinish.Image)
    animatedImages.Add(picBlockStopper1.Image)
    animatedImages.Add(picBall.Image)

    For Each img In animatedImages
        If img IsNot Nothing Then
            ImageAnimator.Animate(img, AddressOf OnFrameChanged)
        End If
    Next
End Sub

#End Region

#Region "Event Handlers"

Private Sub frmDesigner_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    LoadMusic()
    LoadPremades()

    ' Populate lstHatchstyles with hatch style names
    lstHatchstyles.Items.Clear()
    For Each hs As HatchStyle In [Enum].GetValues(GetType(HatchStyle))
        lstHatchstyles.Items.Add(hs.ToString())
    Next

    ' Set default selections
    UpdateSelections()
End Sub

Private Sub LoadPremades()
    If Not Directory.Exists(premadesFolder) Then Directory.CreateDirectory(premadesFolder)
    lstPremades.Items.Clear()
    For Each file As String In Directory.GetFiles(premadesFolder, "*.Premade")
        lstPremades.Items.Add(Path.GetFileNameWithoutExtension(file))
    Next
End Sub

Private Sub LoadMusic()
    If Not Directory.Exists(musicFolder) Then Directory.CreateDirectory(musicFolder)
    cboMusic.Items.Clear()
    For Each file As String In Directory.GetFiles(musicFolder, "*.wav")
        cboMusic.Items.Add(Path.GetFileNameWithoutExtension(file))
    Next
End Sub

Private Sub OnAnimationTick(sender As Object, e As EventArgs)
    For Each img In animatedImages
        ImageAnimator.UpdateFrames(img)
    Next
    picGame.Invalidate()
End Sub

Private Sub OnFrameChanged(o As Object, e As EventArgs)
    ' Required but can be left empty
End Sub

Private Sub HighlightTimer_Tick(sender As Object, e As EventArgs)
    highlightColor = If(highlightColor = Color.Black, Color.White, Color.Black)
    picGame.Invalidate()
End Sub

Private Sub lstPremades_SelectedIndexChanged(sender As Object, e As EventArgs)
    If lstPremades.SelectedIndex >= 0 Then
        If Not isRestoringState Then SaveStateForUndo()
        Dim selectedPremade As String = lstPremades.SelectedItem.ToString()
        LoadSettingsFromFile(Path.Combine(premadesFolder, selectedPremade & ".Premade"))

        ' Update lstHatchstyles selection after loading settings
        UpdateSelections()
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuClearWalls_Click(sender As Object, e As EventArgs) Handles mnuClearWalls.Click
    If Not selectionRectangle.IsEmpty Then
        If Not isRestoringState Then SaveStateForUndo()

        For Each pt As Point In GetVerticalWallsInRectangle(selectionRectangle)
            If pt.X > 0 AndAlso pt.X < SQCOUNTX Then
                verticalWalls(pt.X)(pt.Y) = False
                verticalWallItems(pt.X)(pt.Y) = Nothing
            End If
        Next

        For Each pt As Point In GetHorizontalWallsInRectangle(selectionRectangle)
            If pt.Y > 0 AndAlso pt.Y < SQCOUNTY Then
                horizontalWalls(pt.X)(pt.Y) = False
                horizontalWallItems(pt.X)(pt.Y) = Nothing
            End If
        Next

        selectionRectangle = Rectangle.Empty
        isInSelectionMode = False
        Cursor = Cursors.Default
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuFillWalls_Click(sender As Object, e As EventArgs) Handles mnuFillWalls.Click
    If Not selectionRectangle.IsEmpty Then
        If Not isRestoringState Then SaveStateForUndo()

        For Each pt As Point In GetVerticalWallsInRectangle(selectionRectangle)
            If pt.X > 0 AndAlso pt.X < SQCOUNTX Then
                verticalWalls(pt.X)(pt.Y) = True
            End If
        Next

        For Each pt As Point In GetHorizontalWallsInRectangle(selectionRectangle)
            If pt.Y > 0 AndAlso pt.Y < SQCOUNTY Then
                horizontalWalls(pt.X)(pt.Y) = True
            End If
        Next

        selectionRectangle = Rectangle.Empty
        isInSelectionMode = False
        Cursor = Cursors.Default
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuClearObjects_Click(sender As Object, e As EventArgs) Handles mnuClearObjects.Click
    If Not selectionRectangle.IsEmpty Then
        If Not isRestoringState Then SaveStateForUndo()

        For Each pt As Point In GetCellsInRectangle(selectionRectangle)
            placedItems(pt.X)(pt.Y) = Nothing
        Next

        For Each pt As Point In GetVerticalWallsInRectangle(selectionRectangle)
            verticalWallItems(pt.X)(pt.Y) = Nothing
        Next

        For Each pt As Point In GetHorizontalWallsInRectangle(selectionRectangle)
            horizontalWallItems(pt.X)(pt.Y) = Nothing
        Next

        selectionRectangle = Rectangle.Empty
        isInSelectionMode = False
        Cursor = Cursors.Default
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuClearEdge_Click(sender As Object, e As EventArgs) Handles mnuClearEdge.Click
    If Not selectionRectangle.IsEmpty Then
        If Not isRestoringState Then SaveStateForUndo()

        For Each pt As Point In GetCellsInRectangle(selectionRectangle)
            floorsUseEdgeStyle(pt.X)(pt.Y) = False
        Next

        selectionRectangle = Rectangle.Empty
        isInSelectionMode = False
        Cursor = Cursors.Default
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuAddEdge_Click(sender As Object, e As EventArgs) Handles mnuAddEdge.Click
    ' Implementation for adding edge can be added here
    ' For example, toggling edge styles based on selection
End Sub

Private Sub btnSelect_Click(sender As Object, e As EventArgs) Handles btnSelect.Click
    If isInSelectionMode Then
        isInSelectionMode = False
        Cursor = Cursors.Default
        selectionRectangle = Rectangle.Empty
        picGame.Invalidate()
    Else
        isInSelectionMode = True
        Cursor = Cursors.Cross
    End If
End Sub

Private Sub mnuCancel_Click(sender As Object, e As EventArgs) Handles mnuCancel.Click
    ' Cancel the selection
    selectionRectangle = Rectangle.Empty
    isInSelectionMode = False
    Cursor = Cursors.Default
    picGame.Invalidate()
End Sub

Private Sub mnuNew_Click(sender As Object, e As EventArgs) Handles mnuNew.Click
    If Not isRestoringState Then SaveStateForUndo()
    wallsHatchStyle = HatchStyle.Cross
    wallsColor1 = Color.Gray
    wallsColor2 = Color.DarkGray
    floorsHatchStyle = HatchStyle.SmallCheckerBoard
    floorsColor1 = Color.LightGray
    floorsColor2 = Color.White
    InitializeWalls()
    currentFilePath = ""

    ' Clear level name and music selection
    txtName.Text = ""
    SelectedMusic = ""
    cboMusic.SelectedIndex = -1

    ' Update selections
    UpdateSelections()

    picGame.Invalidate()
End Sub

Private Sub mnuSave_Click(sender As Object, e As EventArgs) Handles mnuSave.Click
    If String.IsNullOrEmpty(currentFilePath) Then
        mnuSaveAs_Click(sender, e)
    Else
        SaveRoomToFile(currentFilePath)
    End If
End Sub

Private Sub mnuLoad_Click(sender As Object, e As EventArgs) Handles mnuLoad.Click
    Dim openDialog As New OpenFileDialog() With {
        .InitialDirectory = savesFolder,
        .Filter = "Room Files (*.room)|*.room",
        .DefaultExt = "room",
        .AddExtension = True,
        .Title = "Load Room"
    }
    If openDialog.ShowDialog() = DialogResult.OK Then
        If Not isRestoringState Then SaveStateForUndo()
        LoadRoomFromFile(openDialog.FileName)
        currentFilePath = openDialog.FileName

        ' Update lstHatchstyles selection
        UpdateSelections()
        picGame.Invalidate()
    End If
End Sub

Private Sub mnuSaveAs_Click(sender As Object, e As EventArgs) Handles mnuSaveAs.Click
    Dim saveDialog As New SaveFileDialog() With {
        .InitialDirectory = savesFolder,
        .Filter = "Room Files (*.room)|*.room",
        .DefaultExt = "room",
        .AddExtension = True,
        .Title = "Save Room As"
    }
    If saveDialog.ShowDialog() = DialogResult.OK Then
        SaveRoomToFile(saveDialog.FileName)
        currentFilePath = saveDialog.FileName
    End If
End Sub

Private Sub btnUndo_Click(sender As Object, e As EventArgs) Handles btnUndo.Click
    If undoStack.Count > 1 Then
        SaveStateForRedo()
        undoStack.Pop()
        isRestoringState = True
        Dim previousState As RoomState = undoStack.Peek()
        previousState.RestoreState(Me)
        isRestoringState = False

        ' Update selections
        UpdateSelections()

        picGame.Invalidate()
    End If
End Sub

Private Sub btnRedo_Click(sender As Object, e As EventArgs) Handles btnRedo.Click
    If redoStack.Count > 0 Then
        SaveStateForUndo()
        isRestoringState = True
        Dim redoState As RoomState = redoStack.Pop()
        redoState.RestoreState(Me)
        isRestoringState = False

        ' Update selections
        UpdateSelections()

        picGame.Invalidate()
    End If
End Sub

Private Sub btnSavePremade_Click(sender As Object, e As EventArgs) Handles btnSavePremade.Click
    Dim saveDialog As New SaveFileDialog() With {
        .InitialDirectory = premadesFolder,
        .Filter = "Premade Files (*.Premade)|*.Premade",
        .DefaultExt = "Premade",
        .AddExtension = True,
        .Title = "Save Premade"
    }
    If saveDialog.ShowDialog() = DialogResult.OK Then
        SaveSettingsToFile(saveDialog.FileName)
        LoadPremades()
    End If
End Sub

Private Sub lstHatchstyles_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstHatchstyles.SelectedIndexChanged
    If lstHatchstyles.SelectedItem IsNot Nothing Then
        Dim selectedHatchStyleName As String = lstHatchstyles.SelectedItem.ToString()
        Dim selectedHatchStyle As HatchStyle
        If [Enum].TryParse(selectedHatchStyleName, selectedHatchStyle) Then
            If rdoFloors.Checked Then
                floorsHatchStyle = selectedHatchStyle
            ElseIf rdoWalls.Checked Then
                wallsHatchStyle = selectedHatchStyle
            End If
            If Not isRestoringState Then SaveStateForUndo()
            picGame.Invalidate()
        End If
    End If
End Sub

Private Sub cboMusic_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboMusic.SelectedIndexChanged
    SelectedMusic = If(cboMusic.SelectedItem?.ToString(), "")
    If Not isRestoringState Then SaveStateForUndo()
End Sub

Private Sub btnAll_Click(sender As Object, e As EventArgs) Handles btnAll.Click
    If Not isRestoringState Then SaveStateForUndo()
    RandomizeWalls()
    RandomizeFloors()

    ' Update the HatchStyle list selection
    UpdateSelections()
    picGame.Invalidate()
End Sub

Private Sub btnWalls_Click(sender As Object, e As EventArgs) Handles btnWall.Click
    If Not isRestoringState Then SaveStateForUndo()
    RandomizeWalls()
    picGame.Invalidate()
End Sub

Private Sub btnFloors_Click(sender As Object, e As EventArgs) Handles btnFloor.Click
    If Not isRestoringState Then SaveStateForUndo()
    RandomizeFloors()
    picGame.Invalidate()
End Sub

Private Sub rdoWalls_CheckedChanged(sender As Object, e As EventArgs) Handles rdoWalls.CheckedChanged
    If rdoWalls.Checked Then
        UpdateSelections()
    End If
End Sub

Private Sub rdoFloors_CheckedChanged(sender As Object, e As EventArgs) Handles rdoFloors.CheckedChanged
    If rdoFloors.Checked Then
        UpdateSelections()
    End If
End Sub

Private Sub rdoEdge_CheckedChanged(sender As Object, e As EventArgs) Handles rdoEdge.CheckedChanged
    If rdoEdge.Checked Then
        UpdateSelections()
    End If
End Sub

Private Sub PicGame_Paint(sender As Object, e As PaintEventArgs) Handles picGame.Paint
    CalculateSquareSizes(picGame.ClientSize.Width, picGame.ClientSize.Height)
    e.Graphics.SmoothingMode = SmoothingMode.None
    DrawGrid(e.Graphics)
    DrawHighlight(e.Graphics)

    If isDraggingItem AndAlso draggedItem IsNot Nothing Then
        Dim img As Image = GetImageByKey(draggedItem.ImageKey)
        If img IsNot Nothing Then
            ImageAnimator.UpdateFrames(img)
            Dim drawRect As Rectangle

            If draggedItemIsWall Then
                If draggedItemIsVerticalWall Then
                    ' Draw vertical wall item under cursor
                    Dim wallWidth As Integer = WALL_THICKNESS
                    Dim wallHeight As Integer = SQSizeY
                    Dim xPos As Integer = mouseX - WALL_THICKNESS \ 2
                    Dim yPos As Integer = mouseY - SQSizeY \ 2

                    ' Keep original image width, stretch height
                    Dim newWidth As Integer = img.Width
                    Dim newHeight As Integer = wallHeight

                    ' Center horizontally
                    Dim centeredX As Integer = xPos + (wallWidth - newWidth) \ 2
                    drawRect = New Rectangle(centeredX, yPos, newWidth, newHeight)
                Else
                    ' Draw horizontal wall item under cursor
                    Dim wallWidth As Integer = SQSizeX
                    Dim wallHeight As Integer = WALL_THICKNESS
                    Dim xPos As Integer = mouseX - SQSizeX \ 2
                    Dim yPos As Integer = mouseY - WALL_THICKNESS \ 2

                    ' Stretch width, keep original image height
                    Dim newWidth As Integer = wallWidth
                    Dim newHeight As Integer = img.Height

                    ' Center vertically
                    Dim centeredY As Integer = yPos + (wallHeight - newHeight) \ 2
                    drawRect = New Rectangle(xPos, centeredY, newWidth, newHeight)
                End If
            Else
                ' Draw floor item under cursor
                Dim xPos As Integer = mouseX - SQSizeX \ 2
                Dim yPos As Integer = mouseY - SQSizeY \ 2
                drawRect = New Rectangle(xPos, yPos, SQSizeX, SQSizeY)
            End If
            e.Graphics.DrawImage(img, drawRect)
        End If
    End If

    If isInSelectionMode AndAlso Not selectionRectangle.IsEmpty Then
        Using selectionBrush As New SolidBrush(Color.FromArgb(64, Color.Blue))
            e.Graphics.FillRectangle(selectionBrush, selectionRectangle)
        End Using
        Using selectionPen As New Pen(Color.Blue, 2) With {.DashStyle = DashStyle.Dash}
            e.Graphics.DrawRectangle(selectionPen, selectionRectangle)
        End Using
    End If
End Sub

Private Sub Form_MouseMove(sender As Object, e As MouseEventArgs)
    If isDraggingItem Then
        Dim screenPos As Point = Me.PointToScreen(New Point(e.X, e.Y))
        Dim picGameClientPoint As Point = picGame.PointToClient(screenPos)

        mouseX = picGameClientPoint.X
        mouseY = picGameClientPoint.Y

        If picGame.ClientRectangle.Contains(picGameClientPoint) Then
            UpdateHighlight(mouseX, mouseY)
            isMouseInsideGrid = True
        Else
            isMouseInsideGrid = False
        End If

        picGame.Invalidate()
    End If
End Sub

Private Sub Form_MouseUp(sender As Object, e As MouseEventArgs)
    If isDraggingItem AndAlso e.Button = MouseButtons.Left Then
        isDraggingItem = False
        Me.Capture = False
        Cursor.Current = Cursors.Default

        Dim screenPos As Point = Me.PointToScreen(New Point(e.X, e.Y))
        Dim picGameClientPoint As Point = picGame.PointToClient(screenPos)

        If picGame.ClientRectangle.Contains(picGameClientPoint) Then
            UpdateHighlight(picGameClientPoint.X, picGameClientPoint.Y)

            ' Place the item if the drop location is valid
            If isMouseInsideGrid Then
                If highlightedWallX AndAlso draggedItemIsWall AndAlso draggedItemIsVerticalWall Then
                    ' Place vertical wall item
                    verticalWallItems(highlightedX)(highlightedY) = draggedItem
                    verticalWalls(highlightedX)(highlightedY) = False
                ElseIf highlightedWallY AndAlso draggedItemIsWall AndAlso Not draggedItemIsVerticalWall Then
                    ' Place horizontal wall item
                    horizontalWallItems(highlightedX)(highlightedY) = draggedItem
                    horizontalWalls(highlightedX)(highlightedY) = False
                ElseIf Not highlightedWallX AndAlso Not highlightedWallY AndAlso Not draggedItemIsWall Then
                    ' Place floor item
                    placedItems(highlightedX)(highlightedY) = draggedItem
                Else
                    ' Invalid drop location, return item to original location
                    ReturnItemToOriginalLocation()
                End If
            Else
                ' Not inside grid, return item to original location
                ReturnItemToOriginalLocation()
            End If

            draggedItem = Nothing
            If isActionInProgress Then
                isActionInProgress = False
                redoStack.Clear()
            End If

            picGame.Invalidate()
        Else
            ' Not over picGame, return item to original location
            ReturnItemToOriginalLocation()
        End If
    End If
End Sub

Private Sub PicGame_MouseDown(sender As Object, e As MouseEventArgs) Handles picGame.MouseDown
    If isInSelectionMode Then
        If e.Button = MouseButtons.Left Then
            isSelecting = True
            selectionStartPoint = e.Location
            selectionEndPoint = e.Location
            selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)
            picGame.Invalidate()
        End If
    Else
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = True
            rightMouseDownPosition = e.Location
            rightMouseDownTime = DateTime.Now
            If Not isActionInProgress Then
                isActionInProgress = True
                If Not isRestoringState Then SaveStateForUndo()
            End If
        ElseIf e.Button = MouseButtons.Left Then
            UpdateHighlight(e.X, e.Y)
            Dim itemPickedUp As Boolean = False
            If isMouseInsideGrid Then
                If Not isActionInProgress Then
                    isActionInProgress = True
                    If Not isRestoringState Then SaveStateForUndo()
                End If

                ' Try to pick up an item first
                If highlightedWallX Then
                    If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up vertical wall item
                        isDraggingItem = True
                        draggedItem = verticalWallItems(highlightedX)(highlightedY)
                        draggedItemIsWall = True
                        draggedItemIsVerticalWall = True
                        draggedItemOriginalX = highlightedX
                        draggedItemOriginalY = highlightedY
                        verticalWallItems(highlightedX)(highlightedY) = Nothing
                        verticalWalls(highlightedX)(highlightedY) = True
                        picGame.Capture = True
                        itemPickedUp = True
                    End If
                ElseIf highlightedWallY Then
                    If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up horizontal wall item
                        isDraggingItem = True
                        draggedItem = horizontalWallItems(highlightedX)(highlightedY)
                        draggedItemIsWall = True
                        draggedItemIsVerticalWall = False
                        draggedItemOriginalX = highlightedX
                        draggedItemOriginalY = highlightedY
                        horizontalWallItems(highlightedX)(highlightedY) = Nothing
                        horizontalWalls(highlightedX)(highlightedY) = True
                        picGame.Capture = True
                        itemPickedUp = True
                    End If
                Else
                    If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up floor item
                        isDraggingItem = True
                        draggedItem = placedItems(highlightedX)(highlightedY)
                        draggedItemIsWall = False
                        draggedItemIsVerticalWall = False
                        draggedItemOriginalX = highlightedX
                        draggedItemOriginalY = highlightedY
                        placedItems(highlightedX)(highlightedY) = Nothing
                        picGame.Capture = True
                        itemPickedUp = True
                    End If
                End If

                If itemPickedUp Then
                    ' Assign a unique ID if not already assigned
                    If String.IsNullOrEmpty(draggedItem.ID) Then
                        draggedItem.ID = GenerateUniqueID(draggedItem.ImageKey)
                    End If
                    redoStack.Clear()
                    picGame.Invalidate()
                Else
                    ' If no item was picked up, proceed to set edge style
                    If rdoEdge.Checked Then
                        isLeftMouseDown = True
                        SetEdgeStyleAtMousePosition(e.X, e.Y)
                    Else
                        ' Existing logic for adding walls or other elements
                        isLeftMouseDown = True
                        AddWallAtMousePosition(e.X, e.Y)
                    End If
                End If
            End If
        End If
    End If
End Sub

Private Sub PicGame_MouseMove(sender As Object, e As MouseEventArgs) Handles picGame.MouseMove
    If isInSelectionMode Then
        If isSelecting Then
            selectionEndPoint = e.Location
            selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)
            picGame.Invalidate()
        End If
    Else
        If isDraggingItem Then
            mouseX = e.X
            mouseY = e.Y
            UpdateHighlight(e.X, e.Y)
            picGame.Invalidate()
        Else
            mouseX = e.X
            mouseY = e.Y
            UpdateHighlight(e.X, e.Y)
            picGame.Invalidate()
            If isRightMouseDown Then
                If rdoEdge.Checked Then
                    UpdateHighlight(e.X, e.Y)
                    If isMouseInsideGrid Then
                        RemoveEdgeStyleAtMousePosition(e.X, e.Y)
                    End If
                Else
                    Dim deltaX As Integer = e.X - rightMouseDownPosition.X
                    Dim deltaY As Integer = e.Y - rightMouseDownPosition.Y
                    Dim distanceSquared As Integer = deltaX * deltaX + deltaY * deltaY
                    If distanceSquared > CLICK_DRAG_THRESHOLD_SQUARED Then
                        RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=True, removeItems:=False)
                    End If
                End If
            ElseIf isLeftMouseDown Then
                If rdoEdge.Checked Then
                    UpdateHighlight(e.X, e.Y)
                    If isMouseInsideGrid Then
                        SetEdgeStyleAtMousePosition(e.X, e.Y)
                    End If
                Else
                    AddWallAtMousePosition(e.X, e.Y)
                End If
            End If
        End If
    End If
End Sub

Private Sub RemoveEdgeStyleAtMousePosition(mouseX As Integer, mouseY As Integer)
    UpdateHighlight(mouseX, mouseY)
    If Not highlightedWallX AndAlso Not highlightedWallY Then
        If highlightedX >= 0 AndAlso highlightedX < SQCOUNTX AndAlso highlightedY >= 0 AndAlso highlightedY < SQCOUNTY Then
            floorsUseEdgeStyle(highlightedX)(highlightedY) = False
            picGame.Invalidate()
        End If
    End If
End Sub

Private Sub PicGame_MouseUp(sender As Object, e As MouseEventArgs) Handles picGame.MouseUp
    If isInSelectionMode Then
        If isSelecting AndAlso e.Button = MouseButtons.Left Then
            isSelecting = False
            selectionEndPoint = e.Location
            selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)
            picGame.Invalidate()

            If Not selectionRectangle.IsEmpty Then
                ' Calculate the center point of the selection rectangle
                Dim centerX As Integer = selectionRectangle.Left + selectionRectangle.Width \ 2
                Dim centerY As Integer = selectionRectangle.Top + selectionRectangle.Height \ 2
                Dim menuPosition As New Point(centerX, centerY)

                ' Show context menu at the center of the selection rectangle
                ctmMenu.Show(picGame, menuPosition)
            End If
        End If
    Else
        If e.Button = MouseButtons.Left Then
            If isDraggingItem Then
                isDraggingItem = False
                picGame.Capture = False

                If isMouseInsideGrid Then
                    If highlightedWallX AndAlso draggedItemIsWall AndAlso draggedItemIsVerticalWall Then
                        verticalWallItems(highlightedX)(highlightedY) = draggedItem
                        verticalWalls(highlightedX)(highlightedY) = False
                    ElseIf highlightedWallY AndAlso draggedItemIsWall AndAlso Not draggedItemIsVerticalWall Then
                        horizontalWallItems(highlightedX)(highlightedY) = draggedItem
                        horizontalWalls(highlightedX)(highlightedY) = False
                    ElseIf Not highlightedWallX AndAlso Not highlightedWallY AndAlso Not draggedItemIsWall Then
                        placedItems(highlightedX)(highlightedY) = draggedItem
                    Else
                        ' Invalid drop location, return item to original location
                        ReturnItemToOriginalLocation()
                    End If
                Else
                    ' Not inside grid, return item to original location
                    ReturnItemToOriginalLocation()
                End If

                draggedItem = Nothing
                If isActionInProgress Then
                    isActionInProgress = False
                    redoStack.Clear()
                End If

                picGame.Invalidate()
            Else
                isLeftMouseDown = False
                If isActionInProgress Then
                    isActionInProgress = False
                    redoStack.Clear()
                End If
            End If
        ElseIf e.Button = MouseButtons.Right Then
            isRightMouseDown = False
            If isActionInProgress Then
                isActionInProgress = False
                redoStack.Clear()
            End If

            If rdoEdge.Checked Then
                ' Remove edge style at the mouse position
                UpdateHighlight(e.X, e.Y)
                If isMouseInsideGrid Then
                    If Not highlightedWallX AndAlso Not highlightedWallY Then
                        If highlightedX >= 0 AndAlso highlightedX < SQCOUNTX AndAlso highlightedY >= 0 AndAlso highlightedY < SQCOUNTY Then
                            floorsUseEdgeStyle(highlightedX)(highlightedY) = False
                            picGame.Invalidate()
                        End If
                    End If
                End If
            Else
                ' Existing logic for right-click when not in edge mode
                Dim deltaX As Integer = e.X - rightMouseDownPosition.X
                Dim deltaY As Integer = e.Y - rightMouseDownPosition.Y
                Dim distanceSquared As Integer = deltaX * deltaX + deltaY * deltaY
                If distanceSquared < CLICK_DRAG_THRESHOLD_SQUARED Then
                    RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=False, removeItems:=True)
                End If
            End If
        End If
    End If
End Sub

#End Region

#Region "Utility and Helper Methods"

Private Sub CalculateSquareSizes(clientWidth As Integer, clientHeight As Integer)
    SQSizeX = (clientWidth - 2 * INDENT - (SQCOUNTX + 1) * WALL_THICKNESS) \ SQCOUNTX
    SQSizeY = (clientHeight - 2 * INDENT - (SQCOUNTY + 1) * WALL_THICKNESS) \ SQCOUNTY
End Sub

Private Sub DrawGrid(g As Graphics)
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT

    ' Draw floor squares
    For x As Integer = 0 To SQCOUNTX - 1
        For y As Integer = 0 To SQCOUNTY - 1
            Dim floorRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                           yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                           SQSizeX, SQSizeY)
            If floorsUseEdgeStyle(x)(y) Then
                Using floorBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            Else
                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            End If
        Next
    Next

    ' Draw wall intersections
    For x As Integer = 0 To SQCOUNTX
        For y As Integer = 0 To SQCOUNTY
            Dim intersectionRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                                  yOrigin + y * (SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                                  WALL_THICKNESS, WALL_THICKNESS)
            Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                g.FillRectangle(wallBrush, intersectionRect)
            End Using
        Next
    Next

    ' Draw vertical walls or floor
    For x As Integer = 0 To SQCOUNTX
        For y As Integer = 0 To SQCOUNTY - 1
            Dim wallRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                          yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                          WALL_THICKNESS, SQSizeY)
            If verticalWalls(x)(y) Then
                ' Draw wall
                Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                    g.FillRectangle(wallBrush, wallRect)
                End Using
            Else
                ' Draw floor
                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, wallRect)
                End Using
            End If
        Next
    Next

    ' Draw horizontal walls or floor
    For x As Integer = 0 To SQCOUNTX - 1
        For y As Integer = 0 To SQCOUNTY
            Dim wallRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                          yOrigin + y * (SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                          SQSizeX, WALL_THICKNESS)
            If horizontalWalls(x)(y) Then
                ' Draw wall
                Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                    g.FillRectangle(wallBrush, wallRect)
                End Using
            Else
                ' Draw floor
                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, wallRect)
                End Using
            End If
        Next
    Next

    ' Draw items
    DrawItems(g)
    DrawWallItems(g)
End Sub

Private Sub DrawItems(g As Graphics)
    For x As Integer = 0 To placedItems.Length - 1
        If placedItems(x) IsNot Nothing Then
            For y As Integer = 0 To placedItems(x).Length - 1
                If placedItems(x)(y) IsNot Nothing Then
                    Dim img As Image = GetImageByKey(placedItems(x)(y).ImageKey)
                    If img IsNot Nothing Then
                        ImageAnimator.UpdateFrames(img)
                        Dim xPos As Integer = INDENT + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
                        Dim yPos As Integer = INDENT + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
                        Dim destRect As New Rectangle(xPos, yPos, SQSizeX, SQSizeY)

                        ' Draw the item with preserved transparency
                        Dim imgAttributes As New Imaging.ImageAttributes()
                        imgAttributes.SetWrapMode(WrapMode.TileFlipXY)
                        g.DrawImage(img, destRect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttributes)
                    End If
                End If
            Next
        End If
    Next
End Sub

Private Sub DrawWallItems(g As Graphics)
    ' Draw vertical wall items
    For x As Integer = 0 To verticalWallItems.Length - 1
        If verticalWallItems(x) IsNot Nothing Then
            For y As Integer = 0 To verticalWallItems(x).Length - 1
                If verticalWallItems(x)(y) IsNot Nothing Then
                    Dim img As Image = GetImageByKey(verticalWallItems(x)(y).ImageKey)
                    If img IsNot Nothing Then
                        ImageAnimator.UpdateFrames(img)
                        Dim wallX As Integer = INDENT + x * (SQSizeX + WALL_THICKNESS) - (WALL_THICKNESS \ 2)
                        Dim wallY As Integer = INDENT + y * (SQSizeY + WALL_THICKNESS) + (WALL_THICKNESS \ 2)
                        Dim wallWidth As Integer = WALL_THICKNESS
                        Dim wallHeight As Integer = SQSizeY

                        ' Keep original image width, stretch height
                        Dim newWidth As Integer = img.Width
                        Dim newHeight As Integer = wallHeight

                        ' Center horizontally
                        Dim centeredX As Integer = wallX + (wallWidth - newWidth) \ 2
                        Dim drawRect As New Rectangle(centeredX, wallY, newWidth, newHeight)
                        g.DrawImage(img, drawRect)
                    End If
                End If
            Next
        End If
    Next

    ' Draw horizontal wall items
    For x As Integer = 0 To horizontalWallItems.Length - 1
        If horizontalWallItems(x) IsNot Nothing Then
            For y As Integer = 0 To horizontalWallItems(x).Length - 1
                If horizontalWallItems(x)(y) IsNot Nothing Then
                    Dim img As Image = GetImageByKey(horizontalWallItems(x)(y).ImageKey)
                    If img IsNot Nothing Then
                        ImageAnimator.UpdateFrames(img)
                        Dim wallX As Integer = INDENT + x * (SQSizeX + WALL_THICKNESS) + (WALL_THICKNESS \ 2)
                        Dim wallY As Integer = INDENT + y * (SQSizeY + WALL_THICKNESS) - (WALL_THICKNESS \ 2)
                        Dim wallWidth As Integer = SQSizeX
                        Dim wallHeight As Integer = WALL_THICKNESS

                        ' Stretch width, keep original image height
                        Dim newWidth As Integer = wallWidth
                        Dim newHeight As Integer = img.Height

                        ' Center vertically
                        Dim centeredY As Integer = wallY + (wallHeight - newHeight) \ 2
                        Dim drawRect As New Rectangle(wallX, centeredY, newWidth, newHeight)
                        g.DrawImage(img, drawRect)
                    End If
                End If
            Next
        End If
    Next
End Sub

Private Sub DrawHighlight(g As Graphics)
    If Not isMouseInsideGrid Then Return
    Using highlightPen As New Pen(highlightColor, 2) With {.DashStyle = DashStyle.Dash}
        If highlightedWallX Then
            Dim x As Integer = INDENT + highlightedX * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2
            Dim y As Integer = INDENT + highlightedY * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim rect As New Rectangle(x, y, WALL_THICKNESS, SQSizeY)
            g.DrawRectangle(highlightPen, rect)
        ElseIf highlightedWallY Then
            Dim x As Integer = INDENT + highlightedX * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim y As Integer = INDENT + highlightedY * (SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2
            Dim rect As New Rectangle(x, y, SQSizeX, WALL_THICKNESS)
            g.DrawRectangle(highlightPen, rect)
        Else
            Dim x As Integer = INDENT + highlightedX * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim y As Integer = INDENT + highlightedY * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim rect As New Rectangle(x, y, SQSizeX, SQSizeY)
            g.DrawRectangle(highlightPen, rect)
        End If
    End Using
End Sub

Private Sub UpdateHighlight(mouseX As Integer, mouseY As Integer)
    mouseX -= INDENT
    mouseY -= INDENT
    isMouseInsideGrid = False

    If mouseX >= 0 AndAlso mouseY >= 0 Then
        Dim cellSizeX As Integer = SQSizeX + WALL_THICKNESS
        Dim cellSizeY As Integer = SQSizeY + WALL_THICKNESS
        Dim cellX As Integer = mouseX \ cellSizeX
        Dim cellY As Integer = mouseY \ cellSizeY

        If cellX < SQCOUNTX AndAlso cellY < SQCOUNTY Then
            Dim offsetX As Integer = mouseX Mod cellSizeX
            Dim offsetY As Integer = mouseY Mod cellSizeY

            highlightedWallX = offsetX < WALL_THICKNESS
            highlightedWallY = offsetY < WALL_THICKNESS
            highlightedX = cellX
            highlightedY = cellY
            isMouseInsideGrid = True
        End If
    End If
End Sub

Private Function GetRectangle(p1 As Point, p2 As Point) As Rectangle
    Dim x As Integer = Math.Min(p1.X, p2.X)
    Dim y As Integer = Math.Min(p1.Y, p2.Y)
    Dim width As Integer = Math.Abs(p1.X - p2.X)
    Dim height As Integer = Math.Abs(p1.Y - p2.Y)
    Return New Rectangle(x, y, width, height)
End Function

Private Sub SetEdgeStyleAtMousePosition(mouseX As Integer, mouseY As Integer)
    UpdateHighlight(mouseX, mouseY)
    If Not highlightedWallX AndAlso Not highlightedWallY Then
        If highlightedX >= 0 AndAlso highlightedX < SQCOUNTX AndAlso highlightedY >= 0 AndAlso highlightedY < SQCOUNTY Then
            floorsUseEdgeStyle(highlightedX)(highlightedY) = True

            ' Place walls around the cell

            ' Left vertical wall at (highlightedX, highlightedY)
            If highlightedX >= 0 AndAlso highlightedX < verticalWalls.Length Then
                ' Only place the wall if there isn't a wall item there
                If verticalWallItems(highlightedX)(highlightedY) Is Nothing Then
                    verticalWalls(highlightedX)(highlightedY) = True
                End If
            End If

            ' Right vertical wall at (highlightedX + 1, highlightedY)
            If highlightedX + 1 >= 0 AndAlso highlightedX + 1 < verticalWalls.Length Then
                If verticalWallItems(highlightedX + 1)(highlightedY) Is Nothing Then
                    verticalWalls(highlightedX + 1)(highlightedY) = True
                End If
            End If

            ' Top horizontal wall at (highlightedX, highlightedY)
            If highlightedX >= 0 AndAlso highlightedX < horizontalWalls.Length AndAlso highlightedY >= 0 AndAlso highlightedY < horizontalWalls(0).Length Then
                If horizontalWallItems(highlightedX)(highlightedY) Is Nothing Then
                    horizontalWalls(highlightedX)(highlightedY) = True
                End If
            End If

            ' Bottom horizontal wall at (highlightedX, highlightedY + 1)
            If highlightedX >= 0 AndAlso highlightedX < horizontalWalls.Length AndAlso highlightedY + 1 >= 0 AndAlso highlightedY + 1 < horizontalWalls(0).Length Then
                If horizontalWallItems(highlightedX)(highlightedY + 1) Is Nothing Then
                    horizontalWalls(highlightedX)(highlightedY + 1) = True
                End If
            End If

            picGame.Invalidate()
        End If
    End If
End Sub

Private Sub SaveStateForUndo()
    If isRestoringState Then Return
    undoStack.Push(New RoomState(Me))
    If undoStack.Count > 50 Then
        undoStack = New Stack(Of RoomState)(undoStack.Reverse().Take(50))
    End If
End Sub

Private Sub SaveStateForRedo()
    If isRestoringState Then Return
    redoStack.Push(New RoomState(Me))
End Sub

Private Function GetCellsInRectangle(rect As Rectangle) As List(Of Point)
    Dim selectedCells As New List(Of Point)
    For x As Integer = 0 To SQCOUNTX - 1
        For y As Integer = 0 To SQCOUNTY - 1
            Dim cellRect As Rectangle = GetCellRectangle(x, y)
            If rect.IntersectsWith(cellRect) Then
                selectedCells.Add(New Point(x, y))
            End If
        Next
    Next
    Return selectedCells
End Function

Private Function GetCellRectangle(x As Integer, y As Integer) As Rectangle
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT
    Return New Rectangle(
    xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
    yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
    SQSizeX, SQSizeY)
End Function

Private Function GetVerticalWallsInRectangle(rect As Rectangle) As List(Of Point)
    Dim selectedWalls As New List(Of Point)
    For x As Integer = 0 To SQCOUNTX
        For y As Integer = 0 To SQCOUNTY - 1
            Dim wallRect As Rectangle = GetVerticalWallRectangle(x, y)
            If rect.IntersectsWith(wallRect) Then
                selectedWalls.Add(New Point(x, y))
            End If
        Next
    Next
    Return selectedWalls
End Function

Private Function GetVerticalWallRectangle(x As Integer, y As Integer) As Rectangle
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT
    Return New Rectangle(
    xOrigin + x * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2,
    yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
    WALL_THICKNESS, SQSizeY)
End Function

Private Function GetHorizontalWallsInRectangle(rect As Rectangle) As List(Of Point)
    Dim selectedWalls As New List(Of Point)
    For x As Integer = 0 To SQCOUNTX - 1
        For y As Integer = 0 To SQCOUNTY
            Dim wallRect As Rectangle = GetHorizontalWallRectangle(x, y)
            If rect.IntersectsWith(wallRect) Then
                selectedWalls.Add(New Point(x, y))
            End If
        Next
    Next
    Return selectedWalls
End Function

Private Function GetHorizontalWallRectangle(x As Integer, y As Integer) As Rectangle
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT
    Return New Rectangle(
    xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
    yOrigin + y * (SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2,
    SQSizeX, WALL_THICKNESS)
End Function

Private Sub RandomizeWalls()
    wallsColor1 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
    wallsColor2 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
    Dim hatchStyles As Array = [Enum].GetValues(GetType(HatchStyle))
    wallsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)

    ' Update the HatchStyle list selection if Walls radio button is checked
    UpdateSelections()
End Sub

Private Sub RandomizeFloors()
    floorsColor1 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
    floorsColor2 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
    Dim hatchStyles As Array = [Enum].GetValues(GetType(HatchStyle))
    floorsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)

    ' Update the HatchStyle list selection if Floors radio button is checked
    UpdateSelections()
End Sub

Private Sub UpdateSelections()
    ' Update Hatch Style Selection
    If rdoWalls.Checked Then
        lstHatchstyles.SelectedItem = wallsHatchStyle.ToString()
    ElseIf rdoFloors.Checked Then
        lstHatchstyles.SelectedItem = floorsHatchStyle.ToString()
    ElseIf rdoEdge.Checked Then
        lstHatchstyles.SelectedItem = wallsHatchStyle.ToString()
    End If

    ' Update Music Selection
    If Not String.IsNullOrEmpty(SelectedMusic) Then
        If cboMusic.Items.Contains(SelectedMusic) Then
            cboMusic.SelectedItem = SelectedMusic
        Else
            MessageBox.Show("The music file '" & SelectedMusic & "' is not available.", "Music Not Found", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            cboMusic.SelectedIndex = -1
        End If
    Else
        cboMusic.SelectedIndex = -1 ' No selection
    End If
End Sub

Private Sub AddColorPictureBoxHandlers(parent As Control)
    For Each ctrl As Control In parent.Controls
        If TypeOf ctrl Is PictureBox AndAlso ctrl.Name.StartsWith("col") Then
            AddHandler ctrl.Click, AddressOf ColorPictureBox_Click
        End If
        If ctrl.HasChildren Then
            AddColorPictureBoxHandlers(ctrl)
        End If
    Next
End Sub

Private Sub ColorPictureBox_Click(sender As Object, e As EventArgs)
    If Not isRestoringState Then SaveStateForUndo()
    Dim pb As PictureBox = CType(sender, PictureBox)
    Dim selectedColor As Color = pb.BackColor

    If pb.Name.StartsWith("col") Then
        Dim numStr As String = pb.Name.Substring(3)
        Dim num As Integer
        If Integer.TryParse(numStr, num) Then
            If num >= 1 AndAlso num <= 48 Then
                ' Update Color1
                If rdoFloors.Checked Then
                    floorsColor1 = selectedColor
                ElseIf rdoWalls.Checked Then
                    wallsColor1 = selectedColor
                ElseIf rdoEdge.Checked Then
                    wallsColor1 = selectedColor ' For edge, use wall's color
                End If
            ElseIf num >= 51 AndAlso num <= 98 Then
                ' Update Color2
                If rdoFloors.Checked Then
                    floorsColor2 = selectedColor
                ElseIf rdoWalls.Checked Then
                    wallsColor2 = selectedColor
                ElseIf rdoEdge.Checked Then
                    wallsColor2 = selectedColor ' For edge, use wall's color
                End If
            End If
        End If
    End If

    picGame.Invalidate()
End Sub

Private Sub PicBlock_MouseDown(sender As Object, e As MouseEventArgs)
    Dim pic As PictureBox = CType(sender, PictureBox)

    isDraggingItem = True
    isActionInProgress = True
    If Not isRestoringState Then SaveStateForUndo()

    ' Create a new PlacedItem with the ImageKey
    draggedItem = New PlacedItem() With {.ImageKey = pic.Name}

    ' Determine if it's a wall item
    draggedItemIsWall = IsVerticalItem(pic.Name) OrElse IsHorizontalItem(pic.Name)
    draggedItemIsVerticalWall = IsVerticalItem(pic.Name)

    ' Since it's being dragged from the toolbox, there is no original position
    draggedItemOriginalX = -1
    draggedItemOriginalY = -1

    ' Assign a unique ID
    draggedItem.ID = GenerateUniqueID(draggedItem.ImageKey)

    ' Capture the mouse events at the form level
    Me.Capture = True

    Cursor.Current = Cursors.Hand

    picGame.Invalidate()
End Sub

Private Sub ReturnItemToOriginalLocation()
    If draggedItemIsWall Then
        If draggedItemOriginalX >= 0 AndAlso draggedItemOriginalY >= 0 Then
            If draggedItemIsVerticalWall Then
                verticalWallItems(draggedItemOriginalX)(draggedItemOriginalY) = draggedItem
                verticalWalls(draggedItemOriginalX)(draggedItemOriginalY) = False
            Else
                horizontalWallItems(draggedItemOriginalX)(draggedItemOriginalY) = draggedItem
                horizontalWalls(draggedItemOriginalX)(draggedItemOriginalY) = False
            End If
        End If
    Else
        If draggedItemOriginalX >= 0 AndAlso draggedItemOriginalY >= 0 Then
            placedItems(draggedItemOriginalX)(draggedItemOriginalY) = draggedItem
        End If
    End If
End Sub

Private Function GetImageByKey(imageKey As String) As Image
    Select Case imageKey
        Case "picBlock1"
            Return picBlock1.Image
        Case "picBaddy1"
            Return picBaddy1.Image
        Case "picBaddy2"
            Return picBaddy2.Image
        Case "picBaddy3"
            Return picBaddy3.Image
        Case "picBaddy4"
            Return picBaddy4.Image
        Case "picLazer1"
            Return picLazer1.Image
        Case "picLazer2"
            Return picLazer2.Image
        Case "picFlipper1"
            Return picFlipper1.Image
        Case "picFlipper2"
            Return picFlipper2.Image
        Case "picFlipper3"
            Return picFlipper3.Image
        Case "picFlipper4"
            Return picFlipper4.Image
        Case "picCoin"
            Return picCoin.Image
        Case "picGate"
            Return picGate.Image
        Case "picKey"
            Return picKey.Image
        Case "picStart"
            Return picStart.Image
        Case "picFinish"
            Return picFinish.Image
        Case "picBlockStopper1"
            Return picBlockStopper1.Image
        Case "picBall"
            Return picBall.Image
        Case Else
            Return Nothing
    End Select
End Function

Private Function IsVerticalItem(imgKey As String) As Boolean
    Return imgKey = "picLazer1" OrElse imgKey = "picFlipper1" OrElse imgKey = "picFlipper2"
End Function

Private Function IsHorizontalItem(imgKey As String) As Boolean
    Return imgKey = "picLazer2" OrElse imgKey = "picFlipper3" OrElse imgKey = "picFlipper4"
End Function

Private Sub AddWallAtMousePosition(mouseX As Integer, mouseY As Integer)
    UpdateHighlight(mouseX, mouseY)
    If highlightedWallX Then
        If verticalWallItems(highlightedX)(highlightedY) Is Nothing AndAlso
           highlightedX > 0 AndAlso highlightedX < SQCOUNTX Then
            verticalWalls(highlightedX)(highlightedY) = True
            picGame.Invalidate()
        End If
    ElseIf highlightedWallY Then
        If horizontalWallItems(highlightedX)(highlightedY) Is Nothing AndAlso
           highlightedY > 0 AndAlso highlightedY < SQCOUNTY Then
            horizontalWalls(highlightedX)(highlightedY) = True
            picGame.Invalidate()
        End If
    End If
End Sub

Private Sub RemoveObjectAtMousePosition(mouseX As Integer, mouseY As Integer, removeWalls As Boolean, removeItems As Boolean)
    UpdateHighlight(mouseX, mouseY)

    If highlightedWallX Then
        If removeItems AndAlso verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
            verticalWallItems(highlightedX)(highlightedY) = Nothing
            picGame.Invalidate()
        ElseIf removeWalls AndAlso verticalWalls(highlightedX)(highlightedY) AndAlso highlightedX > 0 AndAlso highlightedX < SQCOUNTX Then
            verticalWalls(highlightedX)(highlightedY) = False
            picGame.Invalidate()
        End If
    ElseIf highlightedWallY Then
        If removeItems AndAlso horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
            horizontalWallItems(highlightedX)(highlightedY) = Nothing
            picGame.Invalidate()
        ElseIf removeWalls AndAlso horizontalWalls(highlightedX)(highlightedY) AndAlso highlightedY > 0 AndAlso highlightedY < SQCOUNTY Then
            horizontalWalls(highlightedX)(highlightedY) = False
            picGame.Invalidate()
        End If
    Else
        If removeItems AndAlso placedItems(highlightedX)(highlightedY) IsNot Nothing Then
            placedItems(highlightedX)(highlightedY) = Nothing
            picGame.Invalidate()
        End If
    End If
End Sub

Private Sub SaveRoomToFile(filePath As String)
    Try
        Dim roomState As New RoomState(Me)
        Dim options As New JsonSerializerOptions With {
            .WriteIndented = True,
            .IncludeFields = True
        }
        Dim jsonString As String = JsonSerializer.Serialize(roomState, options)
        File.WriteAllText(filePath, jsonString)
    Catch ex As Exception
        MessageBox.Show("Error saving room: " & ex.Message)
    End Try
End Sub

Private Sub LoadRoomFromFile(filePath As String)
    Try
        If Not File.Exists(filePath) Then Return
        Dim options As New JsonSerializerOptions With {
            .IncludeFields = True
        }
        Dim jsonString As String = File.ReadAllText(filePath)
        Dim roomState As RoomState = JsonSerializer.Deserialize(Of RoomState)(jsonString, options)

        ' Ensure music is loaded before restoring state
        LoadMusic()

        roomState.RestoreState(Me)

        ' Update selections
        UpdateSelections()

        ' Update itemCounters based on loaded items
        UpdateItemCounters()

        picGame.Invalidate()
    Catch ex As Exception
        MessageBox.Show("Error loading room: " & ex.Message)
    End Try
End Sub

Private Sub UpdateItemCounters()
    itemCounters.Clear()
    ' Iterate through placedItems, verticalWallItems, and horizontalWallItems
    For Each itemsArray As PlacedItem()() In {placedItems, verticalWallItems, horizontalWallItems}
        For Each row As PlacedItem In itemsArray.SelectMany(Function(r) r)
            If row IsNot Nothing AndAlso Not String.IsNullOrEmpty(row.ID) Then
                Dim parts() As String = row.ID.Split("_"c)
                If parts.Length = 2 Then
                    Dim key As String = parts(0)
                    Dim number As Integer
                    If Integer.TryParse(parts(1), number) Then
                        If Not itemCounters.ContainsKey(key) OrElse itemCounters(key) < number Then
                            itemCounters(key) = number
                        End If
                    End If
                End If
            End If
        Next
    Next
End Sub

Private Sub LoadSettingsFromFile(filePath As String)
    Try
        If Not File.Exists(filePath) Then Return
        Dim lines As String() = File.ReadAllLines(filePath)
        Dim section As String = ""
        For Each line As String In lines
            line = line.Trim()
            If String.IsNullOrEmpty(line) OrElse line.StartsWith(";") Then Continue For
            If line.StartsWith("[") AndAlso line.EndsWith("]") Then
                section = line.Substring(1, line.Length - 2)
            Else
                Dim parts() As String = line.Split(New Char() {":"c}, 2)
                If parts.Length = 2 Then
                    LoadProperty(parts(0).Trim(), parts(1).Trim(), section)
                End If
            End If
        Next

        ' Update lstHatchstyles selection after loading settings
        UpdateSelections()
    Catch ex As Exception
        MessageBox.Show("Error loading settings: " & ex.Message)
    End Try
End Sub

Private Sub LoadProperty(key As String, value As String, section As String)
    Dim isWall As Boolean = (section = "Walls")
    Select Case key
        Case "HatchStyle"
            Dim hatchStyle As HatchStyle
            If [Enum].TryParse(value, hatchStyle) Then
                If isWall Then wallsHatchStyle = hatchStyle Else floorsHatchStyle = hatchStyle
            End If
        Case "Color1"
            Dim color As Color = ParseColor(value)
            If isWall Then wallsColor1 = color Else floorsColor1 = color
        Case "Color2"
            Dim color As Color = ParseColor(value)
            If isWall Then wallsColor2 = color Else floorsColor2 = color
    End Select
End Sub

Private Function ParseColor(colorString As String) As Color
    Dim parts() As String = colorString.Split(","c)
    If parts.Length = 4 Then
        Dim a, r, g, b As Integer
        If Integer.TryParse(parts(0), a) AndAlso Integer.TryParse(parts(1), r) AndAlso
           Integer.TryParse(parts(2), g) AndAlso Integer.TryParse(parts(3), b) Then
            Return Color.FromArgb(a, r, g, b)
        End If
    End If
    Return Color.Black
End Function

Private Function ColorToString(color As Color) As String
    Return $"{color.A},{color.R},{color.G},{color.B}"
End Function

Private Sub SaveSettingsToFile(filePath As String)
    Try
        Using sw As New StreamWriter(filePath)
            sw.WriteLine("[Walls]")
            sw.WriteLine("HatchStyle: " & wallsHatchStyle.ToString())
            sw.WriteLine("Color1: " & ColorToString(wallsColor1))
            sw.WriteLine("Color2: " & ColorToString(wallsColor2))
            sw.WriteLine()
            sw.WriteLine("[Floors]")
            sw.WriteLine("HatchStyle: " & floorsHatchStyle.ToString())
            sw.WriteLine("Color1: " & ColorToString(floorsColor1))
            sw.WriteLine("Color2: " & ColorToString(floorsColor2))
        End Using
    Catch ex As Exception
        MessageBox.Show("Error saving settings: " & ex.Message)
    End Try
End Sub

Private Sub btnTriggerEditor_Click(sender As Object, e As EventArgs) Handles btnTriggerEditor.Click
    ' Instantiate and show frmTrigger, passing the current instance for event handling
    Dim triggerEditor As New frmTrigger(Me)
    triggerEditor.Show()
End Sub

#End Region

#Region "Integration with Trigger Editor"

' Method to begin selection from frmTrigger
Public Sub BeginSelection(mode As SelectionModeType)
    currentSelectionMode = mode
    Cursor = Cursors.Cross

    ' Display a message to guide the user
    Select Case mode
        Case SelectionModeType.SelectItem
            MessageBox.Show("Please select an item on the grid.", "Select Item", MessageBoxButtons.OK, MessageBoxIcon.Information)
        Case SelectionModeType.SelectTarget
            MessageBox.Show("Please select a target on the grid.", "Select Target", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Select
End Sub

#End Region

#Region "Utility Methods"

Private Function GenerateUniqueID(imageKey As String) As String
    If Not itemCounters.ContainsKey(imageKey) Then
        itemCounters(imageKey) = 1
    Else
        itemCounters(imageKey) += 1
    End If
    Return $"{imageKey}_{itemCounters(imageKey):D3}" ' Example: picBaddy1_001
End Function

#End Region
End Class

The following is all in RoomState.vb

Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Text.Json

Public Class RoomState
' Properties to save colors and styles
Public wallsHatchStyleValue As Integer
Public wallsColor1Argb As Integer
Public wallsColor2Argb As Integer
Public floorsHatchStyleValue As Integer
Public floorsColor1Argb As Integer
Public floorsColor2Argb As Integer

' Arrays to save wall states
Public verticalWalls()() As Boolean
Public horizontalWalls()() As Boolean

' Arrays to save placed items
Public placedItems()() As PlacedItemData
Public verticalWallItems()() As PlacedItemData
Public horizontalWallItems()() As PlacedItemData

' Arrays to save floor edge styles
Public floorsUseEdgeStyle()() As Boolean

' Property to save the selected music
Public SelectedMusic As String

' Property to save the level name
Public LevelName As String

' Parameterless constructor for deserialization
Public Sub New()
    ' This constructor is used by the deserializer
End Sub

' Constructor: captures the current state of the frmDesigner
Public Sub New(roomView As frmDesigner)
    ' Save colors and styles as integers
    wallsHatchStyleValue = CInt(roomView.wallsHatchStyle)
    wallsColor1Argb = roomView.wallsColor1.ToArgb()
    wallsColor2Argb = roomView.wallsColor2.ToArgb()
    floorsHatchStyleValue = CInt(roomView.floorsHatchStyle)
    floorsColor1Argb = roomView.floorsColor1.ToArgb()
    floorsColor2Argb = roomView.floorsColor2.ToArgb()

    ' Save wall states
    verticalWalls = CloneJaggedArray(roomView.verticalWalls)
    horizontalWalls = CloneJaggedArray(roomView.horizontalWalls)

    ' Save placed items with IDs
    placedItems = CopyPlacedItems(roomView.placedItems)
    verticalWallItems = CopyPlacedItems(roomView.verticalWallItems)
    horizontalWallItems = CopyPlacedItems(roomView.horizontalWallItems)

    ' Save floorsUseEdgeStyle
    floorsUseEdgeStyle = CloneJaggedArray(roomView.floorsUseEdgeStyle)

    ' Save the selected music
    SelectedMusic = roomView.SelectedMusic

    ' Save the level name
    LevelName = roomView.txtName.Text
End Sub

' Helper method to clone jagged arrays
Private Function CloneJaggedArray(Of T)(sourceArray As T()()) As T()()
    Dim lengthOuter As Integer = sourceArray.Length
    Dim newArray As T()() = New T(lengthOuter - 1)() {}
    For i As Integer = 0 To lengthOuter - 1
        If sourceArray(i) IsNot Nothing Then
            Dim lengthInner As Integer = sourceArray(i).Length
            newArray(i) = New T(lengthInner - 1) {}
            Array.Copy(sourceArray(i), newArray(i), lengthInner)
        End If
    Next
    Return newArray
End Function

' Helper method to copy placed items including IDs
Private Function CopyPlacedItems(items As PlacedItem()()) As PlacedItemData()()
    Dim lengthOuter As Integer = items.Length
    Dim copiedItems As PlacedItemData()() = New PlacedItemData(lengthOuter - 1)() {}
    For x As Integer = 0 To lengthOuter - 1
        If items(x) IsNot Nothing Then
            Dim lengthInner As Integer = items(x).Length
            copiedItems(x) = New PlacedItemData(lengthInner - 1) {}
            For y As Integer = 0 To lengthInner - 1
                If items(x)(y) IsNot Nothing Then
                    copiedItems(x)(y) = New PlacedItemData With {
                        .ImageKey = items(x)(y).ImageKey,
                        .Id = items(x)(y).ID
                    }
                End If
            Next
        End If
    Next
    Return copiedItems
End Function

' Restore the saved state back into the frmDesigner
Public Sub RestoreState(roomView As frmDesigner)
    ' Restore colors and styles from integers
    roomView.wallsHatchStyle = CType(wallsHatchStyleValue, HatchStyle)
    roomView.wallsColor1 = Color.FromArgb(wallsColor1Argb)
    roomView.wallsColor2 = Color.FromArgb(wallsColor2Argb)
    roomView.floorsHatchStyle = CType(floorsHatchStyleValue, HatchStyle)
    roomView.floorsColor1 = Color.FromArgb(floorsColor1Argb)
    roomView.floorsColor2 = Color.FromArgb(floorsColor2Argb)

    ' Restore wall states
    roomView.verticalWalls = CloneJaggedArray(verticalWalls)
    roomView.horizontalWalls = CloneJaggedArray(horizontalWalls)

    ' Restore placed items with IDs
    roomView.placedItems = RestorePlacedItems(placedItems)
    roomView.verticalWallItems = RestorePlacedItems(verticalWallItems)
    roomView.horizontalWallItems = RestorePlacedItems(horizontalWallItems)

    ' Restore floorsUseEdgeStyle
    roomView.floorsUseEdgeStyle = CloneJaggedArray(floorsUseEdgeStyle)

    ' Restore the selected music
    roomView.SelectedMusic = SelectedMusic

    ' Restore the level name
    roomView.txtName.Text = LevelName
End Sub

' Helper method to restore placed items with IDs
Private Function RestorePlacedItems(data As PlacedItemData()()) As PlacedItem()()
    Dim lengthOuter As Integer = data.Length
    Dim restoredItems As PlacedItem()() = New PlacedItem(lengthOuter - 1)() {}
    For x As Integer = 0 To lengthOuter - 1
        If data(x) IsNot Nothing Then
            Dim lengthInner As Integer = data(x).Length
            restoredItems(x) = New PlacedItem(lengthInner - 1) {}
            For y As Integer = 0 To lengthInner - 1
                If data(x)(y) IsNot Nothing Then
                    restoredItems(x)(y) = New PlacedItem With {
                        .ImageKey = data(x)(y).ImageKey,
                        .Id = data(x)(y).ID
                    }
                End If
            Next
        End If
    Next
    Return restoredItems
End Function

End Class

Public Class PlacedItemData
Public Property ImageKey As String
Public Property ID As String ' Unique Identifier
End Class

Public Class PlacedItem
Public Property ImageKey As String
Public Property ID As String ' Unique Identifier
End Class

And finally the problem code... In my trigger editor form

Imports System.Windows.Forms
Imports System.Drawing

Public Class frmTrigger

Private designerForm As frmDesigner

Private selectedItemID As String = String.Empty
Private selectedTargetID As String = String.Empty

Public Sub New(designer As frmDesigner)
    InitializeComponent()
    designerForm = designer

    ' Subscribe to events
    AddHandler designerForm.ItemSelected, AddressOf OnItemSelected
    AddHandler designerForm.TargetSelected, AddressOf OnTargetSelected
End Sub

Private Sub frmTrigger_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' Initialize TreeView with root node if not already present
    If tvTriggers.Nodes.Count = 0 Then
        tvTriggers.Nodes.Add("Triggers")
    End If

    ' Initialize conditions ComboBox
    InitializeConditions()
End Sub

Private Sub InitializeConditions()
    ' Populate cbConditions with available conditions
    cbConditions.Items.Clear()
    cbConditions.Items.Add("Enters Square")
    cbConditions.Items.Add("Contacts")
    ' Add more conditions as needed
End Sub

Private Sub btnSelectItem_Click(sender As Object, e As EventArgs) Handles btnSelectItem.Click
    If designerForm IsNot Nothing Then
        Me.WindowState = FormWindowState.Minimized
        designerForm.BeginSelection(frmDesigner.SelectionModeType.SelectItem)
    End If
End Sub

Private Sub btnSelectTarget_Click(sender As Object, e As EventArgs) Handles btnSelectTarget.Click
    If designerForm IsNot Nothing Then
        Me.WindowState = FormWindowState.Minimized
        designerForm.BeginSelection(frmDesigner.SelectionModeType.SelectTarget)
    End If
End Sub

Private Sub OnItemSelected(selectedID As String)
    Me.WindowState = FormWindowState.Normal
    lblSelectedItem.Text = $"Selected Item: {selectedID}"
    selectedItemID = selectedID
    CheckAndAddTrigger()
End Sub

Private Sub OnTargetSelected(selectedID As String)
    Me.WindowState = FormWindowState.Normal
    lblSelectedTarget.Text = $"Selected Target: {selectedID}"
    selectedTargetID = selectedID
    CheckAndAddTrigger()
End Sub

Private Sub cbConditions_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbConditions.SelectedIndexChanged
    CheckAndAddTrigger()
End Sub

Private Sub CheckAndAddTrigger()
    If Not String.IsNullOrEmpty(selectedItemID) AndAlso
       Not String.IsNullOrEmpty(selectedTargetID) AndAlso
       cbConditions.SelectedItem IsNot Nothing Then

        Dim condition As String = cbConditions.SelectedItem.ToString()

        ' Add a new trigger node
        Dim triggerNumber As Integer = tvTriggers.Nodes(0).Nodes.Count + 1
        Dim newTriggerNode As TreeNode = tvTriggers.Nodes(0).Nodes.Add($"Trigger {triggerNumber}")

        newTriggerNode.Nodes.Add($"Item: {selectedItemID}")
        newTriggerNode.Nodes.Add($"Condition: {condition}")
        newTriggerNode.Nodes.Add($"Target: {selectedTargetID}")

        ' Expand the TreeView node
        newTriggerNode.Expand()

        ' Clear the selections
        lblSelectedItem.Text = ""
        lblSelectedTarget.Text = ""
        selectedItemID = String.Empty
        selectedTargetID = String.Empty
        cbConditions.SelectedIndex = -1
    End If
End Sub

End Class

When I click btnSelectItem or btnSelectTarget, it goes back to form designer with the error
Select Item
Please select an item on the grid.
OK

Can you update the code so when it goes to the form, it doesn't give me the error message, it waits until I click an item or a sqaure or a wall ad then return back to the trigger editor and fill lblSelectedText or lblSelectedTarget with the object or wall coordinates or floor coordiantes.