Option Explicit On
Option Strict On

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

Public Class RoomData
    Public Property GridSizeX As Integer
    Public Property GridSizeY As Integer

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

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

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

    ' Arrays to save floor edge styles
    Public Property floorsUseEdgeStyle As Boolean()()
    ' Property to save the selected music
    Public Property SelectedMusic As String

    ' Property to save the level name
    Public Property LevelName As String

    ' Parameterless constructor for deserialization
    Public Sub New()
    End Sub

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

        ' Save grid size
        GridSizeX = roomView.SQCOUNTX
        GridSizeY = roomView.SQCOUNTY

        ' 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)

        ' Save floorsUseEdgeStyle
        floorsUseEdgeStyle = CloneJaggedArray(roomView.floorsUseEdgeStyle)

        ' Save the selected music
        SelectedMusic = roomView.SelectedMusic

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

    ' Helper method to clone jagged arrays
    Private Function CloneJaggedArray(Of T)(sourceArray As T()()) As T()()
        If sourceArray Is Nothing Then Return Nothing
        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)
            Else
                newArray(i) = Nothing
            End If
        Next
        Return newArray
    End Function

    ' Helper method to copy placed items
    Private Function CopyPlacedItems(items As PlacedItem()()) As PlacedItemData()()
        If items Is Nothing Then Return Nothing
        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}
                    Else
                        copiedItems(x)(y) = Nothing
                    End If
                Next
            Else
                copiedItems(x) = Nothing
            End If
        Next
        Return copiedItems
    End Function

    ' Restore the saved state back into the frmDesigner
    Public Sub RestoreState(roomView As frmDesigner)
        ' Restore the grid size
        Dim oldSQCOUNTX As Integer = roomView.SQCOUNTX
        Dim oldSQCOUNTY As Integer = roomView.SQCOUNTY
        roomView.SQCOUNTX = GridSizeX
        roomView.SQCOUNTY = GridSizeY

        ' Resize arrays to match the saved grid size and preserve data
        roomView.ResizeArrays(oldSQCOUNTX, oldSQCOUNTY, roomView.SQCOUNTX, roomView.SQCOUNTY)

        ' Restore colors and styles from stored values
        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)

        ' Restore floorsUseEdgeStyle
        roomView.floorsUseEdgeStyle = CloneJaggedArray(floorsUseEdgeStyle)

        ' Restore the selected music
        roomView.SelectedMusic = SelectedMusic

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

        ' Update grid size-related UI elements
        roomView.UpdateGridSizeUI()

        ' Invalidate the drawing area to redraw the grid
        roomView.picGame.Invalidate()
    End Sub

    ' Helper method to restore placed items
    Private Function RestorePlacedItems(data As PlacedItemData()()) As PlacedItem()()
        If data Is Nothing Then Return Nothing
        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}
                    Else
                        restoredItems(x)(y) = Nothing
                    End If
                Next
            Else
                restoredItems(x) = Nothing
            End If
        Next
        Return restoredItems
    End Function
End Class

' PlacedItemData class used for serialization/deserialization
Public Class PlacedItemData
    Public Property ImageKey As String
End Class

' PlacedItem class represents an item placed on the grid
Public Class PlacedItem
    Public Property ImageKey As String
End Class

Public Class SimpleRoomData
    ' Existing properties...
    Public WallsHatchStyleValue As Integer
    Public WallsColor1Argb As Integer
    Public WallsColor2Argb As Integer
    Public FloorsHatchStyleValue As Integer
    Public FloorsColor1Argb As Integer
    Public FloorsColor2Argb As Integer
    Public VerticalWalls()() As Boolean
    Public HorizontalWalls()() As Boolean
    Public placedItems()() As PlacedItemData
    Public verticalWallItems()() As PlacedItemData
    Public horizontalWallItems()() As PlacedItemData
    Public FloorsUseEdgeStyle()() As Boolean

    ' Add these new properties:
    Public LevelName As String
    Public SelectedMusic As String
End Class

Option Explicit On
Option Strict On

Imports System.IO
Imports System.Text.Json
Imports System.Text.Json.Serialization
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Media
Imports System.ComponentModel
Imports NAudio.Wave
Imports NAudio.Wave.SampleProviders

Public Class frmGame

#Region " Variables "
    Private Const BallMoveTimerInterval As Integer = 100 ' milliseconds (0.1 seconds)
    Private ballMoveTimer As Timer
    Private movingBalls As New List(Of MovingBall)()
    Private CollectedCoins As Integer = 0
    Private playerX As Integer
    Private playerY As Integer
    Private hasKey As Boolean = False ' To track if the player has collected a key
    Private justWarped As Boolean = False ' To prevent immediate re-warping
    Public Property RoomFilePath As String
    Private SelectedMusic As String

    ' Constants for grid layout
    Private Const INDENT As Integer = 20
    Private Const WALL_THICKNESS As Integer = 10

    ' Variables for grid size
    Private SQCOUNTX As Integer
    Private SQCOUNTY As Integer

    ' Variables for square sizes
    Private SQSizeX As Integer
    Private SQSizeY As Integer

    ' Variables for wall and floor styles
    Private wallsHatchStyle As HatchStyle
    Private wallsColor1 As Color
    Private wallsColor2 As Color
    Private floorsHatchStyle As HatchStyle
    Private floorsColor1 As Color
    Private floorsColor2 As Color

    ' Arrays to store walls and items
    Private verticalWalls()() As Boolean
    Private horizontalWalls()() As Boolean
    Private placedItems()() As String
    Private verticalWallItems()() As String
    Private horizontalWallItems()() As String
    Private floorsUseEdgeStyle()() As Boolean

    ' Image resources
    Private imageDictionary As New Dictionary(Of String, Image)
    Private animatedImages As New List(Of Image)

    ' Animation timer
    Private animationTimer As Timer

    ' Flag to indicate when the room data is loaded
    Private isRoomDataLoaded As Boolean = False

    Private musicPlayer As SoundPlayer

    ' Paths
    Private ReadOnly projectDir As String = GetProjectDirectory()
    Private ReadOnly ImagesFolder As String = Path.Combine(projectDir, "Data", "Images")
    Private ReadOnly musicFolder As String = Path.Combine(projectDir, "Data", "Music")
    Private ReadOnly SFXFolder As String = Path.Combine(projectDir, "Data", "Sound Effects")
    ' Sound effect files
    Private soundEffectFiles As New Dictionary(Of String, String)
    ' NAudio mixer
    Private mixer As MixingSampleProvider
    Private waveOut As WaveOutEvent

    ' Class to represent a moving ball
    Private Class MovingBall
        Public Property X As Integer
        Public Property Y As Integer
        Public Property DeltaX As Integer
        Public Property DeltaY As Integer
    End Class

    ' Class to represent the simple room data
    Public Class SimpleRoomData
        Public Property GridSizeX As Integer
        Public Property GridSizeY As Integer

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

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

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

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

        ' Property to save the selected music
        Public Property SelectedMusic As String

        ' Property to save the level name
        Public Property LevelName As String
    End Class
