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

    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 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
#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
    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
        picGame.AllowDrop = True
        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
        AddHandler btnClear.Click, AddressOf btnClear_Click
        AddHandler btnFill.Click, AddressOf btnFill_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
    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(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
        LoadPremades()
        LoadMusic()

        ' 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 selection
        UpdateSelectedHatchStyle()
    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
            UpdateSelectedHatchStyle()

            picGame.Invalidate()
        End If
    End Sub
#End Region

#Region "Menu Item Click Events"

    Private Sub mnuNew_Click(sender As Object, e As EventArgs)
        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 = ""

        ' Update lstHatchstyles selection
        UpdateSelectedHatchStyle()

        picGame.Invalidate()
    End Sub

    Private Sub mnuSave_Click(sender As Object, e As EventArgs)
        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)
        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
            UpdateSelectedHatchStyle()

            picGame.Invalidate()
        End If
    End Sub

    Private Sub mnuSaveAs_Click(sender As Object, e As EventArgs)
        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

#End Region

#Region "Undo/Redo Methods"

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

            ' Update lstHatchstyles selection
            UpdateSelectedHatchStyle()

            picGame.Invalidate()
        End If
    End Sub

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

            ' Update lstHatchstyles selection
            UpdateSelectedHatchStyle()

            picGame.Invalidate()
        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

#End Region

#Region "Loading and Saving Settings"

    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
            UpdateSelectedHatchStyle()

        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 Sub btnSavePremade_Click(sender As Object, e As EventArgs)
        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 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 Function ColorToString(color As Color) As String
        Return $"{color.A},{color.R},{color.G},{color.B}"
    End Function

#End Region

#Region "Randomization Methods"

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

        ' Update the HatchStyle list selection
        UpdateSelectedHatchStyle()

        picGame.Invalidate()
    End Sub

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

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

    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
        UpdateSelectedHatchStyle()
    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
        UpdateSelectedHatchStyle()
    End Sub

#End Region

#Region "Update Methods"

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

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

    Private Sub UpdateSelectedHatchStyle()
        If rdoWalls.Checked Then
            lstHatchstyles.SelectedItem = wallsHatchStyle.ToString()
        ElseIf rdoFloors.Checked Then
            lstHatchstyles.SelectedItem = floorsHatchStyle.ToString()
        End If
    End Sub

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

#End Region

#Region "Color Selection"

    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

        ' Determine which color to update based on the picture box name
        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
                    End If
                ElseIf num >= 51 AndAlso num <= 98 Then
                    ' Update Color2
                    If rdoFloors.Checked Then
                        floorsColor2 = selectedColor
                    ElseIf rdoWalls.Checked Then
                        wallsColor2 = selectedColor
                    End If
                End If
            End If
        End If

        picGame.Invalidate()
    End Sub

#End Region

#Region "Load and Save Room"

    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)
            roomState.RestoreState(Me)

            ' Update lstHatchstyles selection
            UpdateSelectedHatchStyle()

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

#End Region

#Region "Drawing Methods"

    Private Sub PicGame_Paint(sender As Object, e As PaintEventArgs)
        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
    End Sub

    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)
                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            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
                            g.DrawImage(img, New Rectangle(xPos, yPos, SQSizeX, SQSizeY))
                        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

#End Region

