Imports System.Drawing.Drawing2D
Imports System.IO
Imports System.Text.Json
Public Class FrmDesigner
Inherits Form

#Region " Variables "
Public Property HoveredTrigger As Triggers = Nothing
Private regions As New List(Of RegionInfo)
Public Property TriggerData As New List(Of Triggers)
Private triggerImage2 As Image
Private triggerImage As Image
Private triggerImageSmall As Image
Private triggerImage2Small As Image
Private IsOnGate As Boolean = False
Private IsOnBaddy As Boolean = False
Private xOrigin As Integer = INDENT
Private yOrigin As Integer = INDENT

Private selectedTriggerSquare As New Point(-1, -1)
Private selectedActionSquare As New Point(-1, -1)
Private selectedItemToRemove As String = ""

Private invalidateTimer As Timer
Private draggedSelectionCellX As Integer
Private draggedSelectionCellY As Integer

Private Const MaxRecentFiles As Integer = 10
Private recentFiles As New List(Of String)()
Private recentFilesFilePath As String = Path.Combine(GetProjectDirectory(), "recent_files.txt")

Private lastMouseMoveTime As Date = Date.MinValue
Private Const DEBOUNCE_INTERVAL As Integer = 50 ' milliseconds
Public Property SelectionMode As Boolean = False
Public Property SelectionRequestedByForm As Form = Nothing
Public Property SelectionRequestedFor As String = ""

Private Const INDENT As Integer = 30
Friend SQCOUNTX As Integer = 16
Friend SQCOUNTY As Integer = 11
Private Const WALL_THICKNESS As Integer = 10

Friend wallsHatchStyle As HatchStyle = HatchStyle.LargeConfetti
Friend wallsColor1 As Color = Color.FromArgb(255, 0, 0, 0)
Friend wallsColor2 As Color = Color.FromArgb(255, 64, 0, 0)
Friend floorsHatchStyle As HatchStyle = HatchStyle.Divot
Friend floorsColor1 As Color = Color.FromArgb(255, 64, 0, 0)
Friend floorsColor2 As Color = Color.FromArgb(255, 0, 64, 0)

Friend floorsUseEdgeStyle()() As Boolean 'track edge style usage

Private SQSizeX As Integer
Private SQSizeY As Integer

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 RoomData)
Private redoStack As New Stack(Of RoomData)

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", "Sound/Music")
Private ReadOnly tempFilePath As String = Path.Combine(Path.GetTempPath(), "temp.room")
Private ReadOnly ImagesFolder As String = Path.Combine(projectDir, "Data", "Images")

Private currentFilePath As String = ""
Friend SelectedMusic As String = ""

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

Friend placedItems()() As ItemInfo
Friend verticalWallItems()() As ItemInfo
Friend horizontalWallItems()() As ItemInfo

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

Private isDraggingItem As Boolean = False
Private draggedItem As ItemInfo = 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 Date
Private Const CLICK_DRAG_THRESHOLD As Integer = 5 ' pixels
Private Const CLICK_DRAG_THRESHOLD_SQUARED As Integer = CLICK_DRAG_THRESHOLD * CLICK_DRAG_THRESHOLD

Public useGradient As Boolean = False
Private itemsDictionary As Dictionary(Of String, List(Of ItemInfo))

Private regionOverlayColors As New Dictionary(Of String, Color) From {
{"Region1", Color.FromArgb(128, 255, 255, 0)},   ' Yellow
{"Region2", Color.FromArgb(128, 255, 165, 0)},   ' Orange
{"Region3", Color.FromArgb(128, 173, 255, 47)},  ' Light Green
{"Region4", Color.FromArgb(128, 173, 216, 230)}, ' Light Blue
{"Region5", Color.FromArgb(128, 211, 211, 211)}  ' Light Gray

}

#End Region

#Region " Constructor and Initialization "
Public Sub New()
InitializeComponent()
InitializeWalls()
InitializeTimers()
InitializeEventHandlers()
InitializeAnimationForImages()
If Not isRestoringState Then SaveStateForUndo()
KeyPreview = True

' Load the Trigger.png image
    triggerImage = Image.FromFile(Path.Combine(ImagesFolder, "Trigger.png"))

    ' Load the Trigger2.png image
    triggerImage2 = Image.FromFile(Path.Combine(ImagesFolder, "Trigger2.png"))

    ' Create a smaller version of the image (70% smaller)
    Dim originalWidth As Integer = triggerImage2.Width
    Dim originalHeight As Integer = triggerImage2.Height
    Dim newWidth As Integer = originalWidth * 0.3
    Dim newHeight As Integer = originalHeight * 0.3
    triggerImage2Small = New Bitmap(triggerImage2, New Size(newWidth, newHeight))

    ' Create a smaller version of the Trigger.png image (80% smaller)
    Dim originalWidthTrigger As Integer = triggerImage.Width
    Dim originalHeightTrigger As Integer = triggerImage.Height
    Dim newWidthTrigger As Integer = originalWidthTrigger * 1
    Dim newHeightTrigger As Integer = originalHeightTrigger * 1
    triggerImageSmall = New Bitmap(triggerImage, New Size(newWidthTrigger, newHeightTrigger))
End Sub

Private Sub FrmDesigner_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    RemoveHandler cboX.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged
    RemoveHandler cboY.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged

    If cboX.Items.Contains(SQCOUNTX.ToString()) Then
        cboX.SelectedItem = SQCOUNTX.ToString()
    End If
    If cboY.Items.Contains(SQCOUNTY.ToString()) Then
        cboY.SelectedItem = SQCOUNTY.ToString()
    End If

    AddHandler cboX.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged
    AddHandler cboY.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged

    ' Ensure one of the radio buttons is checked
    rdoWalls.Checked = True

    ' Initialize the color pickers with the current colors
    colPattern.SelectedColor = wallsColor1
    colBackground.SelectedColor = wallsColor2

    LoadMusic()
    LoadPremades()
    LoadRecentFiles()

    UpdateSelections()

    ' Load the state from the temporary file
    If File.Exists(tempFilePath) Then
        Try
            LoadRoomFromFile(tempFilePath)
            UpdateGridSizeUI()
            UpdateSelections()
        Catch ex As Exception
            Dim unused = MessageBox.Show("Error loading temporary file: " & ex.Message)
        End Try
    End If

    ' Reset currentFilePath to treat it as a new file
    currentFilePath = ""

    ' Reset the window title
    Text = "New Room"

    ' Initialize trigger colors
    Dim rand As New Random()
    picGame.Image = Nothing