#End Region

#Region " Movement "
    Private Sub FindPlayerStartPosition()
        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
                    Dim imageKey As String = placedItems(x)(y)
                    If imageKey = "picStart" Then
                        playerX = x
                        playerY = y
                        ' Remove the "picStart" from placedItems
                        placedItems(x)(y) = Nothing
                        Exit Sub
                    End If
                Next
            End If
        Next
    End Sub

    Private Sub frmGame_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
        ' Handle movement keys
        Dim moved As Boolean = False
        Select Case e.KeyCode
            Case Keys.Up, Keys.P
                moved = AttemptMove("Up")
            Case Keys.Down, Keys.L
                moved = AttemptMove("Down")
            Case Keys.Left, Keys.Z
                moved = AttemptMove("Left")
            Case Keys.Right, Keys.X
                moved = AttemptMove("Right")
        End Select
        If moved Then
            picTest.Invalidate()
        End If
    End Sub

    Private Function AttemptMove(direction As String) As Boolean
        Dim deltaX As Integer = 0
        Dim deltaY As Integer = 0
        Select Case direction
            Case "Up"
                deltaY = -1
            Case "Down"
                deltaY = 1
            Case "Left"
                deltaX = -1
            Case "Right"
                deltaX = 1
        End Select

        Dim newX As Integer = playerX + deltaX
        Dim newY As Integer = playerY + deltaY

        ' Check boundaries
        If newX < 0 OrElse newX >= SQCOUNTX OrElse newY < 0 OrElse newY >= SQCOUNTY Then
            Return False
        End If

        ' Check walls between current and new position
        If Not CanMoveTo(newX, newY, deltaX, deltaY) Then
            Return False
        End If

        ' Check what's at the new position
        Dim targetItem As String = placedItems(newX)(newY)

        If String.IsNullOrEmpty(targetItem) Then
            ' The cell is empty, move the player
            playerX = newX
            playerY = newY
            ' Play move sound
            PlaySoundEffect("Move")
            ' After moving, check for warp
            CheckForWarp()
            Return True
        ElseIf targetItem = "picBlock1" Then
            ' There's a block, attempt to push it
            Dim blockMoved As Boolean = AttemptPushBlock(newX, newY, deltaX, deltaY)
            If blockMoved Then
                ' Move the player
                playerX = newX
                playerY = newY
                ' Play move sound
                PlaySoundEffect("Move")
                ' After moving, check for warp
                CheckForWarp()
                Return True
            Else
                Return False
            End If
        ElseIf targetItem = "picBlockStopper1" Then
            ' Player can move onto block stopper, which remains in place
            playerX = newX
            playerY = newY
            ' Play move sound
            PlaySoundEffect("Move")
            ' After moving, check for warp
            CheckForWarp()
            Return True
        ElseIf targetItem = "picBall" Then
            ' Attempt to push the ball
            Dim ballMoved As Boolean = AttemptPushBall(newX, newY, deltaX, deltaY)
            If ballMoved Then
                ' Move the player into the ball's original position
                playerX = newX
                playerY = newY
                ' Play move sound
                PlaySoundEffect("Move")
                ' After moving, check for warp
                CheckForWarp()
                Return True
            Else
                Return False
            End If
        ElseIf targetItem = "picCoin" Then
            ' Collect the coin
            CollectedCoins += 1
            placedItems(newX)(newY) = Nothing
            playerX = newX
            playerY = newY
            ' Play coin sound
            PlaySoundEffect("Coin")
            ' Play move sound
            PlaySoundEffect("Move")
            ' After moving, check for warp
            CheckForWarp()
            Return True
        ElseIf targetItem = "picKey" Then
            ' Collect the key
            hasKey = True
            placedItems(newX)(newY) = Nothing
            playerX = newX
            playerY = newY
            ' Play key pickup sound
            PlaySoundEffect("Key") ' Assuming you have a 'Key.wav' sound effect
            ' Play move sound
            PlaySoundEffect("Move")
            ' After moving, check for warp
            CheckForWarp()
            Return True
        ElseIf targetItem = "picGate" Then
            If hasKey Then
                ' Remove the gate and allow player to move
                placedItems(newX)(newY) = Nothing
                playerX = newX
                playerY = newY
                ' Play door open sound (assuming you have 'Door Open.wav')
                PlaySoundEffect("Door Open")
                ' Play move sound
                PlaySoundEffect("Move")
                ' Optionally, reset hasKey if the key is one-time use
                ' hasKey = False
                ' After moving, check for warp
                CheckForWarp()
                Return True
            Else
                ' Cannot move onto the gate without the key
                Return False
            End If
        ElseIf targetItem = "picFinish" Then
            ' Player reaches finish point
            playerX = newX
            playerY = newY
            ' Play move sound
            PlaySoundEffect("Move")
            GameWon()
            Return True
        ElseIf targetItem = "picWarp" Then
            ' Move the player onto the warp tile first
            playerX = newX
            playerY = newY
            ' Play move sound
            PlaySoundEffect("Move")
            ' Handle warp
            HandleWarp()
            Return True
        Else
            ' Other items, prevent movement for now
            Return False
        End If
    End Function

    Private Sub CheckForWarp()
        If placedItems(playerX)(playerY) = "picWarp" AndAlso Not justWarped Then
            HandleWarp()
        ElseIf placedItems(playerX)(playerY) <> "picWarp" Then
            justWarped = False
        End If
    End Sub

    Private Sub HandleWarp()
        If Not justWarped Then
            ' Find the other warp tile
            Dim warpPositions As New List(Of Point)
            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) = "picWarp" Then
                            warpPositions.Add(New Point(x, y))
                        End If
                    Next
                End If
            Next
            ' Remove the current warp position from the list
            warpPositions.Remove(New Point(playerX, playerY))
            If warpPositions.Count = 1 Then
                ' Set justWarped to True to prevent immediate re-warping
                justWarped = True
                ' Play warp sound
                PlaySoundEffect("Warp")
                ' Move player to the other warp position
                playerX = warpPositions(0).X
                playerY = warpPositions(0).Y
                ' After warping, check if the destination is also a warp (to prevent double warp)
                CheckForWarp()
            Else
                ' There are not exactly two warps. Handle error.
                ' MessageBox.Show("Warp error: Exactly two warp tiles are required.")
                ' You can comment out the error message if desired
            End If
        End If
    End Sub

    Private Function AttemptPushBall(ballX As Integer, ballY As Integer, deltaX As Integer, deltaY As Integer) As Boolean
        Dim nextX As Integer = ballX + deltaX
        Dim nextY As Integer = ballY + deltaY

        ' Check boundaries
        If nextX < 0 OrElse nextX >= SQCOUNTX OrElse nextY < 0 OrElse nextY >= SQCOUNTY Then
            Return False
        End If

        ' Check walls between ball's current position and new position
        If Not CanBallMoveTo(ballX, ballY, deltaX, deltaY) Then
            Return False
        End If

        ' Check what's at ball's new position
        Dim targetItem As String = placedItems(nextX)(nextY)
        ' Check if the player is at the next position
        Dim playerAtNextPos As Boolean = (nextX = playerX AndAlso nextY = playerY)

        ' Check if the immediate next cell is empty and not occupied by the player
        If String.IsNullOrEmpty(targetItem) AndAlso Not playerAtNextPos Then
            ' The ball can start moving
            ' Remove the ball from placedItems
            placedItems(ballX)(ballY) = Nothing

            ' Create a new MovingBall instance
            Dim newBall As New MovingBall With {
            .X = ballX,
            .Y = ballY,
            .DeltaX = deltaX,
            .DeltaY = deltaY
        }

            ' Add the ball to the movingBalls list
            movingBalls.Add(newBall)

            ' Start the ball movement timer if not already running
            If ballMoveTimer Is Nothing Then
                ballMoveTimer = New Timer()
                AddHandler ballMoveTimer.Tick, AddressOf OnBallMoveTimerTick
                ballMoveTimer.Interval = BallMoveTimerInterval
                ballMoveTimer.Start()
            End If

            ' Play Push Block.wav sound
            PlaySoundEffect("Push Block")

            Return True
        Else
            Return False
        End If
    End Function
    Private Sub OnBallMoveTimerTick(sender As Object, e As EventArgs)
        ' Create a copy of the movingBalls list to iterate over
        Dim ballsToMove = movingBalls.ToList()

        For Each ball In ballsToMove
            Dim currentBallX As Integer = ball.X
            Dim currentBallY As Integer = ball.Y
            Dim deltaX As Integer = ball.DeltaX
            Dim deltaY As Integer = ball.DeltaY

            Dim nextX As Integer = currentBallX + deltaX
            Dim nextY As Integer = currentBallY + deltaY
            Dim ballStopped As Boolean = False

            ' Check boundaries
            If nextX < 0 OrElse nextX >= SQCOUNTX OrElse nextY < 0 OrElse nextY >= SQCOUNTY Then
                ballStopped = True
            Else
                ' Check walls between current and next position
                If Not CanBallMoveTo(currentBallX, currentBallY, deltaX, deltaY) Then
                    ballStopped = True
                Else
                    ' Check what's at the next position
                    Dim targetItem As String = placedItems(nextX)(nextY)
                    ' Check if the player is at the next position
                    Dim playerAtNextPos As Boolean = (nextX = playerX AndAlso nextY = playerY)
                    ' Check if another moving ball is at the next position
                    Dim ballAtNextPos As Boolean = movingBalls.Any(Function(b) b.X = nextX AndAlso b.Y = nextY)

                    If String.IsNullOrEmpty(targetItem) AndAlso Not playerAtNextPos AndAlso Not ballAtNextPos Then
                        ' Ball can move to next position
                        ball.X = nextX
                        ball.Y = nextY
                    Else
                        ' Ball is blocked by an item (player or other item)
                        ballStopped = True
                    End If
                End If
            End If

            If ballStopped Then
                ' Place the ball at its current position (if it's not already there)
                placedItems(ball.X)(ball.Y) = "picBall"
                ' Remove the ball from the movingBalls list
                movingBalls.Remove(ball)
            End If
        Next

        ' If no more balls are moving, stop the timer
        If movingBalls.Count = 0 Then
            ballMoveTimer.Stop()
            ballMoveTimer.Dispose()
            ballMoveTimer = Nothing
        End If

        ' Invalidate the picture to redraw
        picTest.Invalidate()
    End Sub
    Private Function CanBallMoveTo(currentX As Integer, currentY As Integer, deltaX As Integer, deltaY As Integer) As Boolean
        ' Check walls between current and next position
        Dim wallExists As Boolean = False
        Dim laserExists As Boolean = False
        Dim laserX As Integer = -1
        Dim laserY As Integer = -1
        Dim laserType As String = ""

        If deltaX = 1 Then
            ' Moving right
            wallExists = verticalWalls(currentX + 1)(currentY)
            ' Check if there's a laser on the vertical wall
            If verticalWallItems(currentX + 1)(currentY) IsNot Nothing Then
                laserExists = True
                laserX = currentX + 1
                laserY = currentY
                laserType = verticalWallItems(laserX)(laserY)
            End If
        ElseIf deltaX = -1 Then
            ' Moving left
            wallExists = verticalWalls(currentX)(currentY)
            If verticalWallItems(currentX)(currentY) IsNot Nothing Then
                laserExists = True
                laserX = currentX
                laserY = currentY
                laserType = verticalWallItems(laserX)(laserY)
            End If
        ElseIf deltaY = 1 Then
            ' Moving down
            wallExists = horizontalWalls(currentX)(currentY + 1)
            If horizontalWallItems(currentX)(currentY + 1) IsNot Nothing Then
                laserExists = True
                laserX = currentX
                laserY = currentY + 1
                laserType = horizontalWallItems(laserX)(laserY)
            End If
        ElseIf deltaY = -1 Then
            ' Moving up
            wallExists = horizontalWalls(currentX)(currentY)
            If horizontalWallItems(currentX)(currentY) IsNot Nothing Then
                laserExists = True
                laserX = currentX
                laserY = currentY
                laserType = horizontalWallItems(laserX)(laserY)
            End If
        End If

        If wallExists Then
            Return False
        End If

        If laserExists Then
            ' Determine if the laser can be destroyed
            Dim laserCanBeDestroyed As Boolean = False

            If (deltaX <> 0 AndAlso laserType = "picLazer1") Then
                ' Ball moving left or right, can destroy Laser1
                laserCanBeDestroyed = True
            ElseIf (deltaY <> 0 AndAlso laserType = "picLazer2") Then
                ' Ball moving up or down, can destroy Laser2
                laserCanBeDestroyed = True
            End If

            If laserCanBeDestroyed Then
                ' Remove the laser
                If deltaX <> 0 Then
                    verticalWallItems(laserX)(laserY) = Nothing
                ElseIf deltaY <> 0 Then
                    horizontalWallItems(laserX)(laserY) = Nothing
                End If

                ' Play Break Lazer.wav sound
                PlaySoundEffect("Break Lazer")
            Else
                ' Cannot move through non-breakable laser
                Return False
            End If
        End If

        Return True
    End Function
    Private Function IsLaserOnVerticalWall(x As Integer, y As Integer) As Boolean
        If verticalWallItems(x)(y) IsNot Nothing Then
            Dim item As String = verticalWallItems(x)(y)
            Return item = "picLazer1" OrElse item = "picLazer2"
        End If
        Return False
    End Function

    Private Function IsLaserOnHorizontalWall(x As Integer, y As Integer) As Boolean
        If horizontalWallItems(x)(y) IsNot Nothing Then
            Dim item As String = horizontalWallItems(x)(y)
            Return item = "picLazer1" OrElse item = "picLazer2"
        End If
        Return False
    End Function

    Private Function AttemptPushBlock(blockX As Integer, blockY As Integer, deltaX As Integer, deltaY As Integer) As Boolean
        Dim blockNewX As Integer = blockX + deltaX
        Dim blockNewY As Integer = blockY + deltaY

        ' Check boundaries
        If blockNewX < 0 OrElse blockNewX >= SQCOUNTX OrElse blockNewY < 0 OrElse blockNewY >= SQCOUNTY Then
            Return False
        End If

        ' Check walls between block's current position and new position
        If Not CanBlockMoveTo(blockX, blockY, deltaX, deltaY) Then
            Return False
        End If

        ' Check what's at block's new position
        Dim blockTargetItem As String = placedItems(blockNewX)(blockNewY)

        If String.IsNullOrEmpty(blockTargetItem) Then
            ' The block can be moved

            ' Remove the laser from the wall, if any
            Dim laserBroken As Boolean = False
            If deltaX = 1 Then
                ' Moving right
                If IsLaserOnVerticalWall(blockX + 1, blockY) Then
                    verticalWallItems(blockX + 1)(blockY) = Nothing
                    laserBroken = True
                    ' Play Break Lazer.wav sound
                    PlaySoundEffect("Break Lazer")
                End If
            ElseIf deltaX = -1 Then
                ' Moving left
                If IsLaserOnVerticalWall(blockX, blockY) Then
                    verticalWallItems(blockX)(blockY) = Nothing
                    laserBroken = True
                    ' Play Break Lazer.wav sound
                    PlaySoundEffect("Break Lazer")
                End If
            ElseIf deltaY = 1 Then
                ' Moving down
                If IsLaserOnHorizontalWall(blockX, blockY + 1) Then
                    horizontalWallItems(blockX)(blockY + 1) = Nothing
                    laserBroken = True
                    ' Play Break Lazer.wav sound
                    PlaySoundEffect("Break Lazer")
                End If
            ElseIf deltaY = -1 Then
                ' Moving up
                If IsLaserOnHorizontalWall(blockX, blockY) Then
                    horizontalWallItems(blockX)(blockY) = Nothing
                    laserBroken = True
                    ' Play Break Lazer.wav sound
                    PlaySoundEffect("Break Lazer")
                End If
            End If

            ' Move the block
            placedItems(blockNewX)(blockNewY) = "picBlock1"
            placedItems(blockX)(blockY) = Nothing

            ' Play Push Block.wav sound
            PlaySoundEffect("Push Block")

            Return True
        Else
            ' Block cannot be moved
            Return False
        End If
    End Function

    Private Function CanBlockMoveTo(blockX As Integer, blockY As Integer, deltaX As Integer, deltaY As Integer) As Boolean
        Dim wallExists As Boolean = False
        Dim laserExists As Boolean = False

        If deltaX = 1 Then
            ' Pushing block right
            wallExists = verticalWalls(blockX + 1)(blockY)
            laserExists = IsLaserOnVerticalWall(blockX + 1, blockY)
        ElseIf deltaX = -1 Then
            ' Pushing block left
            wallExists = verticalWalls(blockX)(blockY)
            laserExists = IsLaserOnVerticalWall(blockX, blockY)
        ElseIf deltaY = 1 Then
            ' Pushing block down
            wallExists = horizontalWalls(blockX)(blockY + 1)
            laserExists = IsLaserOnHorizontalWall(blockX, blockY + 1)
        ElseIf deltaY = -1 Then
            ' Pushing block up
            wallExists = horizontalWalls(blockX)(blockY)
            laserExists = IsLaserOnHorizontalWall(blockX, blockY)
        End If

        If wallExists Then
            If laserExists Then
                ' Block can move through laser, laser will be destroyed
                Return True
            Else
                ' Solid wall, block cannot move
                Return False
            End If
        Else
            ' No wall, block can move
            Return True
        End If
    End Function

    Private Function IsLaser(x As Integer, y As Integer) As Boolean
        Dim item As String = placedItems(x)(y)
        If item IsNot Nothing Then
            Return item = "picLazer1" OrElse item = "picLazer2"
        End If
        Return False
    End Function

    Private Function CanMoveTo(newX As Integer, newY As Integer, deltaX As Integer, deltaY As Integer) As Boolean
        ' Existing wall checks...

        ' Check walls between current and new position
        If deltaX = 1 Then
            ' Moving right
            If verticalWalls(playerX + 1)(playerY) Then Return False
            ' Check for lasers on the vertical wall
            If IsLaserOnVerticalWall(playerX + 1, playerY) Then Return False
        ElseIf deltaX = -1 Then
            ' Moving left
            If verticalWalls(playerX)(playerY) Then Return False
            If IsLaserOnVerticalWall(playerX, playerY) Then Return False
        ElseIf deltaY = 1 Then
            ' Moving down
            If horizontalWalls(playerX)(playerY + 1) Then Return False
            If IsLaserOnHorizontalWall(playerX, playerY + 1) Then Return False
        ElseIf deltaY = -1 Then
            ' Moving up
            If horizontalWalls(playerX)(playerY) Then Return False
            If IsLaserOnHorizontalWall(playerX, playerY) Then Return False
        End If

        ' Check if the new position is occupied by a moving ball
        If movingBalls.Any(Function(b) b.X = newX AndAlso b.Y = newY) Then
            Return False
        End If

        Return True ' Move is allowed
    End Function
    Private Sub GameWon()
        ' Play Finish.wav sound
        PlaySoundEffect("Finish")

        MessageBox.Show($"Congratulations! You have completed the level.{Environment.NewLine}Coins Collected: {CollectedCoins}")
        ' Optionally, stop the game or reset the level
        ' For example, close the game form
        Me.Close()
    End Sub
