Imports System.Configuration
Imports System.Globalization
Imports GPSConnector.Common
Imports System
Imports System.Threading
Imports System.IO
Imports System.Xml.Serialization
Imports System.Data.SqlClient

Public Class StoredDataToDBPersistence
    Implements ICustomService
    Private ReadOnly _connectionString As String
    Private ReadOnly _dataLocationPath As String
    Private _watcher As FileSystemWatcher
    Private ReadOnly _logger As IGPSLogger
    Private _processing As Boolean
    Private _fileQueue As Queue(Of String)
    Private _lastProcessFileResult As ProcessResult
    Private ReadOnly _dataArchiveLocationPath As String
    Public Sub New(ByVal dataLocationPath As String, dataArchiveLocationPath As String, connectionStringName As String, logger As IGPSLogger)
        _dataLocationPath = dataLocationPath
        _connectionString = ConfigurationManager.ConnectionStrings(connectionStringName).ConnectionString
        _logger = logger
        _dataArchiveLocationPath = dataArchiveLocationPath
    End Sub
    Public ReadOnly Property CanRestart() As Boolean Implements ICustomService.CanRestart
        Get
            Return True
        End Get
    End Property
    Public Function Restart() As Boolean Implements ICustomService.Restart
        Start()
        Return True
    End Function
    Public Property ServiceName As String Implements ICustomService.ServiceName

    Public Property Description() As String Implements ICustomService.Description


    Public Sub Start() Implements ICustomService.Start
        If Not Directory.Exists(_dataLocationPath) Then
            Directory.CreateDirectory(_dataLocationPath)

        End If

        If Not Directory.Exists(_dataArchiveLocationPath) Then
            Directory.CreateDirectory(_dataArchiveLocationPath)
        End If

        _logger.LogInformation("Starting watching directory and processing existing xml files", "StoredDataToDBPersistence", False, False)
        _lastProcessFileResult = ProcessResult.Success
        _watcher = New FileSystemWatcher(_dataLocationPath, "*.xml")
        _watcher.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName)
        _fileQueue = New Queue(Of String)()
        Dim files As String() = Directory.GetFiles(_dataLocationPath, "*.xml")

        AddHandler _watcher.Created, AddressOf WatcherOnChanged

        If Not files Is Nothing AndAlso files.Count > 0 Then
            For Each file As String In files
                _fileQueue.Enqueue(file)
            Next
        End If

        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ProcessAll))
    End Sub

    Public Sub [Stop]() Implements ICustomService.Stop
        _watcher.EnableRaisingEvents = False
    End Sub

    Public Function GetState() As CustomServiceState Implements ICustomService.GetState
       If Not _processing AndAlso _lastProcessFileResult = ProcessResult.FatalError Then
            Return CustomServiceState.Stopped
        End If
        Return CustomServiceState.Running
    End Function
    Public Function GetStatus() As List(Of CustomServiceStatus) Implements ICustomService.GetStatus
        Dim statuses As New List(Of CustomServiceStatus)
        statuses.Add(New CustomServiceStatus() With {.StatusName = "FileCount", .StatusValue = IIf(_fileQueue Is Nothing, 0, _fileQueue.Count)})
        statuses.Add(New CustomServiceStatus() With {.StatusName = "Processing", .StatusValue = _processing.ToString()})
        statuses.Add(New CustomServiceStatus() With {.StatusName = "LastFileProcessResult", .StatusValue = _lastProcessFileResult.ToString()})
        Return statuses
    End Function
   Private Sub ProcessAll(state As Object)
        _processing = True
        _watcher.EnableRaisingEvents = True
        Dim fileProcessCount As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)()
        While True
            Try

                While _fileQueue.Count > 0
                    Dim f As String = _fileQueue.Dequeue()
                    _lastProcessFileResult = Process(f)
                    If _lastProcessFileResult = ProcessResult.FatalError Then
                        _logger.LogException(New Exception("StoredDataToDBPersistence stopped because of a fatal error."), "StoredDataToDBPersistence", True)
                        Exit While
                    ElseIf _lastProcessFileResult = ProcessResult.Failed Then
                        If fileProcessCount.ContainsKey(f) Then
                            fileProcessCount(f) += 1
                            If fileProcessCount(f) = 10 Then
                                _lastProcessFileResult = ProcessResult.FatalError
                                _logger.LogException(New Exception("StoredDataToDBPersistence stopped because of a fatal error caused by the file " + f), "StoredDataToDBPersistence", True, True)
                                Exit While
                            End If
                        Else
                            fileProcessCount.Add(f, 1)
                            If _fileQueue.Count <= 2 Then
                                Thread.Sleep(100)
                                _fileQueue.Enqueue(f)
                            End If
                        End If
                    Else
                        If fileProcessCount.ContainsKey(f) Then
                            fileProcessCount.Remove(f)
                        End If
                    End If
                End While
            Catch ex As Exception
                _lastProcessFileResult = ProcessResult.FatalError
                _logger.LogException(ex, "StoredDataToDBPersistence", True, True)
            Finally
                _processing = False
            End Try
            If _lastProcessFileResult <> ProcessResult.FatalError Then
                Thread.Sleep(30000)
            Else
                Exit While
            End If
        End While

    End Sub
    Private Function Process(f As String) As ProcessResult
        Try
            Dim xsr As New XmlSerializer(GetType(GPSDeviceData))
            Dim finfo As New FileInfo(f)

            _logger.LogInformation(String.Format("Saving '{0}' file into database ", finfo.Name), "StoredDataToDBPersistence", True)
            Dim oStream As FileStream
            Dim tryCount As Integer
