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

Public Class RoomView
    Inherits Form

    ' Constants
    Private Const INDENT As Integer = 20
    Private Const SQCOUNTX As Integer = 15
    Private Const SQCOUNTY As Integer = 11
    Private Const WALL_THICKNESS As Integer = 10

    ' Graphics styles and colors
    Friend wallsHatchStyle As HatchStyle = HatchStyle.Cross
    Friend wallsColor1 As Color = Color.Gray
    Friend wallsColor2 As Color = Color.DarkGray
    Friend floorsHatchStyle As HatchStyle = HatchStyle.SmallCheckerBoard
    Friend floorsColor1 As Color = Color.LightGray
    Friend floorsColor2 As Color = Color.White

    ' Grid variables
    Private SQSizeX As Integer
    Private SQSizeY As Integer

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

    ' Highlight variables
    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

    ' Undo/Redo variables
    Private isRestoringState As Boolean = False
    Private isActionInProgress As Boolean = False
    Private undoStack As New Stack(Of RoomState)
    Private redoStack As New Stack(Of RoomState)

    ' File paths
    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 currentFilePath As String = ""

    ' Grid and walls state
    Friend verticalWalls()() As Boolean
    Friend horizontalWalls()() As Boolean

    ' Placed items
    Friend placedItems()() As PlacedItem
    Friend verticalWallItems()() As PlacedItem
    Friend horizontalWallItems()() As PlacedItem

    ' Animation handling
    Private animatedImages As New List(Of Image)
    Private animationTimer As Timer

    ' Random number generator
    Private rand As New Random()

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

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

    ' Initialize walls and grid
    Private Sub InitializeWalls()
        ' Initialize verticalWalls jagged array
        verticalWalls = New Boolean(SQCOUNTX)() {}
        verticalWallItems = New PlacedItem(SQCOUNTX)() {}
        For x As Integer = 0 To SQCOUNTX
            verticalWalls(x) = New Boolean(SQCOUNTY - 1) {}
            verticalWallItems(x) = New PlacedItem(SQCOUNTY - 1) {}
            For y As Integer = 0 To SQCOUNTY - 1
                verticalWalls(x)(y) = True
                verticalWallItems(x)(y) = Nothing
            Next
        Next

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

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

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

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

    ' Initialize timers for highlight and animation
    Private Sub InitializeTimers()
        ' Highlight timer
        highlightTimer = New Timer() With {.Interval = 500}
        AddHandler highlightTimer.Tick, AddressOf HighlightTimer_Tick
        highlightTimer.Start()

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

    ' Initialize event handlers
    Private Sub InitializeEventHandlers()
        ' Add handlers for color selection
        AddColorPictureBoxHandlers(Me)

        ' Add handlers for item picture boxes
        AddHandler picBlock1.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picBaddy1.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picBaddy2.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picLazer1.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picLazer2.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picFlipper1.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picFlipper2.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picFlipper3.MouseDown, AddressOf PicBlock_MouseDown
        AddHandler picFlipper4.MouseDown, AddressOf PicBlock_MouseDown

        ' Add handlers for PicGame
        PicGame.AllowDrop = True
        AddHandler PicGame.DragEnter, AddressOf PicGame_DragEnter
        AddHandler PicGame.DragOver, AddressOf PicGame_DragOver
        AddHandler PicGame.DragDrop, AddressOf PicGame_DragDrop
        AddHandler PicGame.MouseDown, AddressOf PicGame_MouseDown
        AddHandler PicGame.MouseUp, AddressOf PicGame_MouseUp
        AddHandler PicGame.MouseMove, AddressOf PicGame_MouseMove
        AddHandler PicGame.Paint, AddressOf PicGame_Paint

        ' Add handlers for buttons
        AddHandler btnUndo.Click, AddressOf btnUndo_Click
        AddHandler btnRedo.Click, AddressOf btnRedo_Click
        AddHandler btnRandom.Click, AddressOf btnRandom_Click
        AddHandler btnSavePremade.Click, AddressOf btnSavePremade_Click
        AddHandler btnClear.Click, AddressOf btnClear_Click
        AddHandler btnFill.Click, AddressOf btnFill_Click

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

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

        ' Add handler for hatch styles list
        AddHandler lstHatchstyles.SelectedIndexChanged, AddressOf lstHatchstyles_SelectedIndexChanged
    End Sub

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

    ' Initialize animation for animated images
    Private Sub InitializeAnimationForImages()
        ' Add animated images
        animatedImages.Add(picBaddy1.Image)
        animatedImages.Add(picBaddy2.Image)
        animatedImages.Add(picLazer1.Image)
        animatedImages.Add(picLazer2.Image)
        animatedImages.Add(picFlipper1.Image)
        animatedImages.Add(picFlipper2.Image)
        animatedImages.Add(picFlipper3.Image)
        animatedImages.Add(picFlipper4.Image)

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

    ' Timer tick for animation
    Private Sub OnAnimationTick(sender As Object, e As EventArgs)
        For Each img In animatedImages
            ImageAnimator.UpdateFrames(img)
        Next
        PicGame.Invalidate()
    End Sub

    ' Empty method required by ImageAnimator.Animate
    Private Sub OnFrameChanged(o As Object, e As EventArgs)
        ' Required but can be left empty
    End Sub

    ' Event handler for highlighting timer
    Private Sub HighlightTimer_Tick(sender As Object, e As EventArgs)
        highlightColor = If(highlightColor = Color.Black, Color.White, Color.Black)
        PicGame.Invalidate()
    End Sub

    ' Event handler for form load
    Private Sub RoomView_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadPremades()
    End Sub

    ' Event handler for premades list selection change
    Private Sub lstPremades_SelectedIndexChanged(sender As Object, e As EventArgs)
        If lstPremades.SelectedIndex >= 0 Then
            If Not isRestoringState Then SaveStateForUndo()
            Dim selectedPremade As String = lstPremades.SelectedItem.ToString()
            LoadSettingsFromFile(Path.Combine(premadesFolder, selectedPremade & ".Premade"))
            PicGame.Invalidate()
        End If
    End Sub

    ' Load settings from a premade file
    Private Sub LoadSettingsFromFile(filePath As String)
        Try
            If Not File.Exists(filePath) Then Return
            Dim lines As String() = File.ReadAllLines(filePath)
            Dim section As String = ""
            For Each line As String In lines
                line = line.Trim()
                If String.IsNullOrEmpty(line) OrElse line.StartsWith(";") Then Continue For
                If line.StartsWith("[") AndAlso line.EndsWith("]") Then
                    section = line.Substring(1, line.Length - 2)
                Else
                    Dim parts() As String = line.Split(New Char() {":"c}, 2)
                    If parts.Length = 2 Then
                        LoadProperty(parts(0).Trim(), parts(1).Trim(), section)
                    End If
                End If
            Next
        Catch ex As Exception
            MessageBox.Show("Error loading settings: " & ex.Message)
        End Try
    End Sub

    ' Load individual property from settings file
    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

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

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

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

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

    ' New menu item click event
    Private Sub mnuNew_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()
        wallsHatchStyle = HatchStyle.Cross
        wallsColor1 = Color.Gray
        wallsColor2 = Color.DarkGray
        floorsHatchStyle = HatchStyle.SmallCheckerBoard
        floorsColor1 = Color.LightGray
        floorsColor2 = Color.White
        InitializeWalls()
        currentFilePath = ""
        PicGame.Invalidate()
    End Sub

    ' Save menu item click event
    Private Sub mnuSave_Click(sender As Object, e As EventArgs)
        If String.IsNullOrEmpty(currentFilePath) Then
            mnuSaveAs_Click(sender, e)
        Else
            SaveRoomToFile(currentFilePath)
        End If
    End Sub

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

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

    Private Sub SaveRoomToFile(filePath As String)
        Try
            Dim roomState As New RoomState(Me)

            ' If you still need to include fields and indentation in serialization:
            Dim options As New JsonSerializerOptions With {
            .WriteIndented = True,
            .IncludeFields = True
        }

            ' Remove these lines:
            ' options.Converters.Add(New ColorJsonConverter())
            ' options.Converters.Add(New HatchStyleJsonConverter())

            ' Serialize the roomState
            Dim jsonString As String = JsonSerializer.Serialize(roomState, options)
            File.WriteAllText(filePath, jsonString)
        Catch ex As Exception
            MessageBox.Show("Error saving room: " & ex.Message)
        End Try
    End Sub
    Private Sub LoadRoomFromFile(filePath As String)
        Try
            If Not File.Exists(filePath) Then Return

            ' If you still need to include fields in deserialization:
            Dim options As New JsonSerializerOptions With {
            .IncludeFields = True
        }

            ' Remove these lines:
            ' options.Converters.Add(New ColorJsonConverter())
            ' options.Converters.Add(New HatchStyleJsonConverter())

            Dim jsonString As String = File.ReadAllText(filePath)
            Dim roomState As RoomState = JsonSerializer.Deserialize(Of RoomState)(jsonString, options)
            roomState.RestoreState(Me)
            PicGame.Invalidate()
        Catch ex As Exception
            MessageBox.Show("Error loading room: " & ex.Message)
        End Try
    End Sub    ' Undo button click event
    Private Sub btnUndo_Click(sender As Object, e As EventArgs)
        If undoStack.Count > 1 Then
            SaveStateForRedo()
            undoStack.Pop()
            isRestoringState = True
            Dim previousState As RoomState = undoStack.Peek()
            previousState.RestoreState(Me)
            isRestoringState = False
            PicGame.Invalidate()
        End If
    End Sub

    ' Redo button click event
    Private Sub btnRedo_Click(sender As Object, e As EventArgs)
        If redoStack.Count > 0 Then
            SaveStateForUndo()
            isRestoringState = True
            Dim redoState As RoomState = redoStack.Pop()
            redoState.RestoreState(Me)
            isRestoringState = False
            PicGame.Invalidate()
        End If
    End Sub

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

    ' Save state for redo functionality
    Private Sub SaveStateForRedo()
        If isRestoringState Then Return
        redoStack.Push(New RoomState(Me))
    End Sub

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

    ' Color picture box click event
    Private Sub ColorPictureBox_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()

        Dim pb As PictureBox = CType(sender, PictureBox)
        Dim selectedColor As Color = pb.BackColor

        ' Determine which color to update based on the picture box name
        If pb.Name.StartsWith("col") Then
            Dim numStr As String = pb.Name.Substring(3)
            Dim num As Integer
            If Integer.TryParse(numStr, num) Then
                If num >= 1 AndAlso num <= 48 Then
                    ' Update Color1
                    If rdoFloors.Checked Then
                        floorsColor1 = selectedColor
                    ElseIf rdoWalls.Checked Then
                        wallsColor1 = selectedColor
                    End If
                ElseIf num >= 51 AndAlso num <= 98 Then
                    ' Update Color2
                    If rdoFloors.Checked Then
                        floorsColor2 = selectedColor
                    ElseIf rdoWalls.Checked Then
                        wallsColor2 = selectedColor
                    End If
                End If
            End If
        End If

        PicGame.Invalidate()
    End Sub

    ' Hatch styles list selection change event
    Private Sub lstHatchstyles_SelectedIndexChanged(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()
        Dim selectedHatchStyleName As String = lstHatchstyles.SelectedItem.ToString()
        Dim selectedHatchStyle As HatchStyle = CType([Enum].Parse(GetType(HatchStyle), selectedHatchStyleName), HatchStyle)
        If rdoFloors.Checked Then
            floorsHatchStyle = selectedHatchStyle
        ElseIf rdoWalls.Checked Then
            wallsHatchStyle = selectedHatchStyle
        End If
        PicGame.Invalidate()
    End Sub

    ' Randomize colors and styles
    Private Sub btnRandom_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()
        floorsColor1 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
        floorsColor2 = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
        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))
        floorsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)
        wallsHatchStyle = CType(hatchStyles.GetValue(rand.Next(hatchStyles.Length)), HatchStyle)
        PicGame.Invalidate()
    End Sub

    ' PicGame paint event
    Private Sub PicGame_Paint(sender As Object, e As PaintEventArgs)
        CalculateSquareSizes(PicGame.ClientSize.Width, PicGame.ClientSize.Height)
        e.Graphics.SmoothingMode = SmoothingMode.None
        DrawGrid(e.Graphics)
        DrawHighlight(e.Graphics)
    End Sub

    ' Calculate square sizes based on client size
    Private Sub CalculateSquareSizes(clientWidth As Integer, clientHeight As Integer)
        SQSizeX = (clientWidth - 2 * INDENT - (SQCOUNTX + 1) * WALL_THICKNESS) \ SQCOUNTX
        SQSizeY = (clientHeight - 2 * INDENT - (SQCOUNTY + 1) * WALL_THICKNESS) \ SQCOUNTY
    End Sub

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ' Get image by key
    Private Function GetImageByKey(imageKey As String) As Image
        Select Case imageKey
            Case "picBlock1"
                Return picBlock1.Image
            Case "picBaddy1"
                Return picBaddy1.Image
            Case "picBaddy2"
                Return picBaddy2.Image
            Case "picLazer1"
                Return picLazer1.Image
            Case "picLazer2"
                Return picLazer2.Image
            Case "picFlipper1"
                Return picFlipper1.Image
            Case "picFlipper2"
                Return picFlipper2.Image
            Case "picFlipper3"
                Return picFlipper3.Image
            Case "picFlipper4"
                Return picFlipper4.Image
            Case Else
                Return Nothing
        End Select
    End Function

    ' Handle mouse move events
    Private Sub PicGame_MouseMove(sender As Object, e As MouseEventArgs)
        UpdateHighlight(e.X, e.Y)
        PicGame.Invalidate()

        If isRightMouseDown Then
            RemoveObjectAtMousePosition(e.X, e.Y)
        ElseIf isLeftMouseDown Then
            AddWallAtMousePosition(e.X, e.Y)
        End If
    End Sub

    ' Update highlight variables based on mouse position
    Private Sub UpdateHighlight(mouseX As Integer, mouseY As Integer)
        mouseX -= INDENT
        mouseY -= INDENT
        isMouseInsideGrid = False

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

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

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

    ' PicGame drag enter event
    Private Sub PicGame_DragEnter(sender As Object, e As DragEventArgs)
        e.Effect = DragDropEffects.Copy
    End Sub

    ' PicGame drag over event
    Private Sub PicGame_DragOver(sender As Object, e As DragEventArgs)
        Dim clientPoint As Point = PicGame.PointToClient(New Point(e.X, e.Y))
        UpdateHighlight(clientPoint.X, clientPoint.Y)

        ' Determine if drop is valid
        e.Effect = If(IsValidDrop(e.Data, highlightedWallX, highlightedWallY), DragDropEffects.Copy, DragDropEffects.None)
        PicGame.Invalidate()
    End Sub

    ' Check if the drop is valid
    Private Function IsValidDrop(data As IDataObject, isWallX As Boolean, isWallY As Boolean) As Boolean
        If data.GetDataPresent(DataFormats.StringFormat) Then
            Dim imgKey As String = CStr(data.GetData(DataFormats.StringFormat))
            If IsFloorItem(imgKey) Then
                Return Not isWallX AndAlso Not isWallY
            ElseIf IsVerticalItem(imgKey) Then
                Return isWallX
            ElseIf IsHorizontalItem(imgKey) Then
                Return isWallY
            End If
        End If
        Return False
    End Function

    ' PicGame drag drop event
    Private Sub PicGame_DragDrop(sender As Object, e As DragEventArgs)
        If Not IsValidDrop(e.Data, highlightedWallX, highlightedWallY) Then Return

        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim imgKey As String = CStr(e.Data.GetData(DataFormats.StringFormat))
            If Not isRestoringState Then SaveStateForUndo()
            redoStack.Clear()

            If IsFloorItem(imgKey) Then
                PlaceItem(highlightedX, highlightedY, imgKey)
            ElseIf highlightedWallX Then
                ' Remove wall if any and place item
                verticalWalls(highlightedX)(highlightedY) = False
                PlaceWallItem(highlightedX, highlightedY, True, imgKey)
            ElseIf highlightedWallY Then
                ' Remove wall if any and place item
                horizontalWalls(highlightedX)(highlightedY) = False
                PlaceWallItem(highlightedX, highlightedY, False, imgKey)
            End If
            PicGame.Invalidate()
        End If
    End Sub

    ' Check if item is a floor item
    Private Function IsFloorItem(imgKey As String) As Boolean
        Return imgKey = "picBlock1" OrElse imgKey = "picBaddy1" OrElse imgKey = "picBaddy2"
    End Function

    ' Check if item is a vertical wall item
    Private Function IsVerticalItem(imgKey As String) As Boolean
        Return imgKey = "picLazer1" OrElse imgKey = "picFlipper1" OrElse imgKey = "picFlipper2"
    End Function

    ' Check if item is a horizontal wall item
    Private Function IsHorizontalItem(imgKey As String) As Boolean
        Return imgKey = "picLazer2" OrElse imgKey = "picFlipper3" OrElse imgKey = "picFlipper4"
    End Function

    ' Place item on floor
    Private Sub PlaceItem(x As Integer, y As Integer, imgKey As String)
        placedItems(x)(y) = New PlacedItem() With {.ImageKey = imgKey}
    End Sub

    ' Place item on wall
    Private Sub PlaceWallItem(x As Integer, y As Integer, isVertical As Boolean, imgKey As String)
        If isVertical Then
            verticalWallItems(x)(y) = New PlacedItem With {.ImageKey = imgKey}
        Else
            horizontalWallItems(x)(y) = New PlacedItem With {.ImageKey = imgKey}
        End If
    End Sub

    ' Picture box mouse down event for dragging items
    Private Sub PicBlock_MouseDown(sender As Object, e As MouseEventArgs)
        Dim pic As PictureBox = CType(sender, PictureBox)
        pic.DoDragDrop(pic.Name, DragDropEffects.Copy)
    End Sub

    ' PicGame mouse down event
    Private Sub PicGame_MouseDown(sender As Object, e As MouseEventArgs)
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = True

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

            RemoveObjectAtMousePosition(e.X, e.Y)
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = True

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

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

    ' PicGame mouse up event
    Private Sub PicGame_MouseUp(sender As Object, e As MouseEventArgs)
        If e.Button = MouseButtons.Right Then
            isRightMouseDown = False
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = False
        End If

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

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

    ' Remove object at mouse position
    Private Sub RemoveObjectAtMousePosition(mouseX As Integer, mouseY As Integer)
        UpdateHighlight(mouseX, mouseY)

        If highlightedWallX Then
            If verticalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                verticalWallItems(highlightedX)(highlightedY) = Nothing
                PicGame.Invalidate()
            ElseIf verticalWalls(highlightedX)(highlightedY) AndAlso highlightedX > 0 AndAlso highlightedX < SQCOUNTX Then
                ' Remove the wall (ensure it's not a border wall)
                verticalWalls(highlightedX)(highlightedY) = False
                PicGame.Invalidate()
            End If
        ElseIf highlightedWallY Then
            If horizontalWallItems(highlightedX)(highlightedY) IsNot Nothing Then
                horizontalWallItems(highlightedX)(highlightedY) = Nothing
                PicGame.Invalidate()
            ElseIf horizontalWalls(highlightedX)(highlightedY) AndAlso highlightedY > 0 AndAlso highlightedY < SQCOUNTY Then
                ' Remove the wall (ensure it's not a border wall)
                horizontalWalls(highlightedX)(highlightedY) = False
                PicGame.Invalidate()
            End If
        Else
            If placedItems(highlightedX)(highlightedY) IsNot Nothing Then
                placedItems(highlightedX)(highlightedY) = Nothing
                PicGame.Invalidate()
            End If
        End If
    End Sub

    ' Clear all walls except borders
    Private Sub btnClear_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()

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

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

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

    ' Fill all walls except where wall items are present
    Private Sub btnFill_Click(sender As Object, e As EventArgs)
        If Not isRestoringState Then SaveStateForUndo()

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

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

        redoStack.Clear()
        PicGame.Invalidate()
    End Sub
End Class

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

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

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

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

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

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

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

        ' Save placed items
        placedItems = CopyPlacedItems(roomView.placedItems)
        verticalWallItems = CopyPlacedItems(roomView.verticalWallItems)
        horizontalWallItems = CopyPlacedItems(roomView.horizontalWallItems)
    End Sub

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

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

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

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

        ' Restore placed items
        roomView.placedItems = RestorePlacedItems(placedItems)
        roomView.verticalWallItems = RestorePlacedItems(verticalWallItems)
        roomView.horizontalWallItems = RestorePlacedItems(horizontalWallItems)
    End Sub

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

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

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

I have added (IN DESIGNER) the following controls. 
picBaddy3
picBaddy4
picCoin
picGate
picKey
picStart
picFinish

can you update the code so all these items can be dragged and dropped onto the grid. They are ALL Floor items.