#End Region

#Region " Load "
    ' Event handler for form load
    Private Sub frmGame_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Load the room data from file
        If Not String.IsNullOrEmpty(RoomFilePath) AndAlso File.Exists(RoomFilePath) Then
            LoadRoomFromFile(RoomFilePath)
        Else
            MessageBox.Show("Room file not found.")
            Me.Close()
            Return
        End If

        ' Initialize images and animations
        InitializeImages()

        ' Initialize sound effects
        InitializeSoundEffects()

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

        ' Force picTest to repaint
        picTest.Invalidate()

        Me.KeyPreview = True
        Me.Focus()

    End Sub

    Private Sub LoadRoomFromFile(filePath As String)
        Try
            Dim options As New JsonSerializerOptions With {
            .IncludeFields = True,
            .PropertyNameCaseInsensitive = True
        }
            Dim jsonString As String = File.ReadAllText(filePath)
            Dim roomData As RoomData = JsonSerializer.Deserialize(Of RoomData)(jsonString, options)

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

            ' Ensure images are loaded before restoring state
            InitializeImages()

            ' Restore room data
            RestoreRoomData(roomData)
        Catch ex As Exception
            MessageBox.Show("Error loading room: " & ex.ToString())
        End Try
    End Sub

    Private Sub RestoreRoomData(roomData As RoomData)
        Try
            ' Restore grid sizes
            SQCOUNTX = roomData.GridSizeX
            SQCOUNTY = roomData.GridSizeY

            ' Restore colors and styles
            wallsHatchStyle = CType(roomData.wallsHatchStyleValue, HatchStyle)
            wallsColor1 = Color.FromArgb(roomData.wallsColor1Argb)
            wallsColor2 = Color.FromArgb(roomData.wallsColor2Argb)
            floorsHatchStyle = CType(roomData.floorsHatchStyleValue, HatchStyle)
            floorsColor1 = Color.FromArgb(roomData.floorsColor1Argb)
            floorsColor2 = Color.FromArgb(roomData.floorsColor2Argb)

            ' Restore walls and items
            verticalWalls = roomData.verticalWalls
            horizontalWalls = roomData.horizontalWalls
            floorsUseEdgeStyle = roomData.floorsUseEdgeStyle

            ' Restore placed items
            placedItems = RestorePlacedItems(roomData.placedItems)
            verticalWallItems = RestorePlacedItems(roomData.verticalWallItems)
            horizontalWallItems = RestorePlacedItems(roomData.horizontalWallItems)

            ' Restore level name and selected music
            SelectedMusic = roomData.SelectedMusic
            lblName.Text = roomData.LevelName
            PlaySelectedMusic()

            ' Set flag indicating room data is loaded
            isRoomDataLoaded = True

            ' Refresh the game picture
            picTest.Invalidate()
        Catch ex As Exception
            MessageBox.Show("Error restoring room data: " & ex.ToString())
        End Try

        FindPlayerStartPosition()
    End Sub

    Private Function RestorePlacedItems(data As PlacedItemData()()) As String()()
        If data Is Nothing Then
            Return Nothing
        End If

        Dim lengthOuter As Integer = data.Length
        Dim restoredItems As String()() = New String(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 String(lengthInner - 1) {}
                For y As Integer = 0 To lengthInner - 1
                    If data(x)(y) IsNot Nothing Then
                        restoredItems(x)(y) = data(x)(y).ImageKey
                    Else
                        restoredItems(x)(y) = Nothing
                    End If
                Next
            Else
                restoredItems(x) = Nothing
            End If
        Next
        Return restoredItems
    End Function

    Private Sub InitializeVerticalWalls()
        verticalWalls = New Boolean(SQCOUNTX)() {}
        For x As Integer = 0 To SQCOUNTX
            verticalWalls(x) = New Boolean(SQCOUNTY - 1) {}
            For y As Integer = 0 To SQCOUNTY - 1
                verticalWalls(x)(y) = True ' Default to True for walls
            Next
        Next
    End Sub

    Private Sub InitializeHorizontalWalls()
        horizontalWalls = New Boolean(SQCOUNTX - 1)() {}
        For x As Integer = 0 To SQCOUNTX - 1
            horizontalWalls(x) = New Boolean(SQCOUNTY) {}
            For y As Integer = 0 To SQCOUNTY
                horizontalWalls(x)(y) = True ' Default to True for walls
            Next
        Next
    End Sub

    Private Sub InitializePlacedItems()
        placedItems = New String(SQCOUNTX - 1)() {}
        For x As Integer = 0 To SQCOUNTX - 1
            placedItems(x) = New String(SQCOUNTY - 1) {}
            For y As Integer = 0 To SQCOUNTY - 1
                placedItems(x)(y) = Nothing
            Next
        Next
    End Sub

    Private Sub InitializeVerticalWallItems()
        verticalWallItems = New String(SQCOUNTX)() {}
        For x As Integer = 0 To SQCOUNTX
            verticalWallItems(x) = New String(SQCOUNTY - 1) {}
            For y As Integer = 0 To SQCOUNTY - 1
                verticalWallItems(x)(y) = Nothing
            Next
        Next
    End Sub

    Private Sub InitializeHorizontalWallItems()
        horizontalWallItems = New String(SQCOUNTX - 1)() {}
        For x As Integer = 0 To SQCOUNTX - 1
            horizontalWallItems(x) = New String(SQCOUNTY) {}
            For y As Integer = 0 To SQCOUNTY
                horizontalWallItems(x)(y) = Nothing
            Next
        Next
    End Sub

    Private Sub InitializeFloorsUseEdgeStyle()
        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

    ' Method to initialize images and animations
    Private Sub InitializeImages()
        Try
            imageDictionary("picBlock1") = Image.FromFile(Path.Combine(ImagesFolder, "Block.png"))
            imageDictionary("picBaddy1") = Image.FromFile(Path.Combine(ImagesFolder, "Baddy 1 anim.gif"))
            imageDictionary("picBaddy2") = Image.FromFile(Path.Combine(ImagesFolder, "Baddy 2 anim.gif"))
            imageDictionary("picBaddy3") = Image.FromFile(Path.Combine(ImagesFolder, "Baddy3.gif"))
            imageDictionary("picBaddy4") = Image.FromFile(Path.Combine(ImagesFolder, "Baddy4.gif"))
            imageDictionary("picBall") = Image.FromFile(Path.Combine(ImagesFolder, "Ball.gif"))
            imageDictionary("picBlockSwitch") = Image.FromFile(Path.Combine(ImagesFolder, "BlockSwitch.png"))
            imageDictionary("picBomb") = Image.FromFile(Path.Combine(ImagesFolder, "Bomb.gif"))
            imageDictionary("picCoin") = Image.FromFile(Path.Combine(ImagesFolder, "Coin.gif"))
            imageDictionary("picFinish") = Image.FromFile(Path.Combine(ImagesFolder, "Finish.gif"))
            imageDictionary("picFlipper1") = Image.FromFile(Path.Combine(ImagesFolder, "Flipper1.gif"))
            imageDictionary("picFlipper2") = Image.FromFile(Path.Combine(ImagesFolder, "Flipper2.gif"))
            imageDictionary("picFlipper3") = Image.FromFile(Path.Combine(ImagesFolder, "Flipper3.gif"))
            imageDictionary("picFlipper4") = Image.FromFile(Path.Combine(ImagesFolder, "Flipper4.gif"))
            imageDictionary("picGate") = Image.FromFile(Path.Combine(ImagesFolder, "Gate.gif"))
            imageDictionary("picKey") = Image.FromFile(Path.Combine(ImagesFolder, "Key.gif"))
            imageDictionary("picLazer1") = Image.FromFile(Path.Combine(ImagesFolder, "Lazer1.gif"))
            imageDictionary("picLazer2") = Image.FromFile(Path.Combine(ImagesFolder, "Lazer2.gif"))
            imageDictionary("picWarp") = Image.FromFile(Path.Combine(ImagesFolder, "Warp.gif"))
            imageDictionary("picWarp2") = Image.FromFile(Path.Combine(ImagesFolder, "Warp2.gif"))
            imageDictionary("picStart") = Image.FromFile(Path.Combine(ImagesFolder, "Guy.gif"))
            imageDictionary("picBlockStopper1") = Image.FromFile(Path.Combine(ImagesFolder, "Debris1.png"))
            ' Add other images similarly...

            ' List of image keys that are animated
            Dim animatedImageKeys As String() = {
                "picBaddy1", "picBaddy2", "picBaddy3", "picBaddy4",
                "picLazer1", "picLazer2", "picFlipper1", "picFlipper2",
                "picFlipper3", "picFlipper4", "picCoin", "picGate",
                "picKey", "picFinish", "picBall", "picBomb",
                "picWarp", "picWarp2", "picStart"
            }

            ' Add images that require animation to the animatedImages list
            For Each imgKey In animatedImageKeys
                If imageDictionary.ContainsKey(imgKey) Then
                    Dim img As Image = imageDictionary(imgKey)
                    animatedImages.Add(img)
                    ImageAnimator.Animate(img, AddressOf OnFrameChanged)
                End If
            Next
        Catch ex As Exception
            MessageBox.Show("Error loading images: " & ex.Message)
        End Try
    End Sub

    ' Method to initialize sound effects
    Private Sub InitializeSoundEffects()
        Try
            soundEffectFiles("Break Lazer") = Path.Combine(SFXFolder, "Break Lazer.wav")
            soundEffectFiles("Coin") = Path.Combine(SFXFolder, "Coin.wav")
            soundEffectFiles("Finish") = Path.Combine(SFXFolder, "Finish.wav")
            soundEffectFiles("Move") = Path.Combine(SFXFolder, "Move.wav")
            soundEffectFiles("Push Block") = Path.Combine(SFXFolder, "Push Block.wav")
            soundEffectFiles("Warp") = Path.Combine(SFXFolder, "Warp.wav")
            ' Add sound effect for key pickup and door open
            soundEffectFiles("Key") = Path.Combine(SFXFolder, "Key.wav") ' Ensure Key.wav exists
            soundEffectFiles("Door Open") = Path.Combine(SFXFolder, "Door Open.wav") ' Ensure Door Open.wav exists

            ' Ensure all files exist
            For Each kvp In soundEffectFiles
                ' If Not File.Exists(kvp.Value) Then
                '     MessageBox.Show("Sound effect file not found: " & kvp.Value)
                ' End If
                ' Commented out error messages as per your request
            Next

            ' Initialize NAudio mixer for sound effects
            mixer = New MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2))
            mixer.ReadFully = True ' To prevent stream closing unexpectedly
            waveOut = New WaveOutEvent()
            waveOut.Init(mixer)
            waveOut.Play()
        Catch ex As Exception
            MessageBox.Show("Error initializing sound effects: " & ex.Message)
        End Try
    End Sub

    ' Helper method to play a sound effect using NAudio
    Private Sub PlaySoundEffect(soundKey As String)
        If soundEffectFiles.ContainsKey(soundKey) Then
            Dim soundFilePath As String = soundEffectFiles(soundKey)
            If File.Exists(soundFilePath) Then
                Try
                    Dim reader As New AudioFileReader(soundFilePath)
                    ' Adjust volume if necessary
                    reader.Volume = 1.0F
                    ' Explicitly cast to ISampleProvider to resolve overload
                    mixer.AddMixerInput(DirectCast(reader, ISampleProvider))
                Catch ex As Exception
                    ' MessageBox.Show("Error playing sound effect: " & soundKey & vbCrLf & ex.Message)
                    ' Commented out error messages as per your request
                End Try
            End If
        End If
    End Sub

    ' Helper method to get the project directory
    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
