Can you look at this and tell me why Undo is doing 2 steps at a time?

Imports System.Drawing.Drawing2D
Imports System.IO ' Needed for file operations
Imports System.Text.Json ' For JSON serialization
Imports System.Linq ' For stack manipulation

Public Class RoomView
    Inherits Form

    ' Constants for grid dimensions and indentation
    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

    ' Properties for hatch styles and colors
    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

    ' Calculated square sizes
    Private SQSizeX As Integer
    Private SQSizeY As Integer

    ' Mouse state variables
    Private isRightMouseDown As Boolean = False
    Private isLeftMouseDown As Boolean = False
    Private isMouseInsideGrid As Boolean = False

    ' Variables for highlighting
    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

    ' Arrays to store wall presence
    Friend verticalWalls(SQCOUNTX, SQCOUNTY - 1) As Boolean ' Vertical walls
    Friend horizontalWalls(SQCOUNTX - 1, SQCOUNTY) As Boolean ' Horizontal walls

    ' Random number generator
    Private rand As New Random()

    ' Path to premades and saves folders
    Private premadesFolder As String = "D:\01 - Grags\Visual Studio\Enigma\Data\Premades"
    Private savesFolder As String = "D:\01 - Grags\Visual Studio\Enigma\Data\Saves"

    ' Undo/Redo stacks
    Private undoStack As New Stack(Of RoomState)
    Private redoStack As New Stack(Of RoomState)

    ' Current file path
    Private currentFilePath As String = ""

    ' 2D array to store placed items
    Friend placedItems(SQCOUNTX - 1, SQCOUNTY - 1) As PlacedItem

    ' Variable to track if an action is in progress (e.g., wall drawing)
    Private isActionInProgress As Boolean = False

    ' Constructor
    Public Sub New()
        ' Initialize the form components (assumes you have a PictureBox named 'PicGame' and other controls)
        InitializeComponent()

        ' Initialize walls (e.g., set all walls to present)
        InitializeWalls()

        ' Initialize the highlight timer
        highlightTimer = New Timer()
        highlightTimer.Interval = 500 ' Flash every 500 milliseconds
        AddHandler highlightTimer.Tick, AddressOf HighlightTimer_Tick
        highlightTimer.Start()

        ' Attach event handlers to color picker picture boxes
        AddColorPictureBoxHandlers(Me)

        ' Attach MouseDown event handlers to draggable PictureBoxes
        ' Assuming picBlock1 and picBaddy1 are already added via the designer
        AddHandler picBlock1.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picBaddy1.MouseDown, AddressOf PicBlock_MouseDown

        ' Enable dropping on PicGame
        PicGame.AllowDrop = True

        ' Attach DragEnter, DragOver, and DragDrop event handlers to PicGame
        AddHandler PicGame.DragEnter, AddressOf PicGame_DragEnter
        AddHandler PicGame.DragOver, AddressOf PicGame_DragOver
        AddHandler PicGame.DragDrop, AddressOf PicGame_DragDrop

        ' Initialize Undo stack with the initial state
        SaveStateForUndo()
    End Sub

    ' Initialize walls with default values
    Private Sub InitializeWalls()
        For x As Integer = 0 To SQCOUNTX
            For y As Integer = 0 To SQCOUNTY - 1
                verticalWalls(x, y) = True
            Next
        Next
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY
                horizontalWalls(x, y) = True
            Next
        Next

        ' Ensure border walls are always present
        ' Vertical walls at x=0 and x=SQCOUNTX
        For y As Integer = 0 To SQCOUNTY - 1
            verticalWalls(0, y) = True
            verticalWalls(SQCOUNTX, y) = True
        Next

        ' Horizontal walls at y=0 and y=SQCOUNTY
        For x As Integer = 0 To SQCOUNTX - 1
            horizontalWalls(x, 0) = True
            horizontalWalls(x, SQCOUNTY) = True
        Next
    End Sub

    ' Form Load event handler
    Private Sub RoomView_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadPremades()
    End Sub

    ' Load premades into lstPremades
    Private Sub LoadPremades()
        If Not Directory.Exists(premadesFolder) Then
            ' Create the directory if it does not exist
            Directory.CreateDirectory(premadesFolder)
        End If

        lstPremades.Items.Clear()
        Dim files = Directory.GetFiles(premadesFolder, "*.Premade")
        For Each file As String In files
            lstPremades.Items.Add(Path.GetFileNameWithoutExtension(file))
        Next
    End Sub

    ' When a premade is selected from the list
    Private Sub lstPremades_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstPremades.SelectedIndexChanged
        If lstPremades.SelectedIndex >= 0 Then
            ' Save current state for Undo before changing
            SaveStateForUndo()

            Dim selectedPremade As String = lstPremades.SelectedItem.ToString()
            Dim filePath As String = Path.Combine(premadesFolder, selectedPremade & ".Premade")
            LoadSettingsFromFile(filePath)
            PicGame.Invalidate()

            ' Clear Redo stack
            redoStack.Clear()
        End If
    End Sub

    ' Method to load settings from a file
    Private Sub LoadSettingsFromFile(filePath As String)
        Try
            If Not File.Exists(filePath) Then
                MessageBox.Show("File not found: " & filePath)
                Return
            End If

            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
                End If
                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
                        Dim key As String = parts(0).Trim()
                        Dim value As String = parts(1).Trim()
                        Select Case section
                            Case "Walls"
                                LoadProperty(key, value, True)
                            Case "Floors"
                                LoadProperty(key, value, False)
                        End Select
                    End If
                End If
            Next
        Catch ex As Exception
            MessageBox.Show("Error loading settings: " & ex.Message)
        End Try
    End Sub

    ' Helper method to load properties
    Private Sub LoadProperty(key As String, value As String, isWall As Boolean)
        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
                End If
            Case "Color1"
                Dim color As Color = ParseColor(value)
                If isWall Then
                    wallsColor1 = color
                Else
                    floorsColor1 = color
                End If
            Case "Color2"
                Dim color As Color = ParseColor(value)
                If isWall Then
                    wallsColor2 = color
                Else
                    floorsColor2 = color
                End If
        End Select
    End Sub

    ' Helper method to parse color from string "A,R,G,B"
    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

    ' Save premade settings to a file
    Private Sub btnSavePremade_Click(sender As Object, e As EventArgs) Handles btnSavePremade.Click
        Dim saveDialog As New SaveFileDialog()
        saveDialog.InitialDirectory = premadesFolder
        saveDialog.Filter = "Premade Files (*.Premade)|*.Premade"
        saveDialog.DefaultExt = "Premade"
        saveDialog.AddExtension = True
        saveDialog.Title = "Save Premade"

        If saveDialog.ShowDialog() = DialogResult.OK Then
            SaveSettingsToFile(saveDialog.FileName)
            ' Reload premades list
            LoadPremades()
        End If
    End Sub

    ' Method to save settings to a file
    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

    ' Helper method to convert a Color to "A,R,G,B" string
    Private Function ColorToString(color As Color) As String
        Return String.Format("{0},{1},{2},{3}", color.A, color.R, color.G, color.B)
    End Function

    ' Event handler for mnuNew click
    Private Sub mnuNew_Click(sender As Object, e As EventArgs) Handles mnuNew.Click
        ' Save current state for Undo
        SaveStateForUndo()

        ' Reset to default state
        wallsHatchStyle = HatchStyle.Cross
        wallsColor1 = Color.Gray
        wallsColor2 = Color.DarkGray
        floorsHatchStyle = HatchStyle.SmallCheckerBoard
        floorsColor1 = Color.LightGray
        floorsColor2 = Color.White
        InitializeWalls()
        placedItems = New PlacedItem(SQCOUNTX - 1, SQCOUNTY - 1) {}

        ' Clear Redo stack
        redoStack.Clear()

        ' Reset current file path
        currentFilePath = ""

        ' Redraw the grid
        PicGame.Invalidate()
    End Sub

    ' Event handler for mnuSave click
    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

    ' Event handler for mnuLoad click
    Private Sub mnuLoad_Click(sender As Object, e As EventArgs) Handles mnuLoad.Click
        Dim openDialog As New OpenFileDialog()
        openDialog.InitialDirectory = savesFolder ' Set the initial directory to savesFolder
        openDialog.Filter = "Room Files (*.room)|*.room"
        openDialog.DefaultExt = "room"
        openDialog.AddExtension = True
        openDialog.Title = "Load Room"

        If openDialog.ShowDialog() = DialogResult.OK Then
            ' Save current state for Undo
            SaveStateForUndo()

            LoadRoomFromFile(openDialog.FileName)
            currentFilePath = openDialog.FileName

            ' Clear Redo stack
            redoStack.Clear()
        End If
    End Sub

    ' Event handler for mnuSaveAs click
    Private Sub mnuSaveAs_Click(sender As Object, e As EventArgs) Handles mnuSaveAs.Click
        Dim saveDialog As New SaveFileDialog()
        saveDialog.InitialDirectory = savesFolder ' Set the initial directory to savesFolder
        saveDialog.Filter = "Room Files (*.room)|*.room"
        saveDialog.DefaultExt = "room"
        saveDialog.AddExtension = True
        saveDialog.Title = "Save Room As"

        If saveDialog.ShowDialog() = DialogResult.OK Then
            SaveRoomToFile(saveDialog.FileName)
            currentFilePath = saveDialog.FileName
        End If
    End Sub

    ' Method to save the entire room to a file using JSON serialization
    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

    ' Method to load the entire room from a file using JSON deserialization
    Private Sub LoadRoomFromFile(filePath As String)
        Try
            If Not File.Exists(filePath) Then
                MessageBox.Show("File not found: " & filePath)
                Return
            End If

            Dim jsonString As String = File.ReadAllText(filePath)
            Dim options As New JsonSerializerOptions With {
                .IncludeFields = True
            }
            Dim roomState As RoomState = JsonSerializer.Deserialize(Of RoomState)(jsonString, options)
            roomState.RestoreState(Me)
            PicGame.Invalidate()
        Catch ex As Exception
            MessageBox.Show("Error loading room: " & ex.Message)
        End Try
    End Sub

    ' Undo functionality
    Private Sub btnUndo_Click(sender As Object, e As EventArgs) Handles btnUndo.Click
        If undoStack.Count > 1 Then
            ' Save current state to Redo stack
            SaveStateForRedo()

            ' Pop the last state from the Undo stack
            undoStack.Pop()
            Dim previousState As RoomState = undoStack.Peek()
            previousState.RestoreState(Me)

            ' Redraw the grid
            PicGame.Invalidate()
        End If
    End Sub

    ' Redo functionality
    Private Sub btnRedo_Click(sender As Object, e As EventArgs) Handles btnRedo.Click
        If redoStack.Count > 0 Then
            ' Save current state to Undo stack
            SaveStateForUndo()

            ' Pop the state from the Redo stack
            Dim redoState As RoomState = redoStack.Pop()
            redoState.RestoreState(Me)

            ' Redraw the grid
            PicGame.Invalidate()
        End If
    End Sub

    ' Save current state to Undo stack
    Private Sub SaveStateForUndo()
        Dim currentState As New RoomState(Me)
        undoStack.Push(currentState)

        ' Limit the stack size if necessary
        If undoStack.Count > 50 Then
            undoStack = New Stack(Of RoomState)(undoStack.Reverse().Take(50))
        End If
    End Sub

    ' Save current state to Redo stack
    Private Sub SaveStateForRedo()
        Dim currentState As New RoomState(Me)
        redoStack.Push(currentState)
    End Sub

    ' Recursively add event handlers to color picker picture boxes
    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

    ' Event handler for color picker picture boxes
    Private Sub ColorPictureBox_Click(sender As Object, e As EventArgs)
        ' Save current state for Undo
        SaveStateForUndo()

        Dim pb As PictureBox = CType(sender, PictureBox)
        ' Get the color of the clicked picture box
        Dim selectedColor As Color = pb.BackColor

        If rdoFloors.Checked Then
            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
                        ' Change floorsColor1
                        floorsColor1 = selectedColor
                    ElseIf num >= 51 AndAlso num <= 98 Then
                        ' Change floorsColor2
                        floorsColor2 = selectedColor
                    End If
                End If
            End If
        ElseIf rdoWalls.Checked Then
            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
                        ' Change wallsColor1
                        wallsColor1 = selectedColor
                    ElseIf num >= 51 AndAlso num <= 98 Then
                        ' Change wallsColor2
                        wallsColor2 = selectedColor
                    End If
                End If
            End If
        End If

        ' Clear Redo stack
        redoStack.Clear()

        ' Redraw the grid to update colors
        PicGame.Invalidate()
    End Sub

    ' Event handler for lstHatchstyles
    Private Sub lstHatchStyles_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstHatchstyles.SelectedIndexChanged
        ' Save current state for Undo
        SaveStateForUndo()

        ' Get the selected HatchStyle
        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

        ' Clear Redo stack
        redoStack.Clear()

        ' Redraw the grid to update hatch styles
        PicGame.Invalidate()
    End Sub

    ' Event handler for btnRandom click
    Private Sub btnRandom_Click(sender As Object, e As EventArgs) Handles btnRandom.Click
        ' Save current state for Undo
        SaveStateForUndo()

        ' Randomize floor colors
        floorsColor1 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
        floorsColor2 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))

        ' Randomize wall colors
        wallsColor1 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
        wallsColor2 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))

        ' Randomize hatch styles
        Dim hatchStyles As Array = [Enum].GetValues(GetType(HatchStyle))
        floorsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)
        wallsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)

        ' Clear Redo stack
        redoStack.Clear()

        ' Redraw the grid
        PicGame.Invalidate()
    End Sub

    ' Event handler for the highlight timer tick
    Private Sub HighlightTimer_Tick(sender As Object, e As EventArgs)
        ' Toggle the highlight color between black and white
        If highlightColor = Color.Black Then
            highlightColor = Color.White
        Else
            highlightColor = Color.Black
        End If
        PicGame.Invalidate() ' Trigger a redraw on the PictureBox
    End Sub

    ' PicGame event handlers, assuming the PictureBox name is 'PicGame'
    Private Sub PicGame_Paint(sender As Object, e As PaintEventArgs) Handles PicGame.Paint
        ' Calculate square size and draw the grid
        CalculateSquareSizes(PicGame.ClientSize.Width, PicGame.ClientSize.Height)
        e.Graphics.SmoothingMode = SmoothingMode.None
        DrawGrid(e.Graphics)
        DrawHighlight(e.Graphics)

        ' Draw the items on top of the grid
        DrawItems(e.Graphics)
    End Sub

    Private Sub PicGame_MouseMove(sender As Object, e As MouseEventArgs) Handles PicGame.MouseMove
        ' Logic for mouse movement within the grid
        HandleMouseMove(e)
    End Sub

    Private Sub PicGame_MouseDown(sender As Object, e As MouseEventArgs) Handles PicGame.MouseDown
        ' Set mouse button states
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = True
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = True
        End If

        ' Save state for Undo when action starts
        ' For wall modifications, we save the initial state when the mouse button is pressed
        If isLeftMouseDown OrElse isRightMouseDown Then
            ' Begin an action
            isActionInProgress = True
            SaveStateForUndo()
        End If
    End Sub

    Private Sub PicGame_MouseUp(sender As Object, e As MouseEventArgs) Handles PicGame.MouseUp
        ' Reset mouse button states
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = False
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = False
        End If

        ' Action is finished
        If isActionInProgress Then
            isActionInProgress = False

            ' Clear Redo stack
            redoStack.Clear()
        End If
    End Sub

    ' Method to calculate square sizes
    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

    ' Method to draw the grid
    Private Sub DrawGrid(g As Graphics)
        Dim xOrigin As Integer = INDENT
        Dim yOrigin As Integer = INDENT

        ' Draw floors
        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 to fill gaps with wall pattern
        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
        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
                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                Else
                    ' Draw floor pattern where wall is removed
                    Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                        g.FillRectangle(floorBrush, wallRect)
                    End Using
                End If
            Next
        Next

        ' Draw horizontal walls
        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
                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                Else
                    ' Draw floor pattern where wall is removed
                    Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                        g.FillRectangle(floorBrush, wallRect)
                    End Using
                End If
            Next
        Next
    End Sub

    ' Method to draw the highlight
    Private Sub DrawHighlight(g As Graphics)
        If Not isMouseInsideGrid Then
            Return
        End If

        ' Check if it's a border wall
        If highlightedWallX Then
            If highlightedX = 0 OrElse highlightedX = SQCOUNTX Then
                ' Border wall, do not highlight
                Return
            End If
        ElseIf highlightedWallY Then
            If highlightedY = 0 OrElse highlightedY = SQCOUNTY Then
                ' Border wall, do not highlight
                Return
            End If
        End If

        Using highlightPen As New Pen(highlightColor, 2)
            highlightPen.DashStyle = DashStyle.Dash

            If highlightedWallX Then
                ' Vertical wall
                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 highlightRect As New Rectangle(x, y, WALL_THICKNESS, SQSizeY)
                g.DrawRectangle(highlightPen, highlightRect)
            ElseIf highlightedWallY Then
                ' Horizontal wall
                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 highlightRect As New Rectangle(x, y, SQSizeX, WALL_THICKNESS)
                g.DrawRectangle(highlightPen, highlightRect)
            Else
                ' Square
                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 highlightRect As New Rectangle(x, y, SQSizeX, SQSizeY)
                g.DrawRectangle(highlightPen, highlightRect)
            End If
        End Using
    End Sub

    ' Method to handle mouse movement within the grid
    Private Sub HandleMouseMove(e As MouseEventArgs)
        ' Initialize as False at the start of the method
        isMouseInsideGrid = False

        ' Adjust for indentation
        Dim mouseX As Integer = e.X - INDENT
        Dim mouseY As Integer = e.Y - INDENT

        ' Early exit if the mouse is outside the grid area
        If mouseX < 0 OrElse mouseY < 0 Then
            Exit Sub
        End If

        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 OrElse cellY >= SQCOUNTY Then
            Exit Sub
        End If

        Dim offsetX As Integer = mouseX Mod cellSizeX
        Dim offsetY As Integer = mouseY Mod cellSizeY

        highlightedWallX = False
        highlightedWallY = False

        If offsetX < WALL_THICKNESS Then
            ' Vertical wall
            highlightedWallX = True
            highlightedX = cellX
            highlightedY = cellY

            ' Check if it's a border wall
            If highlightedX = 0 OrElse highlightedX = SQCOUNTX Then
                ' It's a border wall, do not highlight
                Exit Sub
            End If

            isMouseInsideGrid = True
        ElseIf offsetY < WALL_THICKNESS Then
            ' Horizontal wall
            highlightedWallY = True
            highlightedX = cellX
            highlightedY = cellY

            ' Check if it's a border wall
            If highlightedY = 0 OrElse highlightedY = SQCOUNTY Then
                ' It's a border wall, do not highlight
                Exit Sub
            End If

            isMouseInsideGrid = True
        ElseIf offsetX >= WALL_THICKNESS AndAlso offsetX < cellSizeX AndAlso offsetY >= WALL_THICKNESS AndAlso offsetY < cellSizeY Then
            ' Inside the square
            highlightedX = cellX
            highlightedY = cellY
            isMouseInsideGrid = True
        Else
            Exit Sub
        End If

        ' If mouse button is down and over a wall, add/remove wall
        If isLeftMouseDown OrElse isRightMouseDown Then
            If highlightedWallX Then
                ' Vertical wall
                ' Check if it's not a border wall
                If highlightedX > 0 AndAlso highlightedX < SQCOUNTX Then
                    If isLeftMouseDown Then
                        verticalWalls(highlightedX, highlightedY) = True
                    ElseIf isRightMouseDown Then
                        verticalWalls(highlightedX, highlightedY) = False
                    End If
                End If
            ElseIf highlightedWallY Then
                ' Horizontal wall
                ' Check if it's not a border wall
                If highlightedY > 0 AndAlso highlightedY < SQCOUNTY Then
                    If isLeftMouseDown Then
                        horizontalWalls(highlightedX, highlightedY) = True
                    ElseIf isRightMouseDown Then
                        horizontalWalls(highlightedX, highlightedY) = False
                    End If
                End If
            End If

            ' Redraw to update the wall
            PicGame.Invalidate()
        End If

        ' Redraw to update the highlight
        PicGame.Invalidate()
    End Sub

    ' Event handler for draggable PictureBoxes
    Private Sub PicBlock_MouseDown(sender As Object, e As MouseEventArgs)
        ' Initiate drag and drop
        Dim pic As PictureBox = CType(sender, PictureBox)
        ' Use the Name as the data (e.g., "picBlock1" or "picBaddy1")
        pic.DoDragDrop(pic.Name, DragDropEffects.Copy)
    End Sub

    ' Event handler for DragEnter on PicGame
    Private Sub PicGame_DragEnter(sender As Object, e As DragEventArgs)
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim data As String = CType(e.Data.GetData(DataFormats.StringFormat), String)
            If data = "picBlock1" OrElse data = "picBaddy1" Then
                e.Effect = DragDropEffects.Copy
            Else
                e.Effect = DragDropEffects.None
            End If
        Else
            e.Effect = DragDropEffects.None
        End If
    End Sub

    ' Event handler for DragOver on PicGame
    Private Sub PicGame_DragOver(sender As Object, e As DragEventArgs)
        e.Effect = DragDropEffects.Copy
    End Sub

    ' Event handler for DragDrop on PicGame
    Private Sub PicGame_DragDrop(sender As Object, e As DragEventArgs)
        ' Get the drop location relative to PicGame
        Dim clientPoint As Point = PicGame.PointToClient(New Point(e.X, e.Y))

        ' Adjust for indentation
        Dim mouseX As Integer = clientPoint.X - INDENT
        Dim mouseY As Integer = clientPoint.Y - INDENT

        ' Early exit if the mouse is outside the grid area
        If mouseX < 0 OrElse mouseY < 0 Then
            Return
        End If

        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 OrElse cellY >= SQCOUNTY Then
            Return
        End If

        ' Calculate the offsets within the cell
        Dim offsetX As Integer = mouseX Mod cellSizeX
        Dim offsetY As Integer = mouseY Mod cellSizeY

        ' Check if dropping onto floor (not wall)
        If offsetX >= WALL_THICKNESS AndAlso offsetY >= WALL_THICKNESS Then
            ' It's in the floor area
            ' Retrieve the ImageKey being dragged
            If e.Data.GetDataPresent(DataFormats.StringFormat) Then
                Dim imgKey As String = CType(e.Data.GetData(DataFormats.StringFormat), String)
                If imgKey = "picBlock1" OrElse imgKey = "picBaddy1" Then
                    ' Save current state for Undo
                    SaveStateForUndo()

                    ' Place the item
                    PlaceItem(cellX, cellY, imgKey)

                    ' Clear Redo stack
                    redoStack.Clear()

                    ' Redraw the grid to show the item
                    PicGame.Invalidate()
                End If
            End If
        End If
    End Sub

    ' Method to place an item on the grid
    Private Sub PlaceItem(x As Integer, y As Integer, imgKey As String)
        placedItems(x, y) = New PlacedItem() With {.ImageKey = imgKey}
    End Sub

    ' Method to draw the items
    Private Sub DrawItems(g As Graphics)
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY - 1
                If placedItems(x, y) IsNot Nothing Then
                    ' Calculate the position
                    Dim xPos As Integer = INDENT + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
                    Dim yPos As Integer = INDENT + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2

                    ' Get the image based on ImageKey
                    Dim img As Image = GetImageByKey(placedItems(x, y).ImageKey)

                    If img IsNot Nothing Then
                        ' Draw the image, resizing it to fit the cell
                        g.DrawImage(img, New Rectangle(xPos, yPos, SQSizeX, SQSizeY))
                    End If
                End If
            Next
        Next
    End Sub

    ' Method to retrieve the image based on the key
    Private Function GetImageByKey(imageKey As String) As Image
        Select Case imageKey
            Case "picBlock1"
                Return picBlock1.Image
            Case "picBaddy1"
                Return picBaddy1.Image
                ' Add cases for other images if any
            Case Else
                Return Nothing
        End Select
    End Function

