Imports System.Net.Sockets
Imports System.IO
Imports GPSConnector.Common
Imports System.Threading

Friend Class ProcessClientRequest
    Private ReadOnly _tcpClient As TcpClient
    Private ReadOnly _deviceTypeComponent As GPSDeviceTypeComponent
    Private _clientIp As String
    Private _clientImei As String
    Private ReadOnly _internalId As String
    Private stream As NetworkStream
    Private _lastAccess As DateTime = DateTime.Now
    Private ReadOnly _lockObject As New Object
    Public Sub New(client As TcpClient, deviceTypeComponent As GPSDeviceTypeComponent)
        _deviceTypeComponent = deviceTypeComponent
        _tcpClient = client
        _internalId = Guid.NewGuid().ToString()
    End Sub

    Public ReadOnly Property Id() As String
        Get
            Return Me._internalId
        End Get
    End Property

    Public Sub Process()

        Dim isHeartBeatMonitor As Boolean = False

        Try
            ' Buffer for reading data
            Dim bytes(2048) As Byte
            Dim data As String = String.Empty
            ' Get a stream object for reading and writing
            stream = _tcpClient.GetStream()
            Dim nBytes() As Byte

            Dim i As Integer
            Dim j As Integer


            Do
                ReDim bytes(2048)
                i = stream.Read(bytes, 0, bytes.Length)
                If i > 0 Then

                    If i = 1 AndAlso bytes(0) = 0 Then 'heart beat monitor. exit
                        isHeartBeatMonitor = True
                        Return
                    End If

                    SyncLock _lockObject
                        _lastAccess = DateTime.Now
                    End SyncLock

                    ReDim nBytes(i - 1)
                    Array.Copy(bytes, nBytes, i)

                    If _deviceTypeComponent.ByteData Then
                        ProcessChunk(nBytes)
                    Else
                        If _deviceTypeComponent.ValidDataFlagCharacter = EnValidDataFlagCharacter.Custom Then
                            data = Text.Encoding.ASCII.GetString(nBytes)
                            ProcessChunk(data)
                            data = String.Empty
                        Else
                            For j = 0 To i - 1
                                If _deviceTypeComponent.ValidDataFlagCharacter = EnValidDataFlagCharacter.ZeroByte AndAlso nBytes(j) = 0 Then
                                    If data.Length > 1 Then
                                        ProcessChunk(data)
                                    End If
                                    data = String.Empty
                                ElseIf _deviceTypeComponent.ValidDataFlagCharacter = EnValidDataFlagCharacter.CrLf AndAlso nBytes(j) = Byte.Parse(Chr(13)) Then
                                    Continue For
                                ElseIf _deviceTypeComponent.ValidDataFlagCharacter = EnValidDataFlagCharacter.CrLf AndAlso nBytes(j) = Byte.Parse(Chr(10)) Then
                                    ProcessChunk(data)
                                    data = String.Empty
                                Else
                                    data &= Text.Encoding.ASCII.GetString(nBytes, j, 1)
                                End If
                            Next

                        End If
                    End If
                Else
                    'test disconnection
                    ' Detect if client disconnected
                    Dim cClosed As Boolean = False
                    If _tcpClient.Client.Poll(0, SelectMode.SelectRead) Then
                        Dim buff As Byte() = {1}
                        If _tcpClient.Client.Receive(buff, SocketFlags.Peek) = 0 Then
                            cClosed = True
                        Else
                            cClosed = False
                        End If
                    Else
                        cClosed = True
                    End If
                    If cClosed OrElse Math.Abs(DateDiff(DateInterval.Second, DateTime.Now, _lastAccess)) >= 10 Then
                        Exit Do
                    End If
                End If
            Loop While True
        Catch ex As SocketException
        Catch ex As ObjectDisposedException
        Catch ex As IOException
        Catch ex As ThreadAbortException
        Catch ex As Exception
            _deviceTypeComponent.Logger.LogException(ex, "DeviceTypeComponent : " + _deviceTypeComponent.DeviceTypeName, False, False)
        Finally
            If Not isHeartBeatMonitor Then
               _deviceTypeComponent.Logger.LogInformation(String.Format("Device with IMEI {0}, IP {1} has disconnected", _clientImei, _clientIp), "DeviceTypeComponent : " + _deviceTypeComponent.DeviceTypeName, False, False)
                ShutDown()
                ' RaiseEvent Disconnected(Me)
            End If
        End Try

    End Sub
    Friend ReadOnly Property ClientIP() As String
        Get
            Return _clientIp
        End Get
    End Property

    Public ReadOnly Property ClientImei As String
        Get
            Return _clientImei
        End Get
    End Property

    Public ReadOnly Property TcpClient() As TcpClient
        Get
            Return _tcpClient
        End Get
    End Property
    Private Sub ProcessChunk(ByVal chunkdata As Byte())
        If chunkdata Is Nothing AndAlso chunkdata.Length = 0 Then
            Return
        End If
        Dim processResult As GPSProcessorResult = _deviceTypeComponent.GPSProcessor.ProcessData(chunkdata, _internalId)

        If Not processResult Is Nothing AndAlso processResult.Success Then
            FinalizeProcess(processResult)
            Return
        Else
            _deviceTypeComponent.Logger.LogException(New Exception("Associated Gps processor failed to process the data in byte array. "), _deviceTypeComponent.DeviceTypeName, True)
            Return
        End If


    End Sub
    Private Sub ProcessChunk(ByVal chunkdata As String)
        If String.IsNullOrEmpty(chunkdata) Then
            Return
        End If

        If _deviceTypeComponent.MinimalDataLength > 0 AndAlso chunkdata.Length < _deviceTypeComponent.MinimalDataLength Then
            _deviceTypeComponent.Logger.LogDebugInfo("Received message from a device is less than allowed. Received '" + chunkdata + "'", _deviceTypeComponent.DeviceTypeName)
            Return
        End If

        Dim processResult As GPSProcessorResult = _deviceTypeComponent.GPSProcessor.ProcessData(chunkdata, _internalId)

        If Not processResult Is Nothing AndAlso processResult.Success Then
            FinalizeProcess(processResult)
            Return
        Else
            _deviceTypeComponent.Logger.LogException(New Exception("Associated Gps processor failed to process the data. Received '" + chunkdata + "'"), _deviceTypeComponent.DeviceTypeName, True)
            Return
        End If
    End Sub
    Private Sub FinalizeProcess(processResult As GPSProcessorResult)

        Try
            If processResult.BackMessages.Count > 0 Then
                For Each msg In processResult.BackMessages
                   _tcpClient.Client.Send(msg)
                Next
            End If

        Catch ex As Exception

        End Try

        For Each gpsDeviceData As GPSDeviceData In processResult.GPSDeviceDataList
            If Not _deviceTypeComponent.GPSDeviceListDAO.IsDeviceRegistered(gpsDeviceData.Imei, _deviceTypeComponent.DeviceTypeName) Then
                _deviceTypeComponent.Logger.LogException(New Exception(String.Format("an unregistered device with IMEI {0} is trying to send data. the data is ignored", gpsDeviceData.Imei)), _deviceTypeComponent.ToString, True, False)
                Exit For
            End If


            gpsDeviceData.ReceivedDate = Format(DateTime.Now, "dd.MM.yy")
            gpsDeviceData.ReceivedTime = Format(DateTime.Now, "HH:mm:ss")
            gpsDeviceData.ExactReceivedDate = DateTime.Now


            If gpsDeviceData.Id = 0 Then
                _deviceTypeComponent.GPSDeviceListDAO.SetGPSDeviceDataId(gpsDeviceData, _deviceTypeComponent.DeviceTypeName)
            End If

            If String.IsNullOrEmpty(gpsDeviceData.Guid) Then
                gpsDeviceData.Guid = Guid.NewGuid().ToString()
            End If

            If String.IsNullOrEmpty(gpsDeviceData.IP) Then
                gpsDeviceData.IP = _deviceTypeComponent.GetClientIP(_tcpClient)
            End If

            If String.IsNullOrEmpty(_clientIp) Then
                _clientIp = gpsDeviceData.IP
            End If

            If String.IsNullOrEmpty(_clientImei) Then
                _clientImei = gpsDeviceData.Imei
            End If

            _deviceTypeComponent.DataPersistence.Persist(_deviceTypeComponent.DeviceTypeName, gpsDeviceData)

            _deviceTypeComponent.RaiseDataArrivalEvent(gpsDeviceData)
        Next
    End Sub
    Sub ShutDown()
        On Error Resume Next
        If Not stream Is Nothing Then stream.Close()
        If Not _tcpClient Is Nothing Then _tcpClient.Close()
        On Error GoTo 0
    End Sub

    Public ReadOnly Property LastAccess As DateTime
        Get
            SyncLock _lockObject
                Return _lastAccess
            End SyncLock
        End Get
    End Property

    Public Property Thread As Thread


End Class