End Sub
Friend Sub InitializeWalls()
    verticalWalls = New Boolean(SQCOUNTX)() {}
    verticalWallItems = New ItemInfo(SQCOUNTX)() {}
    For x As Integer = 0 To SQCOUNTX
        verticalWalls(x) = New Boolean(SQCOUNTY - 1) {}
        verticalWallItems(x) = New ItemInfo(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 ItemInfo(SQCOUNTX - 1)() {}
    For x As Integer = 0 To SQCOUNTX - 1
        horizontalWalls(x) = New Boolean(SQCOUNTY) {}
        horizontalWallItems(x) = New ItemInfo(SQCOUNTY) {}
        For y As Integer = 0 To SQCOUNTY
            horizontalWalls(x)(y) = True
            horizontalWallItems(x)(y) = Nothing
        Next
    Next

    placedItems = New ItemInfo(SQCOUNTX - 1)() {}
    For x As Integer = 0 To SQCOUNTX - 1
        placedItems(x) = New ItemInfo(SQCOUNTY - 1) {}
        For y As Integer = 0 To SQCOUNTY - 1
            placedItems(x)(y) = Nothing
        Next
    Next

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

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

    animationTimer = New Timer() With {.Interval = 100} ' Update every 100 milliseconds (10 times per second)
    AddHandler animationTimer.Tick, AddressOf OnAnimationTick
    animationTimer.Start()

    invalidateTimer = New Timer() With {.Interval = 30} ' Update every 50 milliseconds (20 times per second)
    AddHandler invalidateTimer.Tick, AddressOf InvalidateTimer_Tick
    invalidateTimer.Start()
End Sub

Private Sub InitializeEventHandlers()
    picGame.AllowDrop = True

    For Each ctl As Control In tabFloor1.Controls
        If TypeOf ctl Is PictureBox Then
            AddHandler ctl.MouseEnter, AddressOf PictureBox_MouseEnter
            AddHandler ctl.MouseLeave, AddressOf PictureBox_MouseLeave
        End If
    Next

    For Each ctl As Control In tabWalls.Controls
        If TypeOf ctl Is PictureBox Then
            AddHandler ctl.MouseEnter, AddressOf PictureBox_MouseEnter
            AddHandler ctl.MouseLeave, AddressOf PictureBox_MouseLeave
        End If
    Next
End Sub

Private Sub InitializeAnimationForImages()
    animatedImages.Add(picBaddy1.Image)
    animatedImages.Add(picBaddy2.Image)
    animatedImages.Add(picBaddy3.Image)
    animatedImages.Add(picBaddy4.Image)
    animatedImages.Add(picLaser1.Image)
    animatedImages.Add(picLaser2.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)
    animatedImages.Add(picBomb.Image)
    animatedImages.Add(picWarp.Image)
    animatedImages.Add(picWarp2.Image)
    animatedImages.Add(picWarp3.Image)
    animatedImages.Add(picVolcano.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 LoadPremades()
If Not Directory.Exists(premadesFolder) Then Dim unused1 = Directory.CreateDirectory(premadesFolder)
lstPremades.Items.Clear()
For Each file As String In Directory.GetFiles(premadesFolder, "*.Premade")
Dim unused = lstPremades.Items.Add(Path.GetFileNameWithoutExtension(file))
Next
End Sub

Private Sub LoadMusic()
    If Not Directory.Exists(musicFolder) Then
        Dim unused = Directory.CreateDirectory(musicFolder)
    End If

    Dim allowedExtensions As String() = {".mp3", ".wav"}
    Dim musicFileFound As Boolean = False
    Dim defaultMusicFile As String = Path.Combine(musicFolder, "The Jolly Drummer.wav")
    Dim defaultVolume As Integer = 100
    Dim defaultGain As Integer = 5

    For Each file As String In Directory.GetFiles(musicFolder)
        Dim extension As String = Path.GetExtension(file).ToLower()
        If allowedExtensions.Contains(extension) Then
            Dim fileNameWithoutExtension As String = Path.GetFileNameWithoutExtension(file)
            Dim volume As Integer = 50
            Dim gain As Integer = 10

            SelectedMusic = String.Format("{0}, V:{1}, G:{2}", file, volume, gain)
            lblAudio.Text = String.Format("{0}, V:{1}, G:{2}", fileNameWithoutExtension, volume, gain)

            musicFileFound = True
            Exit For
        End If
    Next

    If Not musicFileFound Then
        ' Set default music file, volume, and gain
        SelectedMusic = String.Format("{0}, V:{1}, G:{2}", defaultMusicFile, defaultVolume, defaultGain)
        lblAudio.Text = String.Format("{0}, V:{1}, G:{2}", Path.GetFileNameWithoutExtension(defaultMusicFile), defaultVolume, defaultGain)
    End If
End Sub

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

Private Sub OnFrameChanged(o As Object, e As EventArgs)
End Sub

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

Private Function IsVerticalWallOuterBorder(x As Integer) As Boolean
    Return x = 0 OrElse x = SQCOUNTX
End Function

Private Function IsHorizontalWallOuterBorder(y As Integer) As Boolean
    Return y = 0 OrElse y = SQCOUNTY
End Function

Private Function GetItemName(imageKey As String) As String
    Select Case imageKey
        Case "picBlock1"
            Return "Block"
        Case "picBlock2"
            Return "Block2"
        Case "picBaddy1"
            Return "Baddy1"
        Case "picBaddy2"
            Return "Baddy2"
        Case "picLaser1"
            Return "Laser1"
        Case "picLaser2"
            Return "Laser2"
        Case "picFlipper1"
            Return "Flipper1"
        Case "picFlipper2"
            Return "Flipper2"
        Case "picFlipper3"
            Return "Flipper3"
        Case "picFlipper4"
            Return "Flipper4"
        Case "picBaddy3"
            Return "Baddy3"
        Case "picBaddy4"
            Return "Baddy4"
        Case "picCoin"
            Return "Coin"
        Case "picGate"
            Return "Gate"
        Case "picKey"
            Return "Key"
        Case "picStart"
            Return "Start"
        Case "picFinish"
            Return "Finish"
        Case "picBlockStopper1"
            Return "BlockStopper"
        Case "picBall"
            Return "Ball"
        Case "picBlockSwitch"
            Return "BlockSwitch"
        Case "picBomb"
            Return "Bomb"
        Case "picWarp"
            Return "Warp"
        Case "picWarp2"
            Return "Warp2"
        Case "picWarp3"
            Return "Warp3"
        Case "picVolcano"
            Return "Volcano"
        Case Else
            Return "Unknown"
    End Select
End Function

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

        End If
    End If
End Sub

Public Sub UpdateGridSizeUI()
    RemoveHandler cboX.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged
    RemoveHandler cboY.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged

    If Not cboX.Items.Contains(SQCOUNTX.ToString()) Then
        Dim unused1 = cboX.Items.Add(SQCOUNTX.ToString())
    End If
    cboX.SelectedItem = SQCOUNTX.ToString()

    If Not cboY.Items.Contains(SQCOUNTY.ToString()) Then
        Dim unused = cboY.Items.Add(SQCOUNTY.ToString())
    End If
    cboY.SelectedItem = SQCOUNTY.ToString()

    AddHandler cboX.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged
    AddHandler cboY.SelectedIndexChanged, AddressOf CboX_SelectedIndexChanged

    CalculateSquareSizes(picGame.ClientSize.Width, picGame.ClientSize.Height)

End Sub
Private Sub CalculateSquareSizes(clientWidth As Integer, clientHeight As Integer)
    If SQCOUNTX <= 0 OrElse SQCOUNTY <= 0 Then
        Throw New InvalidOperationException("Grid dimensions must be greater than zero.")
    End If

    SQSizeX = (clientWidth - (2 * INDENT) - ((SQCOUNTX + 1) * WALL_THICKNESS)) \ SQCOUNTX
    SQSizeY = (clientHeight - (2 * INDENT) - ((SQCOUNTY + 1) * WALL_THICKNESS)) \ SQCOUNTY
End Sub

#End Region

#Region " File "
Public Sub SaveRoomToFile(filePath As String)
Try
Dim currentState As New RoomData(Me)

Dim options As New JsonSerializerOptions With {
        .IncludeFields = True,
        .WriteIndented = True
    }
        Dim jsonString As String = JsonSerializer.Serialize(currentState, options)

        File.WriteAllText(filePath, jsonString)

        ' Update the window title to reflect the saved file path
        Text = filePath
    Catch ex As Exception
        Dim unused = 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 roomData As RoomData = JsonSerializer.Deserialize(Of RoomData)(jsonString, options)

        If roomData Is Nothing Then
            Dim unused1 = MessageBox.Show("Error: Failed to deserialize room data.")
            Return
        End If

        isRestoringState = True
        roomData.RestoreState(Me)
        isRestoringState = False

        UpdateSelections()

        cboStyleChoice.SelectedIndex = If(roomData.useGradient, cboStyleChoice.Items.IndexOf("Gradient"), cboStyleChoice.Items.IndexOf("HatchStyle"))

        ' Update the window title to reflect the loaded file path
        Text = filePath

        ' Update the audio label with the selected music
        Dim selectedMusicParts As String() = SelectedMusic.Split(New String() {", V:", ", G:"}, StringSplitOptions.None)
        If selectedMusicParts.Length = 3 Then
            Dim audioFile As String = selectedMusicParts(0).Trim()
            Dim volumePart As String = selectedMusicParts(1).Trim()
            Dim gainPart As String = selectedMusicParts(2).Trim()

            Dim volume As Integer
            Dim gain As Integer

            If Not Integer.TryParse(volumePart, volume) Then
                Dim unused2 = MessageBox.Show("Error: Invalid volume value in selected music.")
                Return
            End If

            If Not Integer.TryParse(gainPart, gain) Then
                Dim unused3 = MessageBox.Show("Error: Invalid gain value in selected music.")
                Return
            End If

            Dim fileNameWithoutExtension As String = Path.GetFileNameWithoutExtension(audioFile)
            lblAudio.Text = String.Format("{0}, V:{1}, G:{2}", fileNameWithoutExtension, volume, gain)
        End If

    Catch ex As Exception
        Dim unused = MessageBox.Show("Error loading room: " & ex.Message)
    End Try
End Sub

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

    txtName.Text = ""
    SelectedMusic = ""

    ' Reset the window title
    Text = "New Room"

    UpdateSelections()
End Sub

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

Private Sub MnuSaveAs_Click(sender As Object, e As EventArgs) Handles mnuSaveAs.Click
    ' Create and configure the SaveFileDialog
    Dim saveDialog As New SaveFileDialog() With {
        .InitialDirectory = savesFolder, ' Set initial directory to the project directory
        .Filter = "Room Files (*.room)|*.room",
        .DefaultExt = "room",
        .AddExtension = True,
        .Title = "Save Room As"
    }

    ' Show the dialog and check if the user clicked OK
    If saveDialog.ShowDialog() = DialogResult.OK Then
        ' Save the current room data to the specified file
        SaveRoomToFile(saveDialog.FileName)

        ' Update the current file path
        currentFilePath = saveDialog.FileName

        ' Update recent files list
        UpdateRecentFiles(currentFilePath) ' Update recent files with the new save
    End If
End Sub
Private Sub MnuLoad_Click(sender As Object, e As EventArgs) Handles mnuLoad.Click, btnOpen.Click
    Dim openDialog As New OpenFileDialog() With {
    .InitialDirectory = savesFolder,
    .Filter = "Room Files (*.room)|*.room",
    .DefaultExt = "room",
    .AddExtension = True,
    .Title = "Load Room"
}

    If openDialog.ShowDialog() = DialogResult.OK Then
        If Not isRestoringState Then SaveStateForUndo()
        LoadRoomFromFile(openDialog.FileName)

        UpdateRecentFiles(openDialog.FileName) ' Update recent files

        currentFilePath = openDialog.FileName
        UpdateGridSizeUI()
        UpdateSelections()
    End If
End Sub
Private Sub UpdateRecentFiles(filePath As String)
    ' Remove the file if it already exists in the recent files list
    Dim unused = recentFiles.Remove(filePath)

    ' Add the new file to the top of the list
    recentFiles.Insert(0, filePath)

    ' Ensure we do not exceed the maximum number of recent files
    If recentFiles.Count > MaxRecentFiles Then
        recentFiles.RemoveAt(MaxRecentFiles) ' Remove the oldest entry
    End If

    ' Update the menu with the latest recent files
    UpdateRecentFilesMenu()

    ' Save the recent files to a text file
    SaveRecentFilesToFile()
End Sub

Private Sub UpdateRecentFilesMenu()
    ' Remove any existing recent file items and separators
    For i As Integer = mnu.DropDownItems.Count - 1 To 0 Step -1
        Dim item As ToolStripItem = mnu.DropDownItems(i)
        If (TypeOf item Is ToolStripMenuItem AndAlso CType(item, ToolStripMenuItem).Tag IsNot Nothing AndAlso CType(item, ToolStripMenuItem).Tag.ToString() = "RecentFile") OrElse

TypeOf item Is ToolStripSeparator Then
mnu.DropDownItems.RemoveAt(i)
End If
Next

' Find the "New" menu item
    Dim newMenuItem As ToolStripMenuItem = Nothing
    For Each item As ToolStripItem In mnu.DropDownItems
        If TypeOf item Is ToolStripMenuItem AndAlso CType(item, ToolStripMenuItem).Text = "New" Then
            newMenuItem = CType(item, ToolStripMenuItem)
            Exit For
        End If
    Next

    If newMenuItem Is Nothing Then
        Dim unused1 = MessageBox.Show("New menu item not found.")
        Return
    End If

    ' Add the separator below the "New" menu item
    Dim separatorBelowNew As New ToolStripSeparator()
    mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(newMenuItem) + 1, separatorBelowNew)

    ' Find the "Load" menu item
    Dim loadMenuItem As ToolStripMenuItem = Nothing
    For Each item As ToolStripItem In mnu.DropDownItems
        If TypeOf item Is ToolStripMenuItem AndAlso CType(item, ToolStripMenuItem).Text = "Load" Then
            loadMenuItem = CType(item, ToolStripMenuItem)
            Exit For
        End If
    Next

    If loadMenuItem Is Nothing Then
        Dim unused = MessageBox.Show("Load menu item not found.")
        Return
    End If

    ' Add the separator above the "Load" menu item
    Dim separatorAboveLoad As New ToolStripSeparator()
    mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(loadMenuItem), separatorAboveLoad)

    ' Add the separator above recent files
    Dim separatorAboveRecents As New ToolStripSeparator()
    mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(loadMenuItem) + 1, separatorAboveRecents)

    ' Populate the recent files list in the menu in reverse order
    For i As Integer = recentFiles.Count - 1 To 0 Step -1
        Dim file As String = recentFiles(i)
        Dim menuItem As New ToolStripMenuItem(file) With {
            .Tag = "RecentFile" ' Tag the item to identify it as a recent file item
            } ' Create a new menu item for each file
        AddHandler menuItem.Click, AddressOf RecentFileMenuItem_Click ' Attach event handler
        mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(separatorAboveRecents) + 1, menuItem) ' Add item to the dropdown after the separator
    Next

    ' Add the separator below recent files
    Dim separatorBelowRecents As New ToolStripSeparator()
    mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(separatorAboveRecents) + recentFiles.Count + 1, separatorBelowRecents)

    ' Ensure the Exit menu item is added only once
    Dim exitMenuItem As ToolStripMenuItem = Nothing
    For Each item As ToolStripItem In mnu.DropDownItems
        If TypeOf item Is ToolStripMenuItem AndAlso CType(item, ToolStripMenuItem).Text = "Exit" Then
            exitMenuItem = CType(item, ToolStripMenuItem)
            Exit For
        End If
    Next

    If exitMenuItem Is Nothing Then
        exitMenuItem = New ToolStripMenuItem("Exit")
        AddHandler exitMenuItem.Click, AddressOf ExitMenuItem_Click

        ' Load the icon
        Dim iconPath As String = Path.Combine(projectDir, "Data", "Icons", "close.png")
        If File.Exists(iconPath) Then
            exitMenuItem.Image = Image.FromFile(iconPath)
        End If

        mnu.DropDownItems.Insert(mnu.DropDownItems.IndexOf(separatorBelowRecents) + 1, exitMenuItem)
    End If

    ' Hide the separators if there are no recent files
    separatorAboveRecents.Visible = recentFiles.Count > 0
    separatorBelowRecents.Visible = recentFiles.Count > 0
End Sub

Private Sub ExitMenuItem_Click(sender As Object, e As EventArgs)
    Close()
End Sub

Private Sub SaveRecentFilesToFile()
    Try
        Dim lines As New List(Of String)(recentFiles)
        File.WriteAllLines(recentFilesFilePath, lines)
    Catch ex As Exception
        Dim unused = MessageBox.Show("Error saving recent files: " & ex.Message)
    End Try
End Sub
Private Sub LoadRecentFiles()
    If File.Exists(recentFilesFilePath) Then
        Try
            Dim lines As String() = File.ReadAllLines(recentFilesFilePath)
            recentFiles.AddRange(lines.Where(Function(line) Not String.IsNullOrWhiteSpace(line)).Take(MaxRecentFiles))
        Catch ex As Exception
            Dim unused = MessageBox.Show("Error loading recent files: " & ex.Message)
        End Try
    End If
    UpdateRecentFilesMenu()
End Sub
Private Sub RecentFileMenuItem_Click(sender As Object, e As EventArgs)
    Dim selectedItem As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
    Dim filePath As String = selectedItem.Text

    ' Load the selected recent file
    LoadRoomFromFile(filePath)

    ' Update the current file path
    currentFilePath = filePath

    ' Update the recent files list with the selected file
    UpdateRecentFiles(filePath)
End Sub

Private Sub LoadSettingsFromFile(filePath As String)
    Try
        If Not File.Exists(filePath) Then Return
        Dim lines As String() = File.ReadAllLines(filePath)
        Dim section As String = ""
        For Each line As String In lines
            line = line.Trim()
            If String.IsNullOrEmpty(line) OrElse line.StartsWith(";"c) 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

        UpdateSelections()
    Catch ex As Exception
        Dim unused = 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 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
        Dim unused = MessageBox.Show("Error saving settings: " & ex.Message)
    End Try
End Sub

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

Integer.TryParse(parts(2), g) AndAlso Integer.TryParse(parts(3), b) Then
Return Color.FromArgb(a, r, g, b)
End If
End If
Return Color.Black
End Function
Private Function ColorToString(color As Color) As String
Return $"{color.A},{color.R},{color.G},{color.B}"
End Function

#End Region