Again:
            Try
                oStream = File.Open(f, FileMode.Open, FileAccess.Read, FileShare.None)
            Catch ex As IOException
                'file is in used. wait 1sec 
                If tryCount = 0 Then
                    Thread.Sleep(1000)
                    tryCount += 1
                    GoTo again
                Else
                    Return ProcessResult.Failed
                End If
            Catch ex As Exception
                _logger.LogInformation(String.Format("Fatal error while processing the file {0}, Error : {1}", f, ex.Message), "StoredDataToDBPersistence", True, True)
                Return ProcessResult.FatalError
            End Try

            Dim objStreamReader As New StreamReader(oStream)

            Dim gpsDeviceData As GPSDeviceData = xsr.Deserialize(objStreamReader)
            objStreamReader.Close()
            Dim gsLoc As GsLoc = GPSDeviceDataToGsLoc(gpsDeviceData)

            Persist(gsLoc)
            Try
                If Not oStream Is Nothing Then oStream.Close()
                Dim logDate As String = Date.ParseExact(gpsDeviceData.LoggingTime, "ddMMyyHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime().ToString("dd.MM.yyyy")
                Dim dir As String = Path.Combine(_dataArchiveLocationPath, logDate)

                If Not Directory.Exists(dir) Then
                    Directory.CreateDirectory(dir)
                End If


                Dim destFile As String = Path.Combine(dir, finfo.Name)
                If File.Exists(destFile) Then File.Delete(destFile)
               
                File.Move(f, destFile)
            Catch ex As Exception

            End Try
            _logger.LogInformation(String.Format("file '{0} saved into database ", finfo.Name), "StoredDataToDBPersistence", True)

            Return ProcessResult.Success
            'delete after processing

        Catch ex As SqlException
            _logger.LogException(ex, "StoredDataToDBPersistence", True, True)
            Return ProcessResult.FatalError
        Catch ex As InvalidOperationException
            'the file is locked, invalid or anything else. let report it.
            _logger.LogException(ex, "StoredDataToDBPersistence", True, True)
            Return ProcessResult.FatalError
        Catch ex As Exception
            'the file is locked, invalid or anything else. let report it.
            _logger.LogException(ex, "StoredDataToDBPersistence", True)
            Return ProcessResult.Failed
        End Try


    End Function

    Private Sub WatcherOnChanged(sender As Object, e As FileSystemEventArgs)
        If e.ChangeType = WatcherChangeTypes.Created Then
            _fileQueue.Enqueue(e.FullPath)
        End If
    End Sub

    'TODO : TO BE DONE LATER IF IMMEDIATE SAVING DOESN'T WORK. 
  Private Sub Persist(gsLoc As GsLoc)

        'Using conn As New SqlConnection(_connectionString)
        '    conn.Open()
        '    Using cmd As New SqlCommand()
        '        With cmd
        '            .Connection = conn
        '            .CommandText =
        '            .ExecuteNonQuery()
        '        End With
        '    End Using
        'End Using
    End Sub

    Private Enum ProcessResult
        Success
        Failed
        FatalError
    End Enum

End Class