#Region "Mouse Event Handlers"

    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
            Else
                ' Not over picGame, return item to original location
                ReturnItemToOriginalLocation()
            End If

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

            picGame.Invalidate()
        End If
    End Sub

    Private Sub PicGame_MouseDown(sender As Object, e As MouseEventArgs)
        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
            ' Do not remove anything yet
        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

                If highlightedWallX Then
                    ' We're over a vertical wall
                    If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up the 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
                    ' We're over a horizontal wall
                    If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up the 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
                    ' We're over a floor item
                    If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                        ' Pick up the 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
                    redoStack.Clear()
                    picGame.Invalidate()
                Else
                    ' No item picked up, proceed to add walls as before
                    isLeftMouseDown = True

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

                    AddWallAtMousePosition(e.X, e.Y)
                End If
            End If
        End If
    End Sub

    Private Sub PicGame_MouseMove(sender As Object, e As MouseEventArgs)
        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
                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
                    ' User is dragging
                    RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=True, removeItems:=False)
                End If
            ElseIf isLeftMouseDown Then
                AddWallAtMousePosition(e.X, e.Y)
            End If
        End If
    End Sub

    Private Sub PicGame_MouseUp(sender As Object, e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            If isDraggingItem Then
                isDraggingItem = False
                picGame.Capture = False

                ' Place the item at the new location if 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 place
                        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

            Dim deltaTime As TimeSpan = DateTime.Now - rightMouseDownTime
            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
                ' Considered as a click
                RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=False, removeItems:=True)
            Else
                ' Considered as a drag, walls were removed during MouseMove
            End If
        End If
    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

#End Region

#Region "Item Methods"

    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

        ' 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 "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 "picBaddy3"
                Return picBaddy3.Image
            Case "picBaddy4"
                Return picBaddy4.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

#End Region

#Region "Wall and Item Placement Methods"

    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 btnClear_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()

        ' Clear vertical walls (excluding borders)
        For x As Integer = 1 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY - 1
                verticalWalls(x)(y) = False
            Next
        Next

        ' Clear horizontal walls (excluding borders)
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 1 To SQCOUNTY - 1
                horizontalWalls(x)(y) = False
            Next
        Next

        redoStack.Clear()
        picGame.Invalidate()
    End Sub

    Private Sub btnFill_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()

        ' Fill vertical walls (excluding positions with wall items)
        For x As Integer = 0 To SQCOUNTX
            For y As Integer = 0 To SQCOUNTY - 1
                If verticalWallItems(x)(y) Is Nothing Then
                    verticalWalls(x)(y) = True
                End If
            Next
        Next

        ' Fill horizontal walls (excluding positions with wall items)
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY
                If horizontalWallItems(x)(y) Is Nothing Then
                    horizontalWalls(x)(y) = True
                End If
            Next
        Next

        redoStack.Clear()
        picGame.Invalidate()
    End Sub

#End Region

End Class

The following is in a different file. 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 (same as before)
    Public verticalWalls()() As Boolean
    Public horizontalWalls()() As Boolean

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

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

    ' Constructor: captures the current state of the RoomView
    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
        placedItems = CopyPlacedItems(roomView.placedItems)
        verticalWallItems = CopyPlacedItems(roomView.verticalWallItems)
        horizontalWallItems = CopyPlacedItems(roomView.horizontalWallItems)
    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
    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}
                    End If
                Next
            End If
        Next
        Return copiedItems
    End Function

    ' Restore the saved state back into the RoomView
    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
        roomView.placedItems = RestorePlacedItems(placedItems)
        roomView.verticalWallItems = RestorePlacedItems(verticalWallItems)
        roomView.horizontalWallItems = RestorePlacedItems(horizontalWallItems)
    End Sub

    ' Helper method to restore placed items
    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}
                    End If
                Next
            End If
        Next
        Return restoredItems
    End Function
End Class

' Data class to represent a placed item
Public Class PlacedItem
    Public Property ImageKey As String
End Class

' Serializable data class for placed item
Public Class PlacedItemData
    Public Property ImageKey As String
End Class

This is my code. I have added a Selection Button to my toolstripmenu (In Designer). It is called btnSelect.
I have also added a contextmenu (Again in designer) it is called ctmMenu.
In the the context menu are 3 menu options (Again I've added them in designer) mnuClearWalls, mnuFillWalls, mnuClearObjects

Can you make it so, when I click btnSelect the following happens
1, The Cursor Changes to a Selection Cursor. 
2, I can now drag over the grid (with left click) drawing a selection box.
3, When I right click inside the selection box ctmMenu will appear.
4, mnuClearWalls will clear all the walls inside the selected area.
5, mnuFillWalls will fill all the walls inside the selected area.
6, mnuClearObjects will remove any objects (Floor items and Wall items) from the selected area.