End Class

' Serializable class to store the state of the room
Public Class RoomState
    Public wallsHatchStyle As HatchStyle
    Public wallsColor1 As Color
    Public wallsColor2 As Color
    Public floorsHatchStyle As HatchStyle
    Public floorsColor1 As Color
    Public floorsColor2 As Color

    Public verticalWalls(,) As Boolean
    Public horizontalWalls(,) As Boolean

    Public placedItems(,) As PlacedItemData

    ' Constructor: captures the state from the RoomView instance
    Public Sub New(roomView As RoomView)
        Me.wallsHatchStyle = roomView.wallsHatchStyle
        Me.wallsColor1 = roomView.wallsColor1
        Me.wallsColor2 = roomView.wallsColor2
        Me.floorsHatchStyle = roomView.floorsHatchStyle
        Me.floorsColor1 = roomView.floorsColor1
        Me.floorsColor2 = roomView.floorsColor2

        ' Clone the wall arrays
        Me.verticalWalls = CType(roomView.verticalWalls.Clone(), Boolean(,))
        Me.horizontalWalls = CType(roomView.horizontalWalls.Clone(), Boolean(,))

        ' Clone the placedItems array
        Dim itemData(roomView.placedItems.GetLength(0) - 1, roomView.placedItems.GetLength(1) - 1) As PlacedItemData
        For x As Integer = 0 To roomView.placedItems.GetLength(0) - 1
            For y As Integer = 0 To roomView.placedItems.GetLength(1) - 1
                If roomView.placedItems(x, y) IsNot Nothing Then
                    itemData(x, y) = New PlacedItemData With {.ImageKey = roomView.placedItems(x, y).ImageKey}
                End If
            Next
        Next
        Me.placedItems = itemData
    End Sub

    ' Restores the state to the RoomView instance
    Public Sub RestoreState(roomView As RoomView)
        roomView.wallsHatchStyle = Me.wallsHatchStyle
        roomView.wallsColor1 = Me.wallsColor1
        roomView.wallsColor2 = Me.wallsColor2
        roomView.floorsHatchStyle = Me.floorsHatchStyle
        roomView.floorsColor1 = Me.floorsColor1
        roomView.floorsColor2 = Me.floorsColor2

        ' Restore the wall arrays
        roomView.verticalWalls = CType(Me.verticalWalls.Clone(), Boolean(,))
        roomView.horizontalWalls = CType(Me.horizontalWalls.Clone(), Boolean(,))

        ' Restore the placedItems array
        roomView.placedItems = New PlacedItem(Me.placedItems.GetLength(0) - 1, Me.placedItems.GetLength(1) - 1) {}
        For x As Integer = 0 To Me.placedItems.GetLength(0) - 1
            For y As Integer = 0 To Me.placedItems.GetLength(1) - 1
                If Me.placedItems(x, y) IsNot Nothing Then
                    roomView.placedItems(x, y) = New PlacedItem With {.ImageKey = Me.placedItems(x, y).ImageKey}
                End If
            Next
        Next
    End Sub
End Class

' Class to represent a placed item on the grid
Public Class PlacedItem
    Public Property ImageKey As String ' e.g., "picBlock1", "picBaddy1"
End Class

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