#Region " Design "
Private Sub LstPremades_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstPremades.SelectedIndexChanged
TurnOffSelectionMode()
If lstPremades.SelectedIndex >= 0 Then
If Not isRestoringState Then SaveStateForUndo()
Dim selectedPremade As String = lstPremades.SelectedItem.ToString()
LoadSettingsFromFile(Path.Combine(premadesFolder, selectedPremade & ".Premade"))
UpdateSelections()

End If
End Sub

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

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

    End If
End Sub

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

End Sub

Private Sub BtnWalls_Click(sender As Object, e As EventArgs) Handles BtnWall.Click
    TurnOffSelectionMode()
    If Not isRestoringState Then SaveStateForUndo()
    RandomizeWalls()

End Sub

Private Sub BtnFloors_Click(sender As Object, e As EventArgs) Handles BtnFloor.Click
    TurnOffSelectionMode()
    If Not isRestoringState Then SaveStateForUndo()
    RandomizeFloors()

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

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

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

    cboStyleChoice.Enabled = rdoFloors.Checked
    lstHatchstyles.Enabled = Not rdoFloors.Checked OrElse Not useGradient

    If cboStyleChoice.Items.Count > 0 Then
        cboStyleChoice.SelectedIndex = If(useGradient, cboStyleChoice.Items.IndexOf("Gradient"), cboStyleChoice.Items.IndexOf("HatchStyle"))
    End If

    ' Update the color pickers to reflect the current colors
    UpdateColorPickers()
End Sub
Private Sub RdoWalls_CheckedChanged(sender As Object, e As EventArgs) Handles rdoWalls.CheckedChanged
    TurnOffSelectionMode()
    If rdoWalls.Checked Then
        UpdateSelections()
    End If
End Sub

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

Private Sub RdoEdge_CheckedChanged(sender As Object, e As EventArgs) Handles rdoEdge.CheckedChanged
    TurnOffSelectionMode()
    If rdoEdge.Checked Then
        UpdateSelections()
    End If
End Sub
Private Sub CboStyleChoice_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboStyleChoice.SelectedIndexChanged
    TurnOffSelectionMode()
    useGradient = cboStyleChoice.SelectedItem.ToString() = "Gradient"
    lstHatchstyles.Enabled = Not useGradient
    ' Redraw the picGame control
End Sub

Private Sub CboX_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboX.SelectedIndexChanged, cboY.SelectedIndexChanged
    TurnOffSelectionMode()
    UpdateGridSize()
End Sub

Private Sub UpdateGridSize()
    Dim newSQCOUNTX As Integer
    Dim newSQCOUNTY As Integer
    If Integer.TryParse(cboX.SelectedItem?.ToString(), newSQCOUNTX) AndAlso Integer.TryParse(cboY.SelectedItem?.ToString(), newSQCOUNTY) Then
        If Not isRestoringState Then SaveStateForUndo()
        Dim oldSQCOUNTX As Integer = SQCOUNTX
        Dim oldSQCOUNTY As Integer = SQCOUNTY
        SQCOUNTX = newSQCOUNTX
        SQCOUNTY = newSQCOUNTY
        useGradient = cboStyleChoice.SelectedItem?.ToString() = "Gradient"
        ResizeArrays(oldSQCOUNTX, oldSQCOUNTY, SQCOUNTX, SQCOUNTY)
        CalculateSquareSizes(picGame.ClientSize.Width, picGame.ClientSize.Height)

        highlightedX = -1
        highlightedY = -1
        isMouseInsideGrid = False
        selectionRectangle = Rectangle.Empty
    End If
End Sub

Friend Sub ResizeArrays(oldX As Integer, oldY As Integer, newX As Integer, newY As Integer)
    verticalWalls = ResizeJaggedArray(verticalWalls, oldX + 1, oldY - 1, newX + 1, newY - 1, True)
    verticalWallItems = ResizeJaggedArray(verticalWallItems, oldX + 1, oldY - 1, newX + 1, newY - 1, False)
    horizontalWalls = ResizeJaggedArray(horizontalWalls, oldX - 1, oldY + 1, newX - 1, newY + 1, True)
    horizontalWallItems = ResizeJaggedArray(horizontalWallItems, oldX - 1, oldY + 1, newX - 1, newY + 1, False)
    placedItems = ResizeJaggedArray(placedItems, oldX - 1, oldY - 1, newX - 1, newY - 1, False)
    floorsUseEdgeStyle = ResizeJaggedArray(floorsUseEdgeStyle, oldX - 1, oldY - 1, newX - 1, newY - 1, False)
    FillBorderWalls()
End Sub

Private Function ResizeJaggedArray(Of T)(sourceArray As T()(), oldSizeX As Integer, oldSizeY As Integer, newSizeX As Integer, newSizeY As Integer, initializeValue As Boolean) As T()()
    Dim newArray As T()() = New T(newSizeX)() {}
    For x As Integer = 0 To newSizeX
        newArray(x) = New T(newSizeY) {}
        For y As Integer = 0 To newSizeY
            newArray(x)(y) = If(x < sourceArray.Length AndAlso y < sourceArray(x).Length,
                sourceArray(x)(y),
                If(GetType(T) Is GetType(Boolean) AndAlso initializeValue, CType(Convert.ChangeType(True, GetType(T)), T), Nothing))
        Next
    Next
    Return newArray
End Function

#End Region

#Region " Mouse Up, Down, Move "
Private Sub PicGame_MouseDown(sender As Object, e As MouseEventArgs) Handles picGame.MouseDown
If SelectionMode Then
HandleSelectionMode(e)
Return
End If
If e.Button = MouseButtons.Left Then
HandleGateClicked(e.X, e.Y)
HandleBaddyClicked(e.X, e.Y) ' Ensure this line is added
End If
If e.Button = MouseButtons.Right Then
UcBaddyProps.Visible = False
krKeys.Visible = False
End If
If isSelectionReadyToDrag Then
If selectionRectangle.Contains(e.Location) Then
If e.Button = MouseButtons.Left Then
SaveStateForUndo()
isDraggingSelection = True
selectionOffset = New Point(e.X - selectionRectangle.Location.X, e.Y - selectionRectangle.Location.Y)
selectionStartCell = New Point(
(selectionRectangle.Location.X - INDENT) \ (SQSizeX + WALL_THICKNESS),
(selectionRectangle.Location.Y - INDENT) \ (SQSizeY + WALL_THICKNESS))
picGame.Capture = True
Cursor = Cursors.SizeAll
ElseIf e.Button = MouseButtons.Right Then
' Show context menu
Dim menuPosition As Point = e.Location
ctmMenu.Show(picGame, menuPosition)
End If
End If
ElseIf isInSelectionMode Then
' Start selecting a new area
If e.Button = MouseButtons.Left Then
isSelecting = True
selectionStartPoint = e.Location
selectionEndPoint = e.Location
selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)

' Reset selection data
            selectionData = Nothing
            selectionStartCell = New Point(-1, -1)
            copiedSelectionData = Nothing
            copiedSelectionType = ""

            ' Reset the cycle
            isSelectionExpandedToWalls = False
            isSelectionExpandedToFloors = False

            ' Check if the selection starts from a wall
            selectionStartedOnWall = highlightedWallX OrElse highlightedWallY

            ' Adjust the selection start point to snap to the grid
            selectionStartPoint = GetGridAlignedPoint(selectionStartPoint)
        ElseIf e.Button = MouseButtons.Right Then
            If selectionRectangle.Contains(e.Location) Then
                Dim menuPosition As Point = e.Location
                ctmMenu.Show(picGame, menuPosition)
            End If
        Else
            TurnOffSelectionMode()
        End If
    Else
        ' Normal mouse down processing
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = True
            rightMouseDownPosition = e.Location
            rightMouseDownTime = Date.Now
            If Not isActionInProgress Then
                isActionInProgress = True
                SaveStateForUndo()
            End If
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = True
            UpdateHighlight(e.X, e.Y)
            Dim itemPickedUp As Boolean = False
            If isMouseInsideGrid Then
                If Not isActionInProgress Then
                    isActionInProgress = True
                    SaveStateForUndo()
                End If

                If highlightedWallX Then
                    If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso
           verticalWallItems(highlightedX) IsNot Nothing AndAlso
           highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then

                        If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                            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
                    End If
                ElseIf highlightedWallY Then
                    If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso
           horizontalWallItems(highlightedX) IsNot Nothing AndAlso
           highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then

                        If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                            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
                    End If
                Else
                    If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso
           placedItems(highlightedX) IsNot Nothing AndAlso
           highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then

                        If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                            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
                End If

                If itemPickedUp Then
                    redoStack.Clear()
                Else
                    If rdoEdge.Checked Then
                        isLeftMouseDown = True
                        SetEdgeStyleAtMousePosition(e.X, e.Y)
                    Else
                        isLeftMouseDown = True
                        AddWallAtMousePosition(e.X, e.Y)
                    End If
                End If
            End If
        End If
    End If

    ' Check if the krKeys control is visible and the click is outside the cell with the gate
    If krKeys.Visible And IsOnGate = False Then
        Dim clickedX As Integer = (e.X - INDENT) \ (SQSizeX + WALL_THICKNESS)
        Dim clickedY As Integer = (e.Y - INDENT) \ (SQSizeY + WALL_THICKNESS)

        If clickedX >= 0 AndAlso clickedX < placedItems.Length AndAlso

clickedY >= 0 AndAlso clickedY < placedItems(0).Length Then

Dim clickedItem As ItemInfo = placedItems(clickedX)(clickedY)
            If clickedItem Is Nothing OrElse clickedItem.ImageKey <> "picGate" Then
                krKeys.Visible = False
            End If
        Else
            krKeys.Visible = False
        End If
    End If

    ' Check if the UcBaddyProps control is visible and the click is outside the cell with the Baddy
    If UcBaddyProps.Visible AndAlso Not IsOnBaddy Then
        Dim clickedX As Integer = (e.X - INDENT) \ (SQSizeX + WALL_THICKNESS)
        Dim clickedY As Integer = (e.Y - INDENT) \ (SQSizeY + WALL_THICKNESS)

        If clickedX >= 0 AndAlso clickedX < placedItems.Length AndAlso

clickedY >= 0 AndAlso clickedY < placedItems(0).Length Then

Dim clickedItem As ItemInfo = placedItems(clickedX)(clickedY)
            If Not clickedItem.ImageKey.StartsWith("picBaddy") Then
                UcBaddyProps.Visible = False
            End If
        Else
            UcBaddyProps.Visible = False
        End If
    End If
End Sub

Private Sub PicGame_MouseMove(sender As Object, e As MouseEventArgs) Handles picGame.MouseMove
    isCursorOnPicGame = True

    If isDraggingSelection Then
        ' Existing code for dragging selection
        If isCopying Then
            If copiedSelectionData Is Nothing Then
                Return
            End If
        Else
            If selectionData Is Nothing Then
                CaptureSelectionData()
            End If
        End If

        ' Calculate new cell positions
        Dim newX As Integer = e.X - selectionOffset.X
        Dim newY As Integer = e.Y - selectionOffset.Y

        Dim cellX As Integer = (newX - INDENT) \ (SQSizeX + WALL_THICKNESS)
        Dim cellY As Integer = (newY - INDENT) \ (SQSizeY + WALL_THICKNESS)

        ' Ensure cellX and cellY are within bounds
        If isCopying Then
            cellX = Math.Max(0, Math.Min(SQCOUNTX - copiedSelectionData.selectionWidth, cellX))
            cellY = Math.Max(0, Math.Min(SQCOUNTY - copiedSelectionData.selectionHeight, cellY))
            selectionRectangle.Width = (copiedSelectionData.selectionWidth * (SQSizeX + WALL_THICKNESS)) - WALL_THICKNESS
            selectionRectangle.Height = (copiedSelectionData.selectionHeight * (SQSizeY + WALL_THICKNESS)) - WALL_THICKNESS
        Else
            cellX = Math.Max(0, Math.Min(SQCOUNTX - selectionData.selectionWidth, cellX))
            cellY = Math.Max(0, Math.Min(SQCOUNTY - selectionData.selectionHeight, cellY))
            selectionRectangle.Width = (selectionData.selectionWidth * (SQSizeX + WALL_THICKNESS)) - WALL_THICKNESS
            selectionRectangle.Height = (selectionData.selectionHeight * (SQSizeY + WALL_THICKNESS)) - WALL_THICKNESS
        End If

        ' Update selection rectangle position
        selectionRectangle.X = INDENT + (cellX * (SQSizeX + WALL_THICKNESS))
        selectionRectangle.Y = INDENT + (cellY * (SQSizeY + WALL_THICKNESS))

        ' Store cell positions for use in MouseUp
        draggedSelectionCellX = cellX
        draggedSelectionCellY = cellY

    ElseIf isDraggingItem Then
        ' Update the mouse position to the current cursor position
        mouseX = e.X
        mouseY = e.Y

        ' Snap the item to the nearest wall and center it
        UpdateHighlight(e.X, e.Y)
        If draggedItemIsWall Then
            If draggedItemIsVerticalWall Then
                mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 4
                mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeY \ 2)
            Else
                mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeX \ 2)
                mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 4
            End If
        Else
            mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeX \ 2)
            mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeY \ 2)
        End If

    ElseIf isInSelectionMode Then
        If isSelecting Then
            selectionEndPoint = e.Location
            selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)

            ' Adjust the selection rectangle to fit the walls if the selection started from a wall
            If selectionStartedOnWall Then
                selectionRectangle = AdjustSelectionRectangleToWalls(selectionRectangle)
            End If
        End If
    Else
        UpdateHighlight(e.X, e.Y)

        If isRightMouseDown Then
            If rdoEdge.Checked Then
                If isMouseInsideGrid Then
                    RemoveEdgeStyleAtMousePosition(e.X, e.Y)
                End If
            Else
                Dim deltaX As Integer = e.X - rightMouseDownPosition.X
                Dim deltaY As Integer = e.Y - rightMouseDownPosition.Y
                Dim distanceSquared As Integer = (deltaX * deltaX) + (deltaY * deltaY)
                If distanceSquared > CLICK_DRAG_THRESHOLD_SQUARED Then
                    RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=True, removeItems:=False)
                End If
            End If
        End If
        If isLeftMouseDown Then
            If rdoEdge.Checked Then
                If isMouseInsideGrid Then
                    SetEdgeStyleAtMousePosition(e.X, e.Y)
                End If
            Else
                AddWallAtMousePosition(e.X, e.Y)
            End If
        End If
    End If

    UpdateStatusBar(e.X, e.Y)