#End Region

#Region " Sound "
    Private Sub PlaySelectedMusic()
        If musicPlayer IsNot Nothing Then
            ' Stop any currently playing music
            musicPlayer.Stop()
            musicPlayer.Dispose()
            musicPlayer = Nothing
        End If

        If Not String.IsNullOrEmpty(SelectedMusic) Then
            Dim musicFilePath As String = Path.Combine(musicFolder, SelectedMusic & ".wav")
            If File.Exists(musicFilePath) Then
                Try
                    musicPlayer = New SoundPlayer(musicFilePath)
                    ' Set LoadCompleted event handler to play music on repeat
                    AddHandler musicPlayer.LoadCompleted, AddressOf MusicLoadCompleted
                    musicPlayer.LoadAsync()
                Catch ex As Exception
                    MessageBox.Show("Error playing music: " & ex.Message)
                End Try
            Else
                ' MessageBox.Show("Selected music file not found: " & SelectedMusic)
                ' Commented out error messages as per your request
            End If
        End If
    End Sub

    Private Sub MusicLoadCompleted(sender As Object, e As AsyncCompletedEventArgs)
        If musicPlayer IsNot Nothing Then
            musicPlayer.PlayLooping()
        End If
    End Sub
#End Region

#Region " Draw "
    ' Event handler for animation timer tick
    Private Sub OnAnimationTick(sender As Object, e As EventArgs)
        For Each img In animatedImages
            ImageAnimator.UpdateFrames(img)
        Next
        picTest.Invalidate()
    End Sub

    ' Event handler for image frame change (required by ImageAnimator)
    Private Sub OnFrameChanged(o As Object, e As EventArgs)
        ' Required but can be left empty
    End Sub

    Private Sub picTest_Paint(sender As Object, e As PaintEventArgs) Handles picTest.Paint
        If Not isRoomDataLoaded Then
            Return
        End If

        ' Fill the entire background with the walls' hatch style and colors
        Using backgroundBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
            e.Graphics.FillRectangle(backgroundBrush, picTest.ClientRectangle)
        End Using

        ' Calculate square sizes based on the current size of picTest
        CalculateSquareSizes(picTest.ClientSize.Width, picTest.ClientSize.Height)

        e.Graphics.SmoothingMode = SmoothingMode.None

        ' Draw the game grid, walls, and items
        DrawGrid(e.Graphics)
    End Sub

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

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

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

                If floorsUseEdgeStyle IsNot Nothing AndAlso floorsUseEdgeStyle.Length > x AndAlso
                   floorsUseEdgeStyle(x) IsNot Nothing AndAlso floorsUseEdgeStyle(x).Length > y AndAlso
                   floorsUseEdgeStyle(x)(y) Then

                    ' Draw with wall's hatch style and colors (edge style)
                    Using floorBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(floorBrush, floorRect)
                    End Using
                Else
                    ' Draw with floor's hatch style and colors
                    Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                        g.FillRectangle(floorBrush, floorRect)
                    End Using
                End If
            Next
        Next

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

        ' Draw vertical walls or floor
        For x As Integer = 0 To SQCOUNTX
            For y As Integer = 0 To SQCOUNTY - 1
                Dim wallRect As New Rectangle(
                    xOrigin + x * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                    yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                    WALL_THICKNESS,
                    SQSizeY
                )

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

                    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
                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 x >= 0 AndAlso x < horizontalWalls.Length AndAlso
                   horizontalWalls(x) IsNot Nothing AndAlso
                   y >= 0 AndAlso y < horizontalWalls(x).Length Then

                    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
                End If
            Next
        Next

        ' Draw items on the floor squares
        DrawItems(g)

        ' Draw items on the walls
        DrawWallItems(g)
    End Sub

    Private Sub DrawItems(g As Graphics)
        If placedItems Is Nothing Then Return

        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
                    Dim imageKey As String = placedItems(x)(y)
                    If Not String.IsNullOrEmpty(imageKey) AndAlso imageDictionary.ContainsKey(imageKey) Then
                        If imageKey = "picStart" Then
                            ' Skip drawing the player here
                            Continue For
                        End If
                        Dim img As Image = imageDictionary(imageKey)
                        Dim xPos As Integer = INDENT + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
                        Dim yPos As Integer = INDENT + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
                        Dim destRect As New Rectangle(xPos, yPos, SQSizeX, SQSizeY)

                        ' Draw the item
                        g.DrawImage(img, destRect)
                    End If
                Next
            End If
        Next

        ' Draw moving balls
        For Each ball In movingBalls
            Dim img As Image = imageDictionary("picBall")
            Dim xPos As Integer = INDENT + ball.X * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim yPos As Integer = INDENT + ball.Y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
            Dim destRect As New Rectangle(xPos, yPos, SQSizeX, SQSizeY)
            g.DrawImage(img, destRect)
        Next

        ' Now draw the player at its current position
        DrawPlayer(g)
    End Sub
    Private Sub DrawPlayer(g As Graphics)
        Dim img As Image = imageDictionary("picStart")
        Dim xPos As Integer = INDENT + playerX * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim yPos As Integer = INDENT + playerY * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim destRect As New Rectangle(xPos, yPos, SQSizeX, SQSizeY)

        ' Draw the player
        g.DrawImage(img, destRect)
    End Sub

    ' Method to draw items on walls
    Private Sub DrawWallItems(g As Graphics)
        ' Draw vertical wall items
        If verticalWallItems IsNot Nothing Then
            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
                        Dim imageKey As String = verticalWallItems(x)(y)
                        If Not String.IsNullOrEmpty(imageKey) AndAlso imageDictionary.ContainsKey(imageKey) Then
                            Dim img As Image = imageDictionary(imageKey)
                            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
                    Next
                End If
            Next
        End If

        ' Draw horizontal wall items
        If horizontalWallItems IsNot Nothing Then
            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
                        Dim imageKey As String = horizontalWallItems(x)(y)
                        If Not String.IsNullOrEmpty(imageKey) AndAlso imageDictionary.ContainsKey(imageKey) Then
                            Dim img As Image = imageDictionary(imageKey)
                            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
                    Next
                End If
            Next
        End If
    End Sub
