Imports System.Net
Imports System.Net.Sockets
Imports System.Threading
Imports NAudio.Wave

Public Class Form1

    Private FrmO_listener As TcpListener
    Private FrmO_client As TcpClient
    Private FrmO_networkStream As NetworkStream
    Private FrmO_listenThread As Thread
    Private FrmO_receiveThread As Thread


    Private FrmO_waveIn As WaveInEvent
    Private FrmO_waveOut As WaveOutEvent
    Private FrmO_bufferedWaveProvider As BufferedWaveProvider


    Private FrmV_isConnected As Boolean = False
    Private FrmV_isListening As Boolean = False


    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Lbl_LocalIP.Text = "Your IP  : " & GetLocalIPAddress()

        SetupAudio()
    End Sub

    Private Sub SetupAudio()
        Try

            FrmO_waveIn = New WaveInEvent()
            FrmO_waveIn.WaveFormat = New WaveFormat(8000, 16, 1)
            AddHandler FrmO_waveIn.DataAvailable, AddressOf WaveIn_DataAvailable


            FrmO_waveOut = New WaveOutEvent()
            FrmO_bufferedWaveProvider = New BufferedWaveProvider(FrmO_waveIn.WaveFormat)
            FrmO_bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(2)
            FrmO_waveOut.Init(FrmO_bufferedWaveProvider)
        Catch ex As Exception
            MessageBox.Show("Error setting up audio: " & ex.Message & vbCrLf &
                          "Make sure you have a microphone connected.",
                          "Audio Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        End Try
    End Sub


    Private Sub btnListen_Click(sender As Object, e As EventArgs) Handles Btn_Listen.Click
        If Not FrmV_isListening Then
            Try
                Dim port As Integer = Integer.Parse(Txt_Port.Text)
                FrmO_listener = New TcpListener(IPAddress.Any, port)
                FrmO_listener.Start()

                FrmV_isListening = True
                Btn_Listen.Enabled = False
                Btn_Connect.Enabled = False
                UpdateStatus("Listening on port " & port & "...")

                FrmO_listenThread = New Thread(AddressOf AcceptConnection)
                FrmO_listenThread.IsBackground = True
                FrmO_listenThread.Start()
            Catch ex As Exception
                MessageBox.Show("Error starting listener: " & ex.Message,
                              "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End If
    End Sub


    Private Sub AcceptConnection()
        Try
            FrmO_client = FrmO_listener.AcceptTcpClient()
            FrmO_networkStream = FrmO_client.GetStream()

            Me.Invoke(Sub()
                          FrmV_isConnected = True
                          Btn_Disconnect.Enabled = True
                          UpdateStatus("Connected!")
                          StartAudio()
                      End Sub)

            FrmO_receiveThread = New Thread(AddressOf ReceiveAudio)
            FrmO_receiveThread.IsBackground = True
            FrmO_receiveThread.Start()
        Catch ex As Exception
            Me.Invoke(Sub()
                          MessageBox.Show("Error accepting connection: " & ex.Message,
                                        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                      End Sub)
        End Try
    End Sub


    Private Sub btnConnect_Click(sender As Object, e As EventArgs) Handles Btn_Connect.Click
        If Not FrmV_isConnected Then
            Try
                Dim ip As String = Txt_IP.Text
                Dim port As Integer = Integer.Parse(Txt_Port.Text)

                FrmO_client = New TcpClient()
                FrmO_client.Connect(ip, port)
                FrmO_networkStream = FrmO_client.GetStream()

                FrmV_isConnected = True
                Btn_Listen.Enabled = False
                Btn_Connect.Enabled = False
                Btn_Disconnect.Enabled = True
                UpdateStatus("Connected to " & ip & ":" & port)

                StartAudio()

                FrmO_receiveThread = New Thread(AddressOf ReceiveAudio)
                FrmO_receiveThread.IsBackground = True
                FrmO_receiveThread.Start()
            Catch ex As Exception
                MessageBox.Show("Error connecting: " & ex.Message,
                              "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End If
    End Sub


    Private Sub btnDisconnect_Click(sender As Object, e As EventArgs) Handles Btn_Disconnect.Click
        Disconnect()
    End Sub


    Private Sub StartAudio()
        Try
            If FrmO_waveIn IsNot Nothing Then
                FrmO_waveIn.StartRecording()
            End If
            If FrmO_waveOut IsNot Nothing Then
                FrmO_waveOut.Play()
            End If
        Catch ex As Exception
            MessageBox.Show("Error starting audio: " & ex.Message,
                          "Audio Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        End Try
    End Sub


    Private Sub WaveIn_DataAvailable(sender As Object, e As WaveInEventArgs)
        If FrmV_isConnected AndAlso FrmO_networkStream IsNot Nothing AndAlso FrmO_networkStream.CanWrite Then
            Try

                Dim lengthBytes As Byte() = BitConverter.GetBytes(e.BytesRecorded)
                FrmO_networkStream.Write(lengthBytes, 0, lengthBytes.Length)

                FrmO_networkStream.Write(e.Buffer, 0, e.BytesRecorded)
            Catch ex As Exception

                Me.Invoke(Sub() Disconnect())
            End Try
        End If
    End Sub

    Private Sub ReceiveAudio()
        Try
            Dim lengthBytes(3) As Byte
            While FrmV_isConnected AndAlso FrmO_client IsNot Nothing AndAlso FrmO_client.Connected

                Dim bytesRead As Integer = FrmO_networkStream.Read(lengthBytes, 0, 4)
                If bytesRead = 0 Then Exit While

                Dim dataLength As Integer = BitConverter.ToInt32(lengthBytes, 0)

                Dim audioBuffer(dataLength - 1) As Byte
                Dim totalRead As Integer = 0
                While totalRead < dataLength
                    bytesRead = FrmO_networkStream.Read(audioBuffer, totalRead, dataLength - totalRead)
                    If bytesRead = 0 Then Exit While
                    totalRead += bytesRead
                End While

                If totalRead = dataLength Then
                    FrmO_bufferedWaveProvider.AddSamples(audioBuffer, 0, dataLength)
                End If
            End While
        Catch ex As Exception

        Finally
            Me.Invoke(Sub() Disconnect())
        End Try
    End Sub

    Private Sub Disconnect()
        FrmV_isConnected = False
        FrmV_isListening = False

        If FrmO_waveIn IsNot Nothing Then
            Try
                FrmO_waveIn.StopRecording()
            Catch ex As Exception
            End Try
        End If
        If FrmO_waveOut IsNot Nothing Then
            Try
                FrmO_waveOut.Stop()
            Catch ex As Exception
            End Try
        End If

        If FrmO_networkStream IsNot Nothing Then
            Try
                FrmO_networkStream.Close()
            Catch ex As Exception
            End Try
            FrmO_networkStream = Nothing
        End If
        If FrmO_client IsNot Nothing Then
            Try
                FrmO_client.Close()
            Catch ex As Exception
            End Try
            FrmO_client = Nothing
        End If
        If FrmO_listener IsNot Nothing Then
            Try
                FrmO_listener.Stop()
            Catch ex As Exception
            End Try
            FrmO_listener = Nothing
        End If

        Btn_Listen.Enabled = True
        Btn_Connect.Enabled = True
        Btn_Disconnect.Enabled = False
        UpdateStatus("Disconnected")
    End Sub

    Private Sub UpdateStatus(status As String)
        Lbl_Status.Text = "Status: " & status
    End Sub

    Private Function GetLocalIPAddress() As String
        Try
            Dim host As IPHostEntry = Dns.GetHostEntry(Dns.GetHostName())
            For Each ip As IPAddress In host.AddressList
                If ip.AddressFamily = AddressFamily.InterNetwork Then
                    Return ip.ToString()
                End If
            Next
        Catch ex As Exception
            Return "Unable to get IP"
        End Try
        Return "127.0.0.1"
    End Function

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        Disconnect()
        If FrmO_waveIn IsNot Nothing Then
            Try
                FrmO_waveIn.Dispose()
            Catch ex As Exception
            End Try
        End If
        If FrmO_waveOut IsNot Nothing Then
            Try
                FrmO_waveOut.Dispose()
            Catch ex As Exception
            End Try
        End If
    End Sub
End Class