End Sub

Private Sub PicGame_MouseUp(sender As Object, e As MouseEventArgs) Handles picGame.MouseUp
    If isDraggingSelection Then
        ' Completing selection dragging
        If e.Button = MouseButtons.Left Then
            ' Save the state before modifying the data model
            If Not isRestoringState Then
                SaveStateForUndo()
            End If

            isDraggingSelection = False
            picGame.Capture = False
            Cursor = Cursors.Default

            If selectionRectangle.IsEmpty Then
                Return
            End If

            ' Use stored cell positions from MouseMove
            Dim newCellX As Integer = draggedSelectionCellX
            Dim newCellY As Integer = draggedSelectionCellY

            ' Ensure the new position is within bounds
            newCellX = Math.Max(0, Math.Min(SQCOUNTX - copiedSelectionData.selectionWidth, newCellX))
            newCellY = Math.Max(0, Math.Min(SQCOUNTY - copiedSelectionData.selectionHeight, newCellY))
            PasteSelectionDataTo(New Point(newCellX, newCellY), False, copiedSelectionType = "Edge")

            isCopying = False
            isSelectionReadyToDrag = False

            ' Reset selection-related variables to prevent issues with future operations
            selectionData = Nothing
            selectionStartCell = New Point(-1, -1)
            copiedSelectionData = Nothing
            copiedSelectionType = ""
            isCopying = False
            isSelectionReadyToDrag = False
            isDraggingSelection = False

            ' Clear the redo stack since we've made a new change
            redoStack.Clear()
        End If
    ElseIf isInSelectionMode AndAlso isSelecting Then
        If e.Button = MouseButtons.Left Then
            ' Finalizing the selection rectangle
            isSelecting = False
            selectionEndPoint = e.Location
            selectionRectangle = GetRectangle(selectionStartPoint, selectionEndPoint)

            ' Adjust the selection start point to snap to the grid
            selectionStartPoint = GetGridAlignedPoint(selectionStartPoint)

            ' Adjust the selection end point to snap to the grid
            selectionEndPoint = GetGridAlignedPoint(selectionEndPoint)

            ' Reset selection-related variables
            selectionData = Nothing
            selectionStartCell = New Point(-1, -1)
            copiedSelectionData = Nothing
            copiedSelectionType = ""
            isCopying = False
            isSelectionReadyToDrag = False
            isDraggingSelection = False

            If selectionRectangle.IsEmpty Then
                TurnOffSelectionMode()
            End If
        End If
    Else
        ' Normal mouse up processing
        If e.Button = MouseButtons.Left Then
            If isDraggingItem Then
                ' Save the state before modifying the data model
                If Not isRestoringState Then
                    SaveStateForUndo()
                End If

                isDraggingItem = False
                picGame.Capture = False
                Cursor.Current = Cursors.Default

                ' Snap the item to the grid position
                UpdateHighlight(e.X, e.Y)
                If isMouseInsideGrid Then
                    If draggedItemIsWall Then
                        If draggedItemIsVerticalWall Then
                            If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso
                       verticalWallItems(highlightedX) IsNot Nothing AndAlso
                       highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then

                                If Not IsVerticalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                                    verticalWallItems(highlightedX)(highlightedY) = draggedItem
                                    verticalWalls(highlightedX)(highlightedY) = False
                                Else
                                    ReturnItemToOriginalLocation()
                                End If
                            End If
                        Else
                            If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso
                       horizontalWallItems(highlightedX) IsNot Nothing AndAlso
                       highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then

                                If Not IsHorizontalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                                    horizontalWallItems(highlightedX)(highlightedY) = draggedItem
                                    horizontalWalls(highlightedX)(highlightedY) = False
                                Else
                                    ReturnItemToOriginalLocation()
                                End If
                            End If
                        End If
                    Else
                        If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso
               highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then

                            placedItems(highlightedX)(highlightedY) = draggedItem
                        End If
                    End If
                End If

                ' Clear redo stack since we've made a new change
                redoStack.Clear()

                ' Invalidate the picGame control to refresh the display

            End If

            isLeftMouseDown = False
            isActionInProgress = False

        ElseIf e.Button = MouseButtons.Right Then
            If isRightMouseDown Then
                isRightMouseDown = False

                If rdoEdge.Checked Then
                    UpdateHighlight(e.X, e.Y)
                    If isMouseInsideGrid Then
                        If Not highlightedWallX AndAlso Not highlightedWallY Then
                            If highlightedX >= 0 AndAlso highlightedX < SQCOUNTX AndAlso highlightedY >= 0 AndAlso highlightedY < SQCOUNTY Then
                                floorsUseEdgeStyle(highlightedX)(highlightedY) = False

                            End If
                        End If
                    End If
                Else
                    Dim deltaX As Integer = e.X - rightMouseDownPosition.X
                    Dim deltaY As Integer = e.Y - rightMouseDownPosition.Y
                    Dim distanceSquared As Integer = (deltaX * deltaX) + (deltaY * deltaY)
                    If distanceSquared < CLICK_DRAG_THRESHOLD_SQUARED Then
                        RemoveObjectAtMousePosition(e.X, e.Y, removeWalls:=False, removeItems:=True)
                    End If
                End If

                isActionInProgress = False
            End If

            ' Check if the right-click is outside the selected area
            If isInSelectionMode AndAlso Not selectionRectangle.Contains(e.Location) Then
                TurnOffSelectionMode()
            End If
        End If
    End If