#End Region

#Region " Closing "
    ' Ensure that the animation timer is disposed when the form closes
    Private Sub frmGame_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        If animationTimer IsNot Nothing Then
            animationTimer.Stop()
            animationTimer.Dispose()
            animationTimer = Nothing
        End If

        If musicPlayer IsNot Nothing Then
            musicPlayer.Stop()
            musicPlayer.Dispose()
            musicPlayer = Nothing
        End If

        If waveOut IsNot Nothing Then
            waveOut.Stop()
            waveOut.Dispose()
            waveOut = Nothing
        End If

        If mixer IsNot Nothing Then
            mixer = Nothing
        End If
    End Sub

    Private Sub btnClose_Click(sender As Object, e As EventArgs) Handles btnClose.Click
        Me.Close()
    End Sub
#End Region
End Class

Can you add the following to my code pretty please.

Flipper1 = Push it left: In this case the ball or block has entered into a square with a flipper on the right wall pushing left. So the block or ball will be pushed left.
Flipper2 = Push it right: In this case the ball or block has entered into a square with a flipper on the left wall pushing right. So the block or ball will be pushed right.
Flipper3 = Push it Up: In this case the ball or block has entered into a square with a flipper on the bottom wall pushing up. So the block or ball will be pushed up.
Flipper4 = Push it Down: In this case the ball or block has entered into a square with a flipper on the top wall pushing down. So the block or ball will be pushed down.

When a Block or Ball enters into a square that has a flipper. It will be pushed in that direction (as above). The Block or Ball will move a square at a time until it is stopped by (The Player, A Wall or Any Item) If a laser is in it's path the Laser will be destroyed and the Block or Ball will continue on it's way. The movement speed will be slightly faster than the ball maybe 50.

Remember a laser is a wall item and a flipper is a wall item

Laser1 will be destroyed if the ball or block is travelling left or right
Laser2 will be destroyed if the ball or block is travelling up or down