Imports GPSConnector.Common
Imports System.Collections.Concurrent

Public Class PicoTrackProcessor
    Implements IGPSProcessor
    Private ReadOnly messages As New ConcurrentDictionary(Of String, PicoTrackMessages)
    Private ReadOnly database As IDatabase
    Private ReadOnly logger As IGPSLogger
    Private Const BasicConfigurationAckMode As String = "ID11IMEI,,,,,,,xxxxxxx1,,,,,,,,,,,,,,,,,,,,,"
    Public Sub New(db As IDatabase, log As IGPSLogger)
        database = db
        logger = log
    End Sub

    Public Function ProcessData(rawData As String, uniqueKey As String) As GPSProcessorResult Implements IGPSProcessor.ProcessData
        Dim result As GPSProcessorResult = New GPSProcessorResult()
        Dim gpsDeviceData As GPSDeviceData = New GPSDeviceData()
        Dim picoTrackMessages As PicoTrackMessages
        Dim splitData As String()
        Dim msgID As Integer
        Dim msgCode As String
        Dim imei As String


        If rawData.Length < 10 OrElse rawData.IndexOf("|") >= 0 Then 'connection string or test data or heartbeat.
           result.Success = True
            Return result
        End If

        msgID = rawData.Substring(0, 2)
        msgCode = rawData.Substring(2, 2)
        imei = rawData.Substring(4, 6)

        For Each item In messages
            If item.Value.Imei = imei AndAlso item.Value.ProcessId <> uniqueKey Then
                Cleanup(item.Value.ProcessId)
            End If
        Next

        If Not messages.ContainsKey(uniqueKey) Then
            picoTrackMessages = New PicoTrackMessages With {.ProcessId = uniqueKey, .Imei = imei}
            messages.AddOrUpdate(uniqueKey, picoTrackMessages, Function(k, v) v)
        Else
            Try
                picoTrackMessages = messages(uniqueKey)
            Catch ex As Exception
                picoTrackMessages = New PicoTrackMessages With {.ProcessId = uniqueKey, .Imei = imei}
            End Try

        End If

      
        If Not picoTrackMessages.DeploymentLoaded Then
            picoTrackMessages.Deployments.AddRange(database.GetPicoTrackDeployments(imei))
            picoTrackMessages.DeploymentLoaded = True
        End If

        Dim pMessageCode = PicoTrackMessageCode.GetMessageCode(msgCode)

        Select Case pMessageCode.PicoTrackMessageCodeType
            Case PicoTrackMessageCodeType.EventData, PicoTrackMessageCodeType.LoggingData
                If pMessageCode.Code = msgCode OrElse pMessageCode.CodeAckReq = msgCode Then
                    splitData = rawData.Split(",")
                    If splitData.Length >= 19 Then
                        FillData(gpsDeviceData, splitData)
                        result.GPSDeviceDataList.Add(gpsDeviceData)
                        If pMessageCode.CodeAckReq = msgCode Then
                            Dim ackData As Byte() = MessageToByteArray(String.Format("{0:00}{1}{2}", msgID, pMessageCode.CodeAckResp, imei))
                            If picoTrackMessages.Deployments.Count > 0 Then
                                Dim userData As Byte() = MessageToByteArray(picoTrackMessages.Deployments(0).Input)
                                Dim allData As Byte()
                                ReDim allData(ackData.Length + userData.Length - 1)
                                Array.Copy(userData, allData, userData.Length)
                                Array.Copy(ackData, 0, allData, userData.Length, ackData.Length)
                                picoTrackMessages.Deployments(0).Sent = True
                                result.BackMessages.Add(allData)
                            Else
                                result.BackMessages.Add(ackData)
                            End If
                        Else
                            ' we need to change configuration to acknowledged modes if it is not set.
                            If String.IsNullOrEmpty(picoTrackMessages.BasicConfigurationAckModeHeader) Then
                                Dim ackMessageId As String = "01"
                                If picoTrackMessages.Deployments.Count > 0 Then
                                    ackMessageId = (picoTrackMessages.Deployments.Max(Of Integer)(Function(d) d.MgsId) + 1).ToString("00")
                                End If

                                result.BackMessages.Add(MessageToByteArray(BasicConfigurationAckMode.Replace("ID", ackMessageId).Replace("IMEI", imei)))
                                picoTrackMessages.BasicConfigurationAckModeHeader = String.Format("{0:00}11{1}", ackMessageId, imei)
                                picoTrackMessages.BasicConfigurationAckModeMsgId = ackMessageId
                            End If
                        End If
                        result.Success = True
                    Else
                        result.Success = False
                    End If
                Else
                    result.Success = True
                End If
            Case Else ' this acknowledge response.
                If pMessageCode.CodeAckResp = msgCode Then

                    Dim deployment As PicoTrackConfigDeployment

                    If Not String.IsNullOrEmpty(picoTrackMessages.BasicConfigurationAckModeHeader) AndAlso msgCode = 14 Then
                        picoTrackMessages.BasicConfigurationAckModeHeader = String.Empty
                    Else
                        If picoTrackMessages.Deployments.Count > 0 Then
                            deployment = picoTrackMessages.Deployments(0)
                            Dim expectedMessageCode = PicoTrackMessageCode.GetMessageCode(deployment.MsgCode)
                            If deployment.Sent AndAlso expectedMessageCode.CodeAckResp = msgCode Then
                                database.ChangeDeploymentState(deployment.Id, 2, rawData)
                                picoTrackMessages.Deployments.Remove(deployment)
                            End If
                        End If
                    End If
                    result.Success = True
                End If
        End Select
        Return result
    End Function

    Public Function ProcessData(ByVal rawData As Byte(), ByVal uniqueKey As String) As GPSProcessorResult Implements IGPSProcessor.ProcessData
        Throw New NotImplementedException()
    End Function

    Public Sub Cleanup(ByVal uniqueKey As String) Implements IGPSProcessor.Cleanup
        Try
            Dim value As PicoTrackMessages = Nothing
            If messages.ContainsKey(uniqueKey) Then
                If Not String.IsNullOrEmpty(messages(uniqueKey).Imei) Then
                    database.ResetDeploymentState(messages(uniqueKey).Imei)
                End If
                messages.TryRemove(uniqueKey, value)
            End If
        Catch ex As Exception
            logger.LogDebugInfo(ex.Message & vbCrLf & ex.StackTrace, "PicoTrackProcessor")
        End Try
    End Sub
    Private Sub FillData(gpsDeviceData As GPSDeviceData, splitdata As String())
        On Error Resume Next
        ' --- Header part (no data modification)
        gpsDeviceData.MessageId = splitdata(0).Substring(0, 2)
        gpsDeviceData.MessageCode = splitdata(0).Substring(2, 2)
        gpsDeviceData.Imei = splitdata(0).Substring(4, 6)
        ' --- Data part
        gpsDeviceData.LoggingCause = splitdata(0).Substring(10, 2)
        gpsDeviceData.LoggingTime = splitdata(1)
        gpsDeviceData.LoggingInfo = splitdata(2)
        gpsDeviceData.GPSTime = splitdata(3)
        gpsDeviceData.Longtitude = splitdata(4)
        gpsDeviceData.Latitude = splitdata(5)
        gpsDeviceData.FixType = splitdata(6)
        gpsDeviceData.Speed = splitdata(7)
        gpsDeviceData.Course = splitdata(8)
        gpsDeviceData.SatellitesViewed = splitdata(9)
        gpsDeviceData.HDOP = -1                                ' splitData(10)
        gpsDeviceData.VDOP = -1                                ' splitData(11)
        gpsDeviceData.Height = splitdata(12)
        gpsDeviceData.Mileage = splitdata(13)
        gpsDeviceData.MCC = -1                                 ' splitData(14)
        gpsDeviceData.DigIN = splitdata(15)
        gpsDeviceData.DigOUT = splitdata(16)
        gpsDeviceData.AnlgIN = splitdata(17)
        If (IsNumeric(splitdata(18))) Then
            gpsDeviceData.AnlgOUT = splitdata(18)
        Else
            gpsDeviceData.AnlgOUT = -1
        End If
        gpsDeviceData.PomAnlgIN = -1
        gpsDeviceData.PomAnlgIN = -1
        gpsDeviceData.MotionSensor = -1
        gpsDeviceData.GPSAccuracy = -1

        On Error GoTo 0
    End Sub

    Private Function MessageToByteArray(msg As String) As Byte()
        Dim data As Byte()
        Dim tempdata As Byte() = Text.Encoding.ASCII.GetBytes(msg)
        Dim countBytes As Byte() = BitConverter.GetBytes(msg.Length)
        ReDim data(msg.Length + 4)

        data(0) = countBytes(0)
        data(1) = countBytes(1)
        data(2) = countBytes(2)
        data(3) = countBytes(3)
        For i As Integer = 0 To tempData.length - 1
            data(4 + i) = tempdata(i)
        Next
        data(msg.Length + 4) = Convert.ToByte(0)
        Return data
    End Function

End Class