End Sub

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

    isCursorOnPicGame = picGame.ClientRectangle.Contains(picGameClientPoint)

    If isDraggingItem Then
        mouseX = picGameClientPoint.X
        mouseY = picGameClientPoint.Y

        If isCursorOnPicGame Then
            UpdateHighlight(mouseX, mouseY)
            isMouseInsideGrid = True

            ' Snap the item to the nearest wall and center it
            If draggedItemIsWall Then
                If draggedItemIsVerticalWall Then
                    mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 4
                    mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeY \ 2)
                Else
                    mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeX \ 2)
                    mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 4
                End If
            Else
                mouseX = INDENT + (highlightedX * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeX \ 2)
                mouseY = INDENT + (highlightedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (SQSizeY \ 2)
            End If
        Else
            isMouseInsideGrid = False
        End If

        ' Invalidate only the picGame control
    End If

    ' Force the highlight color to toggle based on mouse movement
    Dim currentTime As Date = Date.Now
    If (currentTime - lastMouseMoveTime).TotalMilliseconds >= DEBOUNCE_INTERVAL Then
        highlightColor = If(highlightColor = Color.Black, Color.White, Color.Black)
        lastMouseMoveTime = currentTime
    End If

    UpdateStatusBar(e.X, e.Y)
End Sub

Private Sub Form_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
    If isDraggingItem AndAlso e.Button = MouseButtons.Left Then
        ' Before modifying the data model, save the state
        If Not isRestoringState Then
            SaveStateForUndo()
        End If

        isDraggingItem = False
        Capture = False
        Cursor.Current = Cursors.Default

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

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

            If isMouseInsideGrid Then
                If draggedItemIsWall Then
                    If draggedItemIsVerticalWall Then
                        If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso
                       verticalWallItems(highlightedX) IsNot Nothing AndAlso
                       highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then

                            If Not IsVerticalWallConnectedToEdge(New Point(highlightedX, highlightedY)) OrElse IsFlipperItem(draggedItem.ImageKey) Then
                                verticalWallItems(highlightedX)(highlightedY) = draggedItem
                                verticalWalls(highlightedX)(highlightedY) = False
                            Else
                                ReturnItemToOriginalLocation()
                            End If
                        End If
                    Else
                        If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso
                       horizontalWallItems(highlightedX) IsNot Nothing AndAlso
                       highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then

                            If Not IsHorizontalWallConnectedToEdge(New Point(highlightedX, highlightedY)) OrElse IsFlipperItem(draggedItem.ImageKey) Then
                                horizontalWallItems(highlightedX)(highlightedY) = draggedItem
                                horizontalWalls(highlightedX)(highlightedY) = False
                            Else
                                ReturnItemToOriginalLocation()
                            End If
                        End If
                    End If
                Else
                    ' Existing code for placing non-wall items
                    If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso
                   highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then

                        placedItems(highlightedX)(highlightedY) = draggedItem
                    End If
                End If
            Else
                ReturnItemToOriginalLocation()
            End If
        Else
            ReturnItemToOriginalLocation()
        End If

        draggedItem = Nothing

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

        redoStack.Clear()
    End If
End Sub

Private Sub PicBlock_MouseDown(sender As Object, e As MouseEventArgs) Handles picBlock1.MouseDown, picBlock2.MouseDown, picBaddy1.MouseDown, picBaddy2.MouseDown, picLaser1.MouseDown, picLaser2.MouseDown, picFlipper1.MouseDown, picFlipper2.MouseDown, picFlipper3.MouseDown, picFlipper4.MouseDown, picBaddy3.MouseDown, picBaddy4.MouseDown, picCoin.MouseDown, picGate.MouseDown, picKey.MouseDown, picStart.MouseDown, picFinish.MouseDown, picBlockStopper1.MouseDown, picBall.MouseDown, picBlockSwitch.MouseDown, picBomb.MouseDown, picWarp.MouseDown, picWarp2.MouseDown, picWarp3.MouseDown, picVolcano.MouseDown
    TurnOffSelectionMode()
    Dim pic = CType(sender, PictureBox)

    isDraggingItem = True

    ' Create a new PlacedItem with the ImageKey
    draggedItem = New ItemInfo(pic.Name, New Point(0, 0), Guid.NewGuid.ToString, isDoodad:=IsDoodadItem(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
    Capture = True

    Cursor.Current = Cursors.Hand

    ' Initialize the mouse position to the current cursor position
    Dim mousePos = picGame.PointToClient(Cursor.Position)
    mouseX = mousePos.X
    mouseY = mousePos.Y
End Sub

#End Region

#Region " Mouse Helpers "
Private Function GetGridAlignedPoint(point As Point) As Point
Dim x As Integer = (point.X - INDENT) \ (SQSizeX + WALL_THICKNESS)
Dim y As Integer = (point.Y - INDENT) \ (SQSizeY + WALL_THICKNESS)
Dim alignedX As Integer = INDENT + (x * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2)
Dim alignedY As Integer = INDENT + (y * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2)
Return New Point(alignedX, alignedY)
End Function

Private Function AdjustSelectionRectangleToWalls(rect As Rectangle) As Rectangle
    ' Calculate start and end cell indices from the selection rectangle
    Dim startCellX As Integer = (rect.X - INDENT + (WALL_THICKNESS \ 2)) \ (SQSizeX + WALL_THICKNESS)
    Dim startCellY As Integer = (rect.Y - INDENT + (WALL_THICKNESS \ 2)) \ (SQSizeY + WALL_THICKNESS)
    Dim endCellX As Integer = (rect.Right - INDENT - (WALL_THICKNESS \ 2) - 1) \ (SQSizeX + WALL_THICKNESS)
    Dim endCellY As Integer = (rect.Bottom - INDENT - (WALL_THICKNESS \ 2) - 1) \ (SQSizeY + WALL_THICKNESS)

    ' Ensure cell indices are within bounds
    startCellX = Math.Max(0, Math.Min(SQCOUNTX - 1, startCellX))
    startCellY = Math.Max(0, Math.Min(SQCOUNTY - 1, startCellY))
    endCellX = Math.Max(0, Math.Min(SQCOUNTX - 1, endCellX))
    endCellY = Math.Max(0, Math.Min(SQCOUNTY - 1, endCellY))

    Dim selectionWidth As Integer = endCellX - startCellX + 1
    Dim selectionHeight As Integer = endCellY - startCellY + 1

    ' Adjust the selection rectangle to match the grid and exclude walls
    Dim adjustedRect As New Rectangle(
    INDENT + (startCellX * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2),
    INDENT + (startCellY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2),
    (selectionWidth * (SQSizeX + WALL_THICKNESS)) - WALL_THICKNESS,
    (selectionHeight * (SQSizeY + WALL_THICKNESS)) - WALL_THICKNESS
)

    Return adjustedRect
End Function

Private Sub HandleBaddyClicked(mouseX As Integer, mouseY As Integer)
    ' Calculate the cell coordinates based on the mouse position
    Dim clickedX As Integer = (mouseX - INDENT) \ (SQSizeX + WALL_THICKNESS)
    Dim clickedY As Integer = (mouseY - INDENT) \ (SQSizeY + WALL_THICKNESS)

    ' Check if the clicked cell contains a Baddy
    If clickedX >= 0 AndAlso clickedX < placedItems.Length AndAlso clickedY >= 0 AndAlso clickedY < placedItems(0).Length Then
        Dim clickedItem As ItemInfo = placedItems(clickedX)(clickedY)
        If clickedItem IsNot Nothing AndAlso clickedItem.ImageKey.StartsWith("picBaddy") Then
            IsOnBaddy = True

            ' Calculate the top-right corner of the clicked cell
            Dim cellTopRightX As Integer = INDENT + (clickedX * (SQSizeX + WALL_THICKNESS)) + SQSizeX + 9
            Dim cellTopRightY As Integer = INDENT + (clickedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS * 2) + 38

            ' Set the position and make the control visible
            UcBaddyProps.Location = New Point(cellTopRightX, cellTopRightY)
            UcBaddyProps.Visible = True

            ' Set properties of UcBaddyProps based on the clicked Baddy item
            UcBaddyProps.ItemInfo = clickedItem

            UcBaddyProps.BringToFront() ' Bring the control to the front
        Else
            ' Hide the control if the clicked item is not a Baddy
            UcBaddyProps.Visible = False
        End If
    End If
End Sub

Private Sub HandleGateClicked(mouseX As Integer, mouseY As Integer)
    ' Calculate the cell coordinates based on the mouse position
    Dim clickedX As Integer = (mouseX - INDENT) \ (SQSizeX + WALL_THICKNESS)
    Dim clickedY As Integer = (mouseY - INDENT) \ (SQSizeY + WALL_THICKNESS)

    ' Check if the clicked cell contains a gate
    If clickedX >= 0 AndAlso clickedX < placedItems.Length AndAlso

clickedY >= 0 AndAlso clickedY < placedItems(0).Length Then

Dim clickedItem As ItemInfo = placedItems(clickedX)(clickedY)
        If clickedItem IsNot Nothing AndAlso clickedItem.ImageKey = "picGate" Then
            IsOnGate = True
            ' Calculate the top-right corner of the clicked cell
            Dim cellTopRightX As Integer = INDENT + (clickedX * (SQSizeX + WALL_THICKNESS)) + SQSizeX + 7
            Dim cellTopRightY As Integer = INDENT + (clickedY * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS * 2) + 38

            ' Set the position and make the control visible
            krKeys.Location = New Point(cellTopRightX, cellTopRightY)
            krKeys.SelectedGate = clickedItem ' Set the SelectedGate property
            krKeys.Grid = picGame ' Set the Grid property
            krKeys.SetNumKeysValue(clickedItem.KeysRequiredToOpen) ' Set the NumKeys value to the gate's value
            krKeys.Visible = True
        Else
            IsOnGate = False
        End If
    End If
End Sub

Private Sub HandleSelectionMode(e As MouseEventArgs)
    ' Handle selecting a square in selection mode
    Dim clickedX As Integer = (e.X - INDENT) \ (SQSizeX + WALL_THICKNESS)
    Dim clickedY As Integer = (e.Y - INDENT) \ (SQSizeY + WALL_THICKNESS)

    ' Determine if the user clicked on a wall or a floor
    Dim isWall As Boolean = highlightedWallX OrElse highlightedWallY
    Dim itemType As String = If(isWall, If(highlightedWallX, "VerticalWall", "HorizontalWall"), "Floor")

    ' Determine the item at the selected point
    Dim item As String
    If isWall Then
        If highlightedWallX Then
            ' Check vertical wall items
            If clickedX >= 0 AndAlso clickedX < verticalWallItems.Length AndAlso
   clickedY >= 0 AndAlso clickedY < verticalWallItems(clickedX).Length Then
                item = If(verticalWallItems(clickedX)(clickedY) IsNot Nothing, verticalWallItems(clickedX)(clickedY).ImageKey, "Nothing")
            End If
        ElseIf highlightedWallY Then
            ' Check horizontal wall items
            If clickedX >= 0 AndAlso clickedX < horizontalWallItems.Length AndAlso
   clickedY >= 0 AndAlso clickedY < horizontalWallItems(clickedX).Length Then
                item = If(horizontalWallItems(clickedX)(clickedY) IsNot Nothing, horizontalWallItems(clickedX)(clickedY).ImageKey, "Nothing")
            End If
        End If
    Else
        ' Check floor items
        If clickedX >= 0 AndAlso clickedX < placedItems.Length AndAlso

clickedY >= 0 AndAlso clickedY < placedItems(clickedX).Length Then
item = If(placedItems(clickedX)(clickedY) IsNot Nothing, placedItems(clickedX)(clickedY).ImageKey, "Nothing")
End If
End If

' Check selection constraints
    Dim triggerEditor As frmTriggerEditor = CType(SelectionRequestedByForm, frmTriggerEditor)
    If SelectionRequestedFor = "TriggeringPoint" Then
        If triggerEditor.AllowFloorSelectionOnly AndAlso isWall Then
            Return
        End If
    ElseIf SelectionRequestedFor = "TriggerActionPoint" Then
        ' Allow clicking anything for TriggerActionPoint
    ElseIf SelectionRequestedFor = "TriggerItemToRemove" Then
        If triggerEditor.AllowWallSelectionOnly AndAlso Not isWall Then
            Return
        End If
    End If

    ' Pass the selected point, item, and item type back to the requesting form
    If TypeOf SelectionRequestedByForm Is frmTriggerEditor Then
        If SelectionRequestedFor = "TriggeringPoint" Then
            triggerEditor.SetTriggeringPoint(New Point(clickedX, clickedY))
        ElseIf SelectionRequestedFor = "TriggerActionPoint" Then
            triggerEditor.SetTriggerActionPoint(New Point(clickedX, clickedY), itemType)
        End If
    End If

    TurnOffSelectionMode()

    ' Bring FrmTriggerEditor to the foreground
    If TypeOf SelectionRequestedByForm Is frmTriggerEditor Then
        triggerEditor.BringToFront() ' Bring FrmTriggerEditor to the front
        Dim unused = triggerEditor.Focus() ' Focus on FrmTriggerEditor
    End If
End Sub

' Create a new method to reset the cursor
Private Sub ResetTheCursor()
    picGame.Cursor = Cursors.Default
    Cursor = Cursors.Default
End Sub
' Call this method in TurnOffSelectionMode
Private Sub TurnOffSelectionMode()
    isInSelectionMode = False
    selectionRectangle = Rectangle.Empty
    SelectionMode = False
    SelectionRequestedByForm = Nothing
    SelectionRequestedFor = Nothing
    ResetTheCursor()

    ' Reset the cycle
    isSelectionExpandedToWalls = False
    isSelectionExpandedToFloors = False
End Sub

Private Function IsDoodadItem(imageKey As String) As Boolean
    Return imageKey = "picBlockSwitch" OrElse imageKey.StartsWith("picDoodad")
End Function
Private Sub UpdateStatusBar(pixelX As Integer, pixelY As Integer)
    ' Calculate the square coordinates based on the mouse position
    Dim squareX As Integer = (pixelX - INDENT + ((SQSizeX + WALL_THICKNESS) \ 2)) \ (SQSizeX + WALL_THICKNESS)
    Dim squareY As Integer = (pixelY - INDENT + ((SQSizeY + WALL_THICKNESS) \ 2)) \ (SQSizeY + WALL_THICKNESS)
    Dim triggerInfo As String = ""

    ' Determine if the mouse is over a wall
    Dim isOverWall As Boolean = highlightedWallX OrElse highlightedWallY

    ' Determine the content of the square or wall
    Dim content As String = "Empty"
    Dim uniqueId As String = ""

    If isOverWall Then
        If highlightedWallX Then
            ' Check vertical wall items or walls
            If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso

highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then
If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
content = GetItemFullName(verticalWallItems(highlightedX)(highlightedY).ImageKey)
uniqueId = verticalWallItems(highlightedX)(highlightedY).UniqueId
ElseIf verticalWalls(highlightedX)(highlightedY) Then
content = "Wall"
End If
End If
ElseIf highlightedWallY Then
' Check horizontal wall items or walls
If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso
highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then
If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
content = GetItemFullName(horizontalWallItems(highlightedX)(highlightedY).ImageKey)
uniqueId = horizontalWallItems(highlightedX)(highlightedY).UniqueId
ElseIf horizontalWalls(highlightedX)(highlightedY) Then
content = "Wall"
End If
End If
End If
Else
' Check floor items
If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso
highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then
If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
content = GetItemFullName(placedItems(highlightedX)(highlightedY).ImageKey)
uniqueId = placedItems(highlightedX)(highlightedY).UniqueId
ElseIf floorsUseEdgeStyle(highlightedX)(highlightedY) Then
content = "Edge"
End If
End If
End If

' Update the status bar labels
    stbMouseCoords.Text = \$"{pixelX}, {pixelY}"

    If isOverWall Then
        If highlightedWallX Then
            stbGridCoords.Text = $"Vertical Wall: {highlightedX}, {highlightedY}"
        ElseIf highlightedWallY Then
            stbGridCoords.Text = $"Horizontal Wall: {highlightedX}, {highlightedY}"
        End If
    Else
        stbGridCoords.Text = \$"Floor: {highlightedX}, {highlightedY}"
    End If

    stbItemType.Text = content
    stbID.Text = uniqueId

    ' Look for triggers associated with this point using GRID COORDINATES
    For Each trigger As Triggers In TriggerData
        ' Check floor triggers
        If trigger.TriggeringPoint.Item1 = highlightedX AndAlso trigger.TriggeringPoint.Item2 = highlightedY Then
            triggerInfo = trigger.TriggerFull
            Exit For
        End If

        ' Check if the TriggerActionPoint is on a wall
        If trigger.TriggerActionPoint.Item1 = highlightedX AndAlso trigger.TriggerActionPoint.Item2 = highlightedY Then
            If trigger.TriggerActionIsWall Then
                If trigger.TriggerActionIsVerticalWall AndAlso highlightedWallX Then
                    triggerInfo = trigger.TriggerFull
                ElseIf trigger.TriggerActionIsHorizontalWall AndAlso highlightedWallY Then
                    triggerInfo = trigger.TriggerFull
                End If
            Else
                triggerInfo = trigger.TriggerFull
            End If
            Exit For
        End If
    Next

    ' Only update stbTrigger if not over a wall or if the trigger action point is on the correct wall type
    If Not isOverWall Then
        stbTrigger.Text = triggerInfo ' Display the trigger information in the status bar
    Else
        For Each trigger As Triggers In TriggerData
            If trigger.TriggerActionPoint.Item1 = highlightedX AndAlso trigger.TriggerActionPoint.Item2 = highlightedY Then
                If trigger.TriggerActionIsWall AndAlso
       ((trigger.TriggerActionIsVerticalWall AndAlso highlightedWallX) OrElse
        (trigger.TriggerActionIsHorizontalWall AndAlso highlightedWallY)) Then
                    stbTrigger.Text = triggerInfo ' Display the trigger information in the status bar
                    Exit For
                End If
            End If
        Next
        If stbTrigger.Text = "" Then
            stbTrigger.Text = ""
        End If
    End If

    ' Apply formatting to the RichTextBox
    ApplyFormatting(stbTrigger, triggerInfo)
End Sub
Private Function GetItemTypeAtPosition(x As Integer, y As Integer) As String
    ' Determine if the selected item is a wall, wall item, or floor item
    If highlightedWallX Then
        Return If(verticalWallItems(x)(y) IsNot Nothing, "VerticalWallItem", "VerticalWall")
    ElseIf highlightedWallY Then
        Return If(horizontalWallItems(x)(y) IsNot Nothing, "HorizontalWallItem", "HorizontalWall")
    Else
        Return If(placedItems(x)(y) IsNot Nothing, "FloorItem", "Floor")
    End If
End Function

Private Sub HideUcBaddyProps()
    UcBaddyProps.Visible = False
End Sub

Public Sub PictureBox_MouseEnter(sender As Object, e As EventArgs)
    Dim pb As PictureBox = DirectCast(sender, PictureBox)
    pb.BackColor = Color.LightBlue
End Sub

Public Sub PictureBox_MouseLeave(sender As Object, e As EventArgs)
    Dim pb As PictureBox = DirectCast(sender, PictureBox)
    pb.BackColor = Color.Transparent
End Sub

Private Sub AddWallAtMousePosition(mouseX As Integer, mouseY As Integer)
    UpdateHighlight(mouseX, mouseY)

    If highlightedWallX Then
        If highlightedX >= 0 AndAlso highlightedX < verticalWalls.Length AndAlso

highlightedY >= 0 AndAlso highlightedY < verticalWalls(0).Length Then
verticalWalls(highlightedX)(highlightedY) = True

End If
    ElseIf highlightedWallY Then
        If highlightedX >= 0 AndAlso highlightedX < horizontalWalls.Length AndAlso

highlightedY >= 0 AndAlso highlightedY < horizontalWalls(0).Length Then
horizontalWalls(highlightedX)(highlightedY) = True

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 Not isMouseInsideGrid Then
        Return
    End If

    If highlightedWallX Then
        If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso

verticalWallItems(highlightedX) IsNot Nothing AndAlso
highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then

If removeItems AndAlso verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                verticalWallItems(highlightedX)(highlightedY) = Nothing
                verticalWalls(highlightedX)(highlightedY) = True

                If IsVerticalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                    verticalWalls(highlightedX)(highlightedY) = True
                End If
            ElseIf removeWalls AndAlso verticalWalls(highlightedX)(highlightedY) Then
                If Not IsVerticalWallOuterBorder(highlightedX) AndAlso Not IsVerticalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                    verticalWalls(highlightedX)(highlightedY) = False

                End If
            End If
        End If
    ElseIf highlightedWallY Then
        If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso

horizontalWallItems(highlightedX) IsNot Nothing AndAlso
highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then

If removeItems AndAlso horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                horizontalWallItems(highlightedX)(highlightedY) = Nothing
                horizontalWalls(highlightedX)(highlightedY) = True

                If IsHorizontalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                    horizontalWalls(highlightedX)(highlightedY) = True
                End If
            ElseIf removeWalls AndAlso horizontalWalls(highlightedX)(highlightedY) Then
                If Not IsHorizontalWallOuterBorder(highlightedY) AndAlso Not IsHorizontalWallConnectedToEdge(New Point(highlightedX, highlightedY)) Then
                    horizontalWalls(highlightedX)(highlightedY) = False

                End If
            End If
        End If
    Else
        If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso

placedItems(highlightedX) IsNot Nothing AndAlso
highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then

If removeItems AndAlso placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                placedItems(highlightedX)(highlightedY) = Nothing

            End If
        End If
    End If
End Sub

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

            If highlightedX >= 0 AndAlso highlightedX < verticalWalls.Length AndAlso
       verticalWalls(highlightedX) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < verticalWalls(0).Length Then
                verticalWallItems(highlightedX)(highlightedY) = Nothing
                verticalWalls(highlightedX)(highlightedY) = True
            End If

            If highlightedX + 1 >= 0 AndAlso highlightedX + 1 < verticalWalls.Length AndAlso
       verticalWalls(highlightedX + 1) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < verticalWalls(0).Length Then
                verticalWallItems(highlightedX + 1)(highlightedY) = Nothing
                verticalWalls(highlightedX + 1)(highlightedY) = True
            End If

            If highlightedX >= 0 AndAlso highlightedX < horizontalWalls.Length AndAlso
       horizontalWalls(highlightedX) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < horizontalWalls(0).Length Then
                horizontalWallItems(highlightedX)(highlightedY) = Nothing
                horizontalWalls(highlightedX)(highlightedY) = True
            End If

            If highlightedX >= 0 AndAlso highlightedX < horizontalWalls.Length AndAlso
       horizontalWalls(highlightedX) IsNot Nothing AndAlso
       highlightedY + 1 >= 0 AndAlso highlightedY + 1 < horizontalWalls(0).Length Then
                horizontalWallItems(highlightedX)(highlightedY + 1) = Nothing
                horizontalWalls(highlightedX)(highlightedY + 1) = True
            End If

        End If
    End If
End Sub
Private Function IsHorizontalWallConnectedToEdge(pt As Point) As Boolean
    Dim x As Integer = pt.X
    Dim y As Integer = pt.Y

    If y > 0 AndAlso floorsUseEdgeStyle(x)(y - 1) Then Return True
    If y < SQCOUNTY AndAlso floorsUseEdgeStyle(x)(y) Then Return True

    Return False
End Function
Private Function IsVerticalWallConnectedToEdge(pt As Point) As Boolean
    Dim x As Integer = pt.X
    Dim y As Integer = pt.Y

    If x > 0 AndAlso floorsUseEdgeStyle(x - 1)(y) Then Return True
    If x < SQCOUNTX AndAlso floorsUseEdgeStyle(x)(y) Then Return True

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

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

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

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

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

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

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

Private Sub 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 IsVerticalItem(imgKey As String) As Boolean
    Return imgKey = "picLaser1" OrElse imgKey = "picFlipper1" OrElse imgKey = "picFlipper2"
End Function

Private Function IsHorizontalItem(imgKey As String) As Boolean
    Return imgKey = "picLaser2" OrElse imgKey = "picFlipper3" OrElse imgKey = "picFlipper4"
End Function
Private Function IsFlipperItem(item As ItemInfo) As Boolean
    If item Is Nothing Then Return False

    Dim imgKey = item.ImageKey
    Return imgKey = "picFlipper1" OrElse imgKey = "picFlipper2" _
    OrElse imgKey = "picFlipper3" OrElse imgKey = "picFlipper4"
End Function

Private Function IsFlipperItem(imgKey As String) As Boolean
    Return imgKey = "picFlipper1" OrElse imgKey = "picFlipper2" _
    OrElse imgKey = "picFlipper3" OrElse imgKey = "picFlipper4"
End Function

#End Region

#Region " Draw "
Private Sub PicGame_Paint(sender As Object, e As PaintEventArgs) Handles picGame.Paint
' Calculate the sizes of the squares based on the client size of picGame
CalculateSquareSizes(picGame.ClientSize.Width, picGame.ClientSize.Height)

' Set the smoothing mode to None for pixel-perfect drawing
    e.Graphics.SmoothingMode = SmoothingMode.None

    ' Draw the gradient background if useGradient is true
    If useGradient Then
        DrawGradientBackground(e.Graphics)
    End If

    ' Draw the grid
    DrawGrid(e.Graphics)

    ' Draw any dragged item
    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
                    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)
                    Dim newWidth As Integer = img.Width
                    Dim newHeight As Integer = wallHeight
                    Dim centeredX As Integer = xPos + ((wallWidth - newWidth) \ 2)
                    drawRect = New Rectangle(centeredX, yPos, newWidth, newHeight)
                Else
                    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)
                    Dim newWidth As Integer = wallWidth
                    Dim newHeight As Integer = img.Height
                    Dim centeredY As Integer = yPos + ((wallHeight - newHeight) \ 2)
                    drawRect = New Rectangle(xPos, centeredY, newWidth, newHeight)
                End If
            Else
                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

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

    ' Draw selection data during drag
    If isDraggingSelection Then
        Dim baseCellX As Integer = (selectionRectangle.X - INDENT) \ (SQSizeX + WALL_THICKNESS)
        Dim baseCellY As Integer = (selectionRectangle.Y - INDENT) \ (SQSizeY + WALL_THICKNESS)
        If isCopying Then
            DrawSelectionData(e.Graphics, baseCellX, baseCellY, copiedSelectionData)
        Else
            DrawSelectionData(e.Graphics, baseCellX, baseCellY, selectionData)
        End If
    End If

    ' Ensure DrawHighlight is called
    DrawHighlight(e.Graphics)

    ' Draw triggers
    DrawTriggers(e.Graphics)
End Sub
Private Sub DrawGradientBackground(g As Graphics)
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT
    Dim gridWidth As Integer = SQCOUNTX * (SQSizeX + WALL_THICKNESS)
    Dim gridHeight As Integer = SQCOUNTY * (SQSizeY + WALL_THICKNESS)

    Dim gradientRect As New Rectangle(xOrigin, yOrigin, gridWidth, gridHeight)

    Using brush As New LinearGradientBrush(gradientRect, floorsColor1, floorsColor2, LinearGradientMode.Vertical)
        g.FillRectangle(brush, gradientRect)
    End Using
End Sub

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

    DrawGradientBackground(g)

    For x As Integer = 0 To SQCOUNTX - 1
        For y As Integer = 0 To SQCOUNTY - 1
            Dim floorRect As New Rectangle(
xOrigin + (x * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2),
yOrigin + (y * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2),
SQSizeX,
SQSizeY

)

If floorsUseEdgeStyle(x)(y) Then
                Using floorBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            ElseIf useGradient Then
                Using hatchBrush As New HatchBrush(floorsHatchStyle, Color.Transparent, Color.Transparent)
                    g.FillRectangle(hatchBrush, floorRect)
                End Using
            Else
                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            End If
        Next
    Next

    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

    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 x >= 0 AndAlso x < verticalWalls.Length AndAlso

verticalWalls(x) IsNot Nothing AndAlso
y >= 0 AndAlso y < verticalWalls(0).Length Then

If verticalWalls(x)(y) Then
                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                ElseIf useGradient Then
                    Using hatchBrush As New HatchBrush(floorsHatchStyle, Color.Transparent, Color.Transparent)
                        g.FillRectangle(hatchBrush, wallRect)
                    End Using
                Else
                    Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                        g.FillRectangle(floorBrush, wallRect)
                    End Using
                End If
            End If
        Next
    Next

    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 x >= 0 AndAlso x < horizontalWalls.Length AndAlso

horizontalWalls(x) IsNot Nothing AndAlso
y >= 0 AndAlso y < horizontalWalls(0).Length Then

If horizontalWalls(x)(y) Then
                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                ElseIf useGradient Then
                    Using hatchBrush As New HatchBrush(floorsHatchStyle, Color.Transparent, Color.Transparent)
                        g.FillRectangle(hatchBrush, wallRect)
                    End Using
                Else
                    Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                        g.FillRectangle(floorBrush, wallRect)
                    End Using
                End If
            End If
        Next
    Next

    DrawItems(g)
    DrawWallItems(g)

    DrawGridNumbers(g)

    ' Draw triggers
    DrawTriggers(g)
End Sub

Private Sub DrawTriggers(g As Graphics)
    If triggerImage Is Nothing OrElse triggerImage2Small Is Nothing Then Return ' Ensure the images are loaded

    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT

    For Each trigger As Triggers In TriggerData
        ' Draw the trigger icon for the TriggeringPoint (always on floor)
        If trigger.TriggeringPoint.Item1 >= 0 AndAlso trigger.TriggeringPoint.Item1 < SQCOUNTX AndAlso
   trigger.TriggeringPoint.Item2 >= 0 AndAlso trigger.TriggeringPoint.Item2 < SQCOUNTY Then

            ' Move the TriggeringPoint icon to the top-left of the floor
            Dim triggerX = xOrigin + (trigger.TriggeringPoint.Item1 * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + 4 ' Slightly right of the top-left
            Dim triggerY = yOrigin + (trigger.TriggeringPoint.Item2 * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + 4 ' Slightly down from the top-left
            g.DrawImage(triggerImageSmall, triggerX, triggerY)
        End If

        ' Draw the trigger icon for the TriggerActionPoint
        If trigger.TriggerActionPoint.Item1 >= 0 AndAlso trigger.TriggerActionPoint.Item1 <= SQCOUNTX AndAlso
   trigger.TriggerActionPoint.Item2 >= 0 AndAlso trigger.TriggerActionPoint.Item2 <= SQCOUNTY Then

            Dim actionX As Integer = 0
            Dim actionY As Integer = 0

            If trigger.TriggerActionIsWall Then
                ' Handle drawing for walls (horizontal or vertical)
                If trigger.TriggerActionIsVerticalWall Then
                    ' Move to the top of the vertical wall
                    actionX = xOrigin + (trigger.TriggerActionPoint.Item1 * (SQSizeX + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) - 2
                    actionY = yOrigin + (trigger.TriggerActionPoint.Item2 * (SQSizeY + WALL_THICKNESS)) - (triggerImage2Small.Height \ 2) + 12 ' Slightly above the center
                ElseIf trigger.TriggerActionIsHorizontalWall Then
                    ' Move to the center of the horizontal wall
                    actionX = xOrigin + (trigger.TriggerActionPoint.Item1 * (SQSizeX + WALL_THICKNESS)) + (SQSizeX \ 2) - 20
                    actionY = yOrigin + (trigger.TriggerActionPoint.Item2 * (SQSizeY + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) - (triggerImage2Small.Height \ 2) + 2
                End If
            Else
                ' Move the action point icon to the top-left of the floor
                If trigger.TriggerActionPoint.Item1 >= 0 AndAlso trigger.TriggerActionPoint.Item1 < SQCOUNTX AndAlso
       trigger.TriggerActionPoint.Item2 >= 0 AndAlso trigger.TriggerActionPoint.Item2 < SQCOUNTY Then
                    actionX = xOrigin + (trigger.TriggerActionPoint.Item1 * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + 4 ' Slightly right of the top-left
                    actionY = yOrigin + (trigger.TriggerActionPoint.Item2 * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + 4 ' Slightly down from the top-left
                End If
            End If

            ' Check if the trigger action is "Flip Horizontal" or "Flip Vertical"
            If trigger.TriggerAction = "Flip Horizontal" OrElse trigger.TriggerAction = "Flip Vertical" Then
                ' Skip drawing the trigger icon for these actions
                Continue For
            End If

            ' Draw the action trigger icon using triggerImage2Small
            g.DrawImage(triggerImage2Small, actionX, actionY)

            ' Draw a line between the triggering point and the action point if hovered
            If HoveredTrigger IsNot Nothing AndAlso HoveredTrigger.Equals(trigger) Then
                Dim pen As New Pen(Color.Red, 2)

                ' Calculate the center of the triggering point icon
                Dim startX As Integer = xOrigin + (trigger.TriggeringPoint.Item1 * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (triggerImageSmall.Width \ 2)
                Dim startY As Integer = yOrigin + (trigger.TriggeringPoint.Item2 * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (triggerImageSmall.Height \ 2)

                ' Calculate the center of the action point icon
                Dim endX As Integer = actionX + (triggerImage2Small.Width \ 2)
                Dim endY As Integer = actionY + (triggerImage2Small.Height \ 2)

                g.DrawLine(pen, startX, startY, endX, endY)
            End If
        End If
    Next
End Sub
Private Sub DrawGridNumbers(g As Graphics)
    Dim xOrigin As Integer = INDENT
    Dim yOrigin As Integer = INDENT
    Dim font As New Font("Arial", 12, FontStyle.Regular)
    Dim brush As New SolidBrush(Color.Black)
    Dim brush2 As New SolidBrush(Color.Green)
    Dim smallFont As New Font("Arial", 6, FontStyle.Regular)

    Dim columnNumberOffsetY As Single = -20.0F
    Dim rowNumberOffsetX As Single = -5.0F

    For x As Integer = 0 To SQCOUNTX - 1
        Dim xPos As Single = xOrigin + (x * (SQSizeX + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (CSng(SQSizeX) / 2)
        Dim yPos As Single = yOrigin + columnNumberOffsetY

        Dim label As String = x.ToString()
        Dim xLabelSize As SizeF = g.MeasureString(label, font)
        g.DrawString(label, font, brush, xPos - (xLabelSize.Width / 2.0F), yPos)
    Next

    For y As Integer = 0 To SQCOUNTY - 1
        Dim xPos As Single = xOrigin + rowNumberOffsetX
        Dim yPos As Single = yOrigin + (y * (SQSizeY + WALL_THICKNESS)) + (WALL_THICKNESS \ 2) + (CSng(SQSizeY) / 2)

        Dim label As String = y.ToString()
        Dim yLabelSize As SizeF = g.MeasureString(label, font)
        g.DrawString(label, font, brush, xPos - yLabelSize.Width, yPos - (yLabelSize.Height / 2.0F))
    Next

    ' Draw smaller numbers for vertical walls
    For x As Integer = 0 To SQCOUNTX
        Dim smallLabel As String = x.ToString()
        Dim smallXPos As Single = xOrigin + (x * (SQSizeX + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 4
        Dim smallYPos As Single = yOrigin + columnNumberOffsetY + 4
        Dim smallXLabelSize As SizeF = g.MeasureString(smallLabel, smallFont)
        g.DrawString(smallLabel, smallFont, brush2, smallXPos - (smallXLabelSize.Width / 2.0F), smallYPos)
    Next

    ' Draw smaller numbers for horizontal walls
    For y As Integer = 0 To SQCOUNTY
        Dim smallLabel As String = y.ToString()
        Dim smallYPos As Single = yOrigin + (y * (SQSizeY + WALL_THICKNESS)) - (WALL_THICKNESS \ 2) + 3
        Dim smallXPos As Single = xOrigin + rowNumberOffsetX
        Dim smallYLabelSize As SizeF = g.MeasureString(smallLabel, smallFont)
        g.DrawString(smallLabel, smallFont, brush2, smallXPos - smallYLabelSize.Width, smallYPos - (smallYLabelSize.Height / 2.0F))
    Next

    font.Dispose()
    brush.Dispose()
    smallFont.Dispose()
End Sub

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

Private Sub DrawItems(g As Graphics)
    ' Font and brush for key count text
    Dim keyFont As New Font("Arial", 14, FontStyle.Bold) ' Text will now be white

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

                        Dim imgAttributes As New Imaging.ImageAttributes()
                        imgAttributes.SetWrapMode(WrapMode.TileFlipXY)
                        g.DrawImage(img, destRect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttributes)

                        ' Draw the key count in the center of the gate (only for gates)
                        If placedItems(x)(y).ImageKey = "picGate" Then
                            Dim keysRequired = placedItems(x)(y).KeysRequiredToOpen
                            Dim numKeyText As String = keysRequired.ToString()

                            ' Center the text in the middle of the gate
                            Dim textSize As SizeF = g.MeasureString(numKeyText, keyFont)
                            Dim textX As Integer = xPos + (SQSizeX \ 2) - (textSize.Width \ 2)
                            Dim textY As Integer = yPos + (SQSizeY \ 2) - (textSize.Height \ 2)

                            ' Draw the number of keys on the gate
                            g.DrawString(numKeyText, keyFont, Brushes.White, textX, textY)
                        End If
                    End If
                End If
            Next
        End If
    Next

    ' Draw region overlays if chkShowRegions is checked
    If chkShowRegions.Checked Then
        For Each region As RegionInfo In regions
            If regionOverlayColors.ContainsKey(region.Name) Then
                Dim overlayColor As Color = regionOverlayColors(region.Name)
                Using brush As New SolidBrush(overlayColor)
                    g.FillRectangle(brush, region.Rectangle)
                End Using
            End If
        Next
    End If
End Sub
Private Sub DrawWallItems(g As Graphics)
    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

                        Dim newWidth As Integer = img.Width
                        Dim newHeight As Integer = wallHeight

                        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

    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

                        Dim newWidth As Integer = wallWidth
                        Dim newHeight As Integer = img.Height

                        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 OrElse highlightedX < 0 OrElse highlightedY < 0 OrElse Not isCursorOnPicGame Then
        Return
    End If

    ' Determine the highlight color
    Dim highlightColorToUse As Color = highlightColor
    If SelectionMode Then
        Dim triggerEditor As frmTriggerEditor = CType(SelectionRequestedByForm, frmTriggerEditor)
        If (triggerEditor.AllowFloorSelectionOnly AndAlso (highlightedWallX OrElse highlightedWallY)) OrElse
       (triggerEditor.AllowWallSelectionOnly AndAlso Not (highlightedWallX OrElse highlightedWallY)) Then
            highlightColorToUse = Color.Red
        End If
    End If

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

    isMouseInsideGrid = False
    highlightedWallX = False
    highlightedWallY = False
    highlightedX = -1
    highlightedY = -1

    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 >= 0 AndAlso cellX <= SQCOUNTX AndAlso
   cellY >= 0 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

    If highlightedX < 0 Then highlightedX = 0
    If highlightedX > SQCOUNTX Then highlightedX = SQCOUNTX
    If highlightedY < 0 Then highlightedY = 0
    If highlightedY > SQCOUNTY Then highlightedY = SQCOUNTY

    If highlightedWallX AndAlso (highlightedX = 0 OrElse highlightedX = SQCOUNTX) Then
        highlightedWallX = True
    End If

    If highlightedWallY AndAlso (highlightedY = 0 OrElse highlightedY = SQCOUNTY) Then
        highlightedWallY = True
    End If

    Dim itemName As String

    If highlightedWallX Then
        If highlightedX >= 0 AndAlso highlightedX < verticalWallItems.Length AndAlso
       verticalWallItems(highlightedX) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < verticalWallItems(0).Length Then
            If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                itemName = GetItemName(verticalWallItems(highlightedX)(highlightedY).ImageKey)
            End If
        End If
    ElseIf highlightedWallY Then
        If highlightedX >= 0 AndAlso highlightedX < horizontalWallItems.Length AndAlso
       horizontalWallItems(highlightedX) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < horizontalWallItems(0).Length Then
            If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                itemName = GetItemName(horizontalWallItems(highlightedX)(highlightedY).ImageKey)
            End If
        End If
    Else
        If highlightedX >= 0 AndAlso highlightedX < placedItems.Length AndAlso
       placedItems(highlightedX) IsNot Nothing AndAlso
       highlightedY >= 0 AndAlso highlightedY < placedItems(0).Length Then
            If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                itemName = GetItemName(placedItems(highlightedX)(highlightedY).ImageKey)
            End If
        End If
    End If
    ' Reset hoveredTrigger
    HoveredTrigger = Nothing

    ' Check if the mouse is over a triggering point or action point
    For Each trigger As Triggers In TriggerData
        If trigger.TriggeringPoint.Item1 = highlightedX AndAlso trigger.TriggeringPoint.Item2 = highlightedY Then
            If Not highlightedWallX AndAlso Not highlightedWallY Then
                HoveredTrigger = trigger
                Exit For
            End If
        End If
        If trigger.TriggerActionPoint.Item1 = highlightedX AndAlso trigger.TriggerActionPoint.Item2 = highlightedY Then
            If trigger.TriggerActionIsWall Then
                If trigger.TriggerActionIsVerticalWall AndAlso highlightedWallX Then
                    HoveredTrigger = trigger
                    Exit For
                ElseIf trigger.TriggerActionIsHorizontalWall AndAlso highlightedWallY Then
                    HoveredTrigger = trigger
                    Exit For
                End If
            ElseIf Not highlightedWallX AndAlso Not highlightedWallY Then
                HoveredTrigger = trigger
                Exit For
            End If
        End If
    Next
End Sub

Private Function GetImageByKey(imageKey As String) As Image
    Select Case imageKey
        Case "picBlock1"
            Return picBlock1.Image
        Case "picBlock2"
            Return picBlock2.Image
        Case "picBaddy1"
            Return picBaddy1.Image
        Case "picBaddy2"
            Return picBaddy2.Image
        Case "picBaddy3"
            Return picBaddy3.Image
        Case "picBaddy4"
            Return picBaddy4.Image
        Case "picLaser1"
            Return picLaser1.Image
        Case "picLaser2"
            Return picLaser2.Image
        Case "picFlipper1"
            Return picFlipper1.Image
        Case "picFlipper2"
            Return picFlipper2.Image
        Case "picFlipper3"
            Return picFlipper3.Image
        Case "picFlipper4"
            Return picFlipper4.Image
        Case "picCoin"
            Return picCoin.Image
        Case "picGate"
            Return picGate.Image
        Case "picKey"
            Return picKey.Image
        Case "picStart"
            Return picStart.Image
        Case "picFinish"
            Return picFinish.Image
        Case "picBlockStopper1"
            Return picBlockStopper1.Image
        Case "picBall"
            Return picBall.Image
        Case "picBlockSwitch"
            Return picBlockSwitch.Image
        Case "picBomb"
            Return picBomb.Image
        Case "picWarp"
            Return picWarp.Image
        Case "picWarp2"
            Return picWarp2.Image
        Case "picWarp3"
            Return picWarp3.Image
        Case "picVolcano"
            Return picVolcano.Image
        Case Else
            Return Nothing
    End Select
End Function

#End Region

#Region " Colours "
Private Sub UpdateColorPickers()
If rdoWalls.Checked Then
' Set color pickers to wall colors
colPattern.SelectedColor = wallsColor1
colBackground.SelectedColor = wallsColor2
ElseIf rdoFloors.Checked Then
' Set color pickers to floor colors
colPattern.SelectedColor = floorsColor1
colBackground.SelectedColor = floorsColor2
End If
End Sub

Public Sub UpdateGameColors(senderObject As Object, newColor As Color)
    If rdoWalls.Checked Then
        ' Update wall colors
        If senderObject Is colPattern Then
            wallsColor1 = newColor
        ElseIf senderObject Is colBackground Then
            wallsColor2 = newColor
        End If
    ElseIf rdoFloors.Checked Then
        ' Update floor colors
        If senderObject Is colPattern Then
            floorsColor1 = newColor
        ElseIf senderObject Is colBackground Then
            floorsColor2 = newColor
        End If
    End If

    ' Redraw the game area to reflect changes

End Sub

Private Sub ColPattern_ColorChanged(sender As Object, e As EventArgs) Handles colPattern.ColorChanged
    TurnOffSelectionMode()
    UpdateGameColors(colPattern, colPattern.SelectedColor)
End Sub

Private Sub ColBackground_ColorChanged(sender As Object, e As EventArgs) Handles colBackground.ColorChanged
    TurnOffSelectionMode()
    UpdateGameColors(colBackground, colBackground.SelectedColor)
End Sub

#End Region

#Region " Undo, Redo "
Private Sub SaveStateForUndo()
If isRestoringState Then Return
undoStack.Push(New RoomData(Me))
If undoStack.Count > 50 Then
undoStack = New Stack(Of RoomData)(undoStack.Reverse().Take(50))
End If
End Sub

Private Sub SaveState(isNewAction As Boolean)
    If isRestoringState Then Return
    If isNewAction Then
        redoStack.Clear()
    End If
    undoStack.Push(New RoomData(Me))
    If undoStack.Count > 50 Then
        undoStack = New Stack(Of RoomData)(undoStack.Reverse().Take(50))
    End If
End Sub
Private Sub BtnUndo_Click(sender As Object, e As EventArgs) Handles btnUndo.Click
    TurnOffSelectionMode()
    If undoStack.Count > 1 Then
        ' Save the current state for redo before performing undo
        SaveStateForRedo()

        ' Pop the current state from the undo stack
        Dim unused = undoStack.Pop()

        ' Restore the previous state
        isRestoringState = True
        Dim previousState As RoomData = undoStack.Peek()
        previousState.RestoreState(Me)
        isRestoringState = False

        ' Update the UI to reflect the restored state
        UpdateGridSizeUI()
        UpdateSelections()

    End If
End Sub

Private Sub SaveStateForRedo()
    If isRestoringState Then Return
    redoStack.Push(New RoomData(Me))
    If redoStack.Count > 50 Then
        redoStack = New Stack(Of RoomData)(redoStack.Reverse().Take(50))
    End If
End Sub

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

    End If
End Sub

Private Sub FrmDesigner_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
    ' Check for Ctrl+Z (Undo)
    If e.Control AndAlso e.KeyCode = Keys.Z Then
        BtnUndo_Click(sender, e)
        e.SuppressKeyPress = True ' Prevent further processing
    End If

    ' Check for Ctrl+Y (Redo)
    If e.Control AndAlso e.KeyCode = Keys.Y Then
        BtnRedo_Click(sender, e)
        e.SuppressKeyPress = True
    End If
End Sub

#End Region

#Region " ToolStrip Buttons "
Private Sub BtnTriggerEditor_Click(sender As Object, e As EventArgs) Handles btnTriggerEditor.Click
TurnOffSelectionMode()
Dim frm As New frmTriggerEditor(TriggerData)
AddHandler frm.FormClosed, AddressOf OnTriggerEditorFormClosed
frm.Show()
End Sub

Private Sub OnTriggerEditorFormClosed(sender As Object, e As FormClosedEventArgs)
    ' Update the triggerData in FrmDesigner with the current state from FrmTriggerEditor
    Dim frmTriggerEditor As FrmTriggerEditor = CType(sender, FrmTriggerEditor)
    triggerData = frmTriggerEditor.triggerData
End Sub

Private Sub BtnFlipHorizontal_Click(sender As Object, e As EventArgs) Handles btnFlipHorizontal.Click
    TurnOffSelectionMode()
    If Not isRestoringState Then SaveStateForUndo()
    Utilities.FlipGridHorizontally(Me)
End Sub

Private Sub BtnFlipVertical_Click(sender As Object, e As EventArgs) Handles btnFlipVertical.Click
    TurnOffSelectionMode()
    If Not isRestoringState Then SaveStateForUndo()
    Utilities.FlipGridVertically(Me)
End Sub
Private Sub BtnFillDeadEnds_Click(sender As Object, e As EventArgs) Handles btnFillDeadEnds.Click
    TurnOffSelectionMode()
    If Not isRestoringState Then SaveStateForUndo()

    ' Loop through each square in the grid
    For y As Integer = 0 To SQCOUNTY - 1
        For x As Integer = 0 To SQCOUNTX - 1
            Dim wallCount As Integer = 0
            Dim isFlipperPresent As Boolean = False ' To track if any flippers are present

            ' Count the walls around the square at (x, y)
            If x = 0 OrElse (x > 0 AndAlso (verticalWalls(x)(y) OrElse IsFlipperItem(verticalWallItems(x)(y)))) Then
                wallCount += 1  ' Check vertical wall on the left
                If IsFlipperItem(verticalWallItems(x)(y)) Then isFlipperPresent = True
            End If
            If x = SQCOUNTX - 1 OrElse (x < SQCOUNTX AndAlso (verticalWalls(x + 1)(y) OrElse IsFlipperItem(verticalWallItems(x + 1)(y)))) Then
                wallCount += 1  ' Check vertical wall on the right
                If IsFlipperItem(verticalWallItems(x + 1)(y)) Then isFlipperPresent = True
            End If
            If y = 0 OrElse (y > 0 AndAlso (horizontalWalls(x)(y) OrElse IsFlipperItem(horizontalWallItems(x)(y)))) Then
                wallCount += 1  ' Check horizontal wall on the top
                If IsFlipperItem(horizontalWallItems(x)(y)) Then isFlipperPresent = True
            End If
            If y = SQCOUNTY - 1 OrElse (y < SQCOUNTY AndAlso (horizontalWalls(x)(y + 1) OrElse IsFlipperItem(horizontalWallItems(x)(y + 1)))) Then
                wallCount += 1  ' Check horizontal wall on the bottom
                If IsFlipperItem(horizontalWallItems(x)(y + 1)) Then isFlipperPresent = True
            End If

            ' Check if the square is not occupied, has three walls, and does not contain a flipper
            If wallCount = 3 AndAlso Not isFlipperPresent AndAlso placedItems(x)(y) Is Nothing Then
                placedItems(x)(y) = New ItemInfo("picCoin", New Point(x, y), Guid.NewGuid().ToString())
            End If
        Next
    Next
End Sub

Private Sub BtnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
    TurnOffSelectionMode()
    Dim tempFilePath As String = Path.Combine(Path.GetTempPath(), "temp.room")
    SaveRoomToFile(tempFilePath)

    PauseBackgroundTasks()

    Dim gameForm As New frmGame With {
        .RoomFilePath = tempFilePath
    }
    AddHandler gameForm.FormClosed, AddressOf OnGameFormClosed
    gameForm.Show()
End Sub
Private Sub PauseBackgroundTasks()
    animationTimer.Stop()
    highlightTimer.Stop()
End Sub
Private Sub OnGameFormClosed(sender As Object, e As FormClosedEventArgs)
    'When frmGame is closed
    ResumeBackgroundTasks()
End Sub

Public Function CollectItems() As Dictionary(Of String, List(Of ItemInfo))
    Dim itemsDictionary As New Dictionary(Of String, List(Of ItemInfo))

    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 itemKey As String = placedItems(x)(y).ImageKey
                    Dim itemName As String = GetItemName(itemKey)
                    Dim location As New Point(x, y)
                    Dim uniqueId As String = \$"{itemName}_{x}_{y}"

                    If Not itemsDictionary.ContainsKey(itemName) Then
                        itemsDictionary(itemName) = New List(Of ItemInfo)
                    End If

                    itemsDictionary(itemName).Add(New ItemInfo(itemKey, location, uniqueId))
                End If
            Next
        End If
    Next

    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 itemKey As String = verticalWallItems(x)(y).ImageKey
                    Dim itemName As String = GetItemName(itemKey)
                    Dim location As New Point(x, y)
                    Dim uniqueId As String = \$"{itemName}_{x}_{y}"

                    If Not itemsDictionary.ContainsKey(itemName) Then
                        itemsDictionary(itemName) = New List(Of ItemInfo)
                    End If

                    itemsDictionary(itemName).Add(New ItemInfo(itemKey, location, uniqueId))
                End If
            Next
        End If
    Next

    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 itemKey As String = horizontalWallItems(x)(y).ImageKey
                    Dim itemName As String = GetItemName(itemKey)
                    Dim location As New Point(x, y)
                    Dim uniqueId As String = \$"{itemName}_{x}_{y}"

                    If Not itemsDictionary.ContainsKey(itemName) Then
                        itemsDictionary(itemName) = New List(Of ItemInfo)
                    End If

                    itemsDictionary(itemName).Add(New ItemInfo(itemKey, location, uniqueId))
                End If
            Next
        End If
    Next

    Return itemsDictionary
End Function

Private Sub BtnProperties_Click(sender As Object, e As EventArgs) Handles btnProperties.Click
    TurnOffSelectionMode()
    Dim itemsDictionary = CollectItems()
    Dim propertiesForm As New frmProperties(itemsDictionary, Me)
    Dim unused = propertiesForm.ShowDialog()
    frmGame.Dispose()
End Sub

Private Sub BtnAudio_Click(sender As Object, e As EventArgs) Handles btnAudio.Click, lblAudio.Click
    TurnOffSelectionMode()
    Dim audioFile As String = ""
    Dim volume As Integer = 50 ' Default volume
    Dim gain As Integer = 10 ' Default gain

    If Not String.IsNullOrWhiteSpace(SelectedMusic) Then
        Dim selectedMusicParts As String() = SelectedMusic.Split(New String() {", V:", ", G:"}, StringSplitOptions.None)
        If selectedMusicParts.Length = 3 Then
            audioFile = selectedMusicParts(0).Trim()
            If Integer.TryParse(selectedMusicParts(1).Trim(), volume) AndAlso Integer.TryParse(selectedMusicParts(2).Trim(), gain) Then
                ' Valid format, proceed with the values
            Else
                Dim unused1 = MessageBox.Show("Error parsing volume or gain values.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            End If
        Else
            Dim unused = MessageBox.Show("SelectedMusic string is not in the expected format.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If
    End If

    PauseBackgroundTasks()
    Using frm As New frmMusic With {
    .InitialVolume = volume,
    .InitialGain = gain,
    .InitialSelectedTrack = audioFile
}
        If frm.ShowDialog() = DialogResult.OK Then
            Dim returnedVolume As Integer = frm.VolumeLevel
            Dim returnedGain As Integer = frm.GainLevel
            Dim returnedAudioFile As String = frm.AudioFile

            SelectedMusic = String.Format("{0}, V:{1}, G:{2}", returnedAudioFile, returnedVolume, returnedGain)

            Dim fileNameWithoutExtension As String = Path.GetFileNameWithoutExtension(returnedAudioFile)
            lblAudio.Text = String.Format("{0}, V:{1}, G:{2}", fileNameWithoutExtension, returnedVolume, returnedGain)
        End If
    End Using
End Sub

#End Region

#Region " Closing "
Private Sub FrmDesigner_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
SaveRoomToFile(tempFilePath)
End Sub
Private Sub ResumeBackgroundTasks()
animationTimer.Start()
highlightTimer.Start()
End Sub

Private Sub picGame_Click(sender As Object, e As EventArgs) Handles picGame.Click

End Sub

#End Region

End Class

Can you help... When I've finished selecting an area on the grid. The secleted area alters on it's own. More over. The right side moves and the bottom moves. The top left corner correctly stays where it's supposed to after snapping. Everything snaps perfectly. But at the end the Bottom Right corner moves. And it should stay where it is, where I let go of mousedown