Imports System.Collections.ObjectModel
Imports System.IO
Imports Newtonsoft.Json
Imports System.Windows.Threading
Imports System.Text.RegularExpressions
Imports System.Security.Cryptography
Imports Newtonsoft.Json.Linq
Imports Effector.Plugin.StoneCut.Door
Imports System.Globalization
Imports System.Diagnostics
Imports Effector.Plugin.Lib

Public Class DoorListPageVM
   Inherits VMBase

   Private m_BackupTimer As New DispatcherTimer
   Private m_WriteCSVTimer As New DispatcherTimer
   Private m_RefreshGraphicsTimer As New DispatcherTimer
   Private m_ExecProcessManager As ExecProcessManager

   Private m_ReadPartProgramFolderTimer As New DispatcherTimer
   Private m_sPartProgramFolderPath As String

   Private m_DoorList As New ObservableCollection(Of Door)

   Private m_bResetList As Boolean = False
   Public ReadOnly Property bResetList As Boolean
      Get
         Return m_bResetList
      End Get
   End Property
   Friend Sub ResetResetList()
      m_bResetList = False
   End Sub

   Public ReadOnly Property DoorList As ObservableCollection(Of Door)
      Get
         Return m_DoorList
      End Get
   End Property
   Private m_SelDoor As Door
   Public Property SelDoor As Door
      Get
         Return m_SelDoor
      End Get
      Set(value As Door)
         m_SelDoor = value
      End Set
   End Property

   Private m_DDTList As New ObservableCollection(Of String)
   Public ReadOnly Property DDTList As ObservableCollection(Of String)
      Get
         Return m_DDTList
      End Get
   End Property

   Private m_ManualAddedDoorName As String = "Manual"
   Public ReadOnly Property ManualAddedDoorName As String
      Get
         Return m_ManualAddedDoorName
      End Get
   End Property

   Private m_bExecButton_IsEnabled As Boolean = True
   Public ReadOnly Property bExecButton_IsEnabled As Boolean
      Get
         Return m_bExecButton_IsEnabled
      End Get
   End Property
   Private Sub SetExecButtonIsEnabled(bValue As Boolean)
      m_bExecButton_IsEnabled = bValue
      NotifyPropertyChanged(NameOf(bExecButton_IsEnabled))
   End Sub
   Private m_nModifyIndex As Integer = 0
   Private m_nBackupindex As Integer = 0
   Private m_bWritingBackup As Boolean = False

   Private m_sCSVOutDirPath As String = ""

   Private m_nExecIncrement As Integer = 0
   Private m_nExecutedArgsCount As Integer = 0
   Public ReadOnly Property dExecPercentage As Double
      Get
         If m_bCreatingDdfs Then
            Return 5 / m_nTotCreatingDdf * m_nCurrCreatingDdf
         End If
         If IsNothing(m_ExecProcessManager) Then Return 0
         Dim dExecIncrement As Double = If(m_ExecProcessManager.nArgsInQueue = 0, 0, Math.Min(28, m_nExecIncrement) / 34)
         Return (m_ExecProcessManager.nExecutedArgsCounter + dExecIncrement) / (m_ExecProcessManager.nExecutedArgsCounter + m_ExecProcessManager.nArgsInQueue) * 100
      End Get
   End Property

   Public ReadOnly Property sExecPercentage As String
      Get
         Return DoubleToString(dExecPercentage, 1) & "%"
      End Get
   End Property

   Public ReadOnly Property ExecPercentage_Visibility As Visibility
      Get
         Return If((Not IsNothing(m_ExecProcessManager) AndAlso m_ExecProcessManager.ExecutionThreadStatus = ExecProcessManager.ExecutionThreadStatuses.RUNNING) OrElse m_bCreatingDdfs, Visibility.Visible, Visibility.Collapsed)
      End Get
   End Property

   Private m_bCreatingDdfs As Boolean
   Private m_nTotCreatingDdf As Integer
   Private m_nCurrCreatingDdf As Integer

   ' Definizione comandi
   Private m_cmdProduce As ICommand
   Private m_cmdProduceAll As ICommand
   Private m_cmdResetProductionQueue As ICommand
   Private m_cmdMoveUp As ICommand
   Private m_cmdMoveDown As ICommand
   Private m_cmdNotProduce As ICommand
   Private m_cmdCopy As ICommand
   ' Private m_cmdMultipleCopy As ICommand
   Private m_cmdDelete As ICommand
   Private m_cmdDeleteAll As ICommand

#Region "CONSTRUCTOR"

   Sub New()
      ' imposto riferimento in Map
      Map.SetRefDoorListPageVM(Me)
      ' leggo backup
      Dim sBackupDirPath As String = ""
      GetPluginPrivateProfileString(S_GENERAL, "BackupDir", "", sBackupDirPath)
      Dim sBackupFilePath As String = sBackupDirPath & "\Backup.json"
      Read(sBackupFilePath)
      m_BackupTimer.Interval = New TimeSpan(0, 0, 1)
      AddHandler m_BackupTimer.Tick, AddressOf BackupTimer_Tick
      m_WriteCSVTimer.Interval = New TimeSpan(0, 1, 0)
      AddHandler m_WriteCSVTimer.Tick, AddressOf WriteCSVTimer_Tick
      m_RefreshGraphicsTimer.Interval = New TimeSpan(0, 0, 1)
      AddHandler m_RefreshGraphicsTimer.Tick, AddressOf RefreshGraphicsTimer_Tick
      m_BackupTimer.Start()
      ' verifico path salvataggio CSV
      Dim bStartWriteCSV As Boolean = False
      If GetPluginPrivateProfileInt(S_GENERAL, K_ISCSVOUTPUTENABLED, 0) = 1 Then
         bStartWriteCSV = True
         If GetPluginPrivateProfileString(S_GENERAL, K_CSVOUTPUTPATH, "", m_sCSVOutDirPath) = 0 Then
            bStartWriteCSV = False
            MessageBox.Show("Path di salvataggio CSV non impostata! Impossibile creare file CSV!", "Errore!", MessageBoxButton.OK, MessageBoxImage.Error)
         End If
         If bStartWriteCSV AndAlso Not Directory.Exists(m_sCSVOutDirPath) Then
            bStartWriteCSV = False
            MessageBox.Show("Path di salvataggio CSV impostata non esiste! Impossibile creare file CSV!", "Errore!", MessageBoxButton.OK, MessageBoxImage.Error)
         End If
         If bStartWriteCSV Then
            m_WriteCSVTimer.Start()
         End If
      End If
      m_RefreshGraphicsTimer.Start()

      ' leggo path cartella dei partprogram
      GetPluginPrivateProfileString(S_GENERAL, "PartProgramFolder", "", m_sPartProgramFolderPath)
      If Not Directory.Exists(m_sPartProgramFolderPath) Then
         MessageBox.Show("Folder not found! Folder path not configured correctly")
      End If
      ' avvio timer di copia file
      AddHandler m_ReadPartProgramFolderTimer.Tick, AddressOf ReadPartProgramFolderTimer_Tick
      m_ReadPartProgramFolderTimer.Interval = TimeSpan.FromSeconds(1)
      m_ReadPartProgramFolderTimer.Start()
   End Sub
#End Region    ' CONSTRUCTOR

#Region "METHODS"

   Private Sub ReadPartProgramFolderTimer_Tick(sender As Object, e As EventArgs)
      Dim FileList() As String = Directory.GetFiles(m_sPartProgramFolderPath)
      If FileList.Count = 0 Then Return
      ' copio file e li metto in lista
      Dim sPartProgramDirPath As String = ""
      GetPluginPrivateProfileString(S_GENERAL, "PartProgramDir", "", sPartProgramDirPath)
      For nFileIndex = 0 To FileList.Count - 1
         Dim bFileMove As Boolean = False

         Dim sNewFilePath As String = sPartProgramDirPath & "\" & Path.GetFileName(FileList(nFileIndex))
         Try
            File.Move(FileList(nFileIndex), sNewFilePath)
            bFileMove = True
         Catch ex As Exception
            MessageBox.Show("Impossible loading file " & FileList(0))
         End Try
         If bFileMove Then
            Dim CurrFileInfo As New FileInfo(sNewFilePath)
            Dim CurrFileLength As Double = CDbl(CurrFileInfo.Length)
            Dim CurrFileLines = File.ReadLines(sNewFilePath).Take(10).ToList()
            Dim nId As Integer = 0
            Dim nTable As Integer = 0
            For nLineIndex = 0 To CurrFileLines.Count - 1
               Dim IdMatch As Match = Regex.Match(CurrFileLines(nLineIndex), ".*\(ID=(\d+)\)")
               If Not IsNothing(IdMatch) AndAlso Not String.IsNullOrWhiteSpace(IdMatch.Value) Then
                  Integer.TryParse(IdMatch.Groups(1).Value, nId)
               End If
               Dim TableMatch As Match = Regex.Match(CurrFileLines(nLineIndex), ".*\(TABLE=(\d+)\)")
               If Not IsNothing(TableMatch) AndAlso Not String.IsNullOrWhiteSpace(TableMatch.Value) Then
                  Integer.TryParse(TableMatch.Groups(1).Value, nTable)
               End If
               If nId <> 0 AndAlso nTable <> 0 Then Exit For
            Next
            m_DoorList.Add(New Door(nId, Path.GetFileName(FileList(nFileIndex)), nTable, CurrFileLength))
         End If
      Next
      WriteBackup()
   End Sub

   Public Sub Read(FilePath As String)
      If Not File.Exists(FilePath) Then Return
      Dim sReadedFile As String = File.ReadAllText(FilePath)
      Dim JsonDoorList As List(Of JsonDoor) = JsonConvert.DeserializeObject(Of List(Of JsonDoor))(sReadedFile)
      JsonDoorList = JsonDoorList.OrderBy(Of Integer)(Function(x) x.nListIndex).ToList()

      Dim TempDoorList As ObservableCollection(Of Door) = New ObservableCollection(Of Door)((From JsonDoor In JsonDoorList
                                                                                             Select New Door(JsonDoor)).ToList())
      'For Each CurrDoor In TempDoorList
      '   If CurrDoor.nState >= Door.PartProgramStates.READY_FOR_PRODUCTION AndAlso CurrDoor.nState < Door.PartProgramStates.MACHINE_1_END Then
      '      CurrDoor.SetState(Door.PartProgramStates.VERIFIED, False)
      '   End If
      'Next
      'WriteBackup()
      m_DoorList = TempDoorList
   End Sub

   Public Sub Write(FilePath As String)
      Dim JsonDoorList As New List(Of JsonDoor)
      For nDoorIndex As Integer = 0 To m_DoorList.Count - 1
         JsonDoorList.Add(New JsonDoor(nDoorIndex, m_DoorList(nDoorIndex)))
      Next
      Dim JsonFromWindow As String = JsonConvert.SerializeObject(JsonDoorList, Formatting.Indented)
      If File.Exists(FilePath) Then
         Dim sBackupFilePath As String = Path.ChangeExtension(FilePath, ".bck")
         If File.Exists(sBackupFilePath) Then
            Try
               File.Delete(sBackupFilePath)
            Catch ex As Exception
               Map.refSupervisorFunction.PlgOutLog("Error! Impossible deleting backup file!")
            End Try
         End If
         Try
            File.Move(FilePath, sBackupFilePath)
         Catch ex As Exception
            Map.refSupervisorFunction.PlgOutLog("Error! Impossible renaming backup file!")
         End Try
      End If
      File.WriteAllText(FilePath, JsonFromWindow)
   End Sub

   Private Sub WriteBackupCmd()
      If m_bWritingBackup Then Return
      m_bWritingBackup = True
      m_nBackupindex = m_nModifyIndex
      Dim sBackupDirPath As String = ""
      GetPluginPrivateProfileString(S_GENERAL, K_BACKUPDIR, "", sBackupDirPath)
      Dim sBackupFilePath As String = sBackupDirPath & "\Backup.json"
      Write(sBackupFilePath)
      m_bWritingBackup = False
   End Sub

   Friend Sub WriteBackup()
      m_nModifyIndex += 1
   End Sub

   Private Sub BackupTimer_Tick(sender As Object, e As EventArgs)
      If m_nBackupindex < m_nModifyIndex Then
         WriteBackupCmd()
      End If
   End Sub

   Private Sub WriteCSVTimer_Tick(sender As Object, e As EventArgs)
      'SaveCSV()
   End Sub

   Private Sub RefreshGraphicsTimer_Tick(sender As Object, e As EventArgs)
      'If IsNothing(m_ExecProcessManager) Then Return
      '' ciclo su coda risultati
      'Dim ArgumentsResult As ProcessArgsResult = m_ExecProcessManager.ArgumentsResultDequeue
      'While Not IsNothing(ArgumentsResult)
      '   ' aggiorno risultato sulla porta
      '   Dim CurrRequestDoor As Door = m_DoorList.FirstOrDefault(Function(x) x.nId = ArgumentsResult.ProcessArgs.nId)
      '   If Not IsNothing(CurrRequestDoor) Then
      '      CurrRequestDoor.SetState(If(ArgumentsResult.nResult = 0, Door.PartProgramStates.VERIFIED, Door.PartProgramStates.VERIFICATION_FAILED))
      '      ' elimino file generati
      '      Dim sGenDDFDirPath As String = ""
      '      GetPluginPrivateProfileString(S_GENERAL, K_GENDDFDIR, "", sGenDDFDirPath)
      '      Dim sDoorFileName As String = Path.GetFileNameWithoutExtension(CurrRequestDoor.sFileName) & "_" & CurrRequestDoor.nId
      '      Try
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".tok")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2.tok")
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".cnc")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_a.cnc")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_b.cnc")
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2.cnc")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2_a.cnc")
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".html")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2.html")
      '         'If ArgumentsResult.nResult = 0 Then
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".log")
      '         'End If
      '         ''File.Delete(sGenDDFDirPath & "\" & CurrRequestDoor.sDDFName & ".nge")
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".nge")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".sest")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2.sest")
      '         'Dim SestFiles() As String = System.IO.Directory.GetFiles(sGenDDFDirPath & "\", sDoorFileName & "*.sest")
      '         'For Each FilePath In SestFiles
      '         '   File.Delete(FilePath)
      '         'Next
      '         ''File.Delete(sGenDDFDirPath & "\" & CurrRequestDoor.sDDFName & ".txt")
      '         File.Delete(sGenDDFDirPath & "\" & sDoorFileName & ".txt")
      '         'File.Delete(sGenDDFDirPath & "\" & sDoorFileName & "_2.txt")
      '      Catch ex As Exception

      '      End Try
      '   End If
      '   ' verifico se ci sono altre porte dello stesso tipo da aggiornare
      '   If m_AssociationList.ContainsKey(CurrRequestDoor.nId) Then
      '      Dim SameDoorList As List(Of Integer) = m_AssociationList(CurrRequestDoor.nId)
      '      For Each CurrDoorId In SameDoorList
      '         Dim CurrDoor As Door = m_DoorList.FirstOrDefault(Function(x) x.nId = CurrDoorId)
      '         If Not IsNothing(CurrDoor) Then
      '            If CopyGenDdfFile(CurrRequestDoor, CurrDoor) Then
      '               CurrDoor.SetState(If(ArgumentsResult.nResult = 0, Door.PartProgramStates.VERIFIED, Door.PartProgramStates.VERIFICATION_FAILED))
      '            End If
      '         End If
      '      Next
      '   End If
      '   WriteBackup()
      '   ArgumentsResult = m_ExecProcessManager.ArgumentsResultDequeue
      'End While
      '' aggiorno grafica
      'If m_nExecutedArgsCount <> m_ExecProcessManager.nExecutedArgsCounter Then
      '   m_nExecutedArgsCount = m_ExecProcessManager.nExecutedArgsCounter
      '   m_nExecIncrement = 0
      'ElseIf m_ExecProcessManager.nCalculatingProcesses > 0 Then
      '   m_nExecIncrement += 1
      'End If
      'If Not bExecButton_IsEnabled AndAlso m_ExecProcessManager.ExecutionThreadStatus = ExecProcessManager.ExecutionThreadStatuses.RUNNING Then
      '   NotifyPropertyChanged(NameOf(dExecPercentage))
      '   NotifyPropertyChanged(NameOf(sExecPercentage))
      'End If
      'If Not bExecButton_IsEnabled AndAlso m_ExecProcessManager.ExecutionThreadStatus = ExecProcessManager.ExecutionThreadStatuses.STOPPED Then
      '   SetExecButtonIsEnabled(True)
      'End If
      'NotifyPropertyChanged(NameOf(ExecPercentage_Visibility))
   End Sub

   Friend Function GetNextDoor() As Door
      Dim NextDoor As Door = m_DoorList.FirstOrDefault(Function(x) x.nState = Door.PartProgramStates.READY_FOR_PRODUCTION)
      If Not IsNothing(NextDoor) Then
         Map.refSupervisorFunction.PlgOutLog("GetNextDoor: door " & NextDoor.nId & " sent to NC")
         NextDoor.SetState(Door.PartProgramStates.SENT_TO_NC)
         WriteBackup()
      End If
      Return NextDoor
   End Function

   Friend Function NewDoorId() As Integer
      Dim nId As Integer = 1
      If m_DoorList.Count > 0 Then
         Dim nMaxId As Integer = m_DoorList.Max(Of Integer)(Function(x) x.nId)
         nId = nMaxId + 1
      End If
      Return nId
   End Function

   Friend Sub ResetStateAfterReset()
      'For Each Door In m_DoorList
      '   If Door.nState > Door.PartProgramStates.VERIFIED AndAlso Door.nState < Door.PartProgramStates.MACHINE_1_END Then
      '      Door.SetState(Door.PartProgramStates.VERIFIED, False)
      '   End If
      'Next
   End Sub

#End Region    ' METHODS

#Region "COMMANDS"

#Region "Copy"

   Public ReadOnly Property Copy_Command As ICommand
      Get
         If m_cmdCopy Is Nothing Then
            m_cmdCopy = New Command(AddressOf CopyCmd)
         End If
         Return m_cmdCopy
      End Get
   End Property

   Public Sub CopyCmd(CurrDoor As Door)
      If IsNothing(CurrDoor) Then Return
      Dim nId As Integer = NewDoorId()
      Dim CopyDoor As Door = CurrDoor.Copy(nId)
      If Not IsNothing(CopyDoor) Then
         m_DoorList.Add(CurrDoor.Copy(nId))
      End If
   End Sub


#End Region ' CopyDoor

#Region "MoveUp"

   Public ReadOnly Property MoveUp_Command As ICommand
      Get
         If m_cmdMoveUp Is Nothing Then
            m_cmdMoveUp = New Command(AddressOf MoveUp)
         End If
         Return m_cmdMoveUp
      End Get
   End Property

   Public Sub MoveUp(CurrDoor As Object)
      If IsNothing(CurrDoor) Then Return
      Dim nOldIndex As Integer = m_DoorList.IndexOf(CurrDoor)
      If nOldIndex = 0 Then Return
      If m_DoorList(nOldIndex - 1).nState >= Door.PartProgramStates.READY_FOR_PRODUCTION Then Return
      m_DoorList.Move(nOldIndex, nOldIndex - 1)
      WriteBackup()
   End Sub

#End Region ' MoveUp

#Region "MoveDown"

   Public ReadOnly Property MoveDown_Command As ICommand
      Get
         If m_cmdMoveDown Is Nothing Then
            m_cmdMoveDown = New Command(AddressOf MoveDown)
         End If
         Return m_cmdMoveDown
      End Get
   End Property

   Public Sub MoveDown(CurrDoor As Door)
      If IsNothing(CurrDoor) OrElse CurrDoor.nState >= Door.PartProgramStates.READY_FOR_PRODUCTION Then Return
      Dim nOldIndex As Integer = m_DoorList.IndexOf(CurrDoor)
      If nOldIndex = m_DoorList.Count - 1 Then Return
      m_DoorList.Move(nOldIndex, nOldIndex + 1)
      WriteBackup()
   End Sub

#End Region ' MoveDown

#Region "Delete"

   Public ReadOnly Property Delete_Command As ICommand
      Get
         If m_cmdDelete Is Nothing Then
            m_cmdDelete = New Command(AddressOf Delete)
         End If
         Return m_cmdDelete
      End Get
   End Property

   Public Sub Delete(CurrDoor As Door)
      If m_DoorList.Count = 0 OrElse IsNothing(CurrDoor) Then Return
      Dim bReset As Boolean = True
      'If Not LuaGetGlobVar("bReset", bReset) OrElse Not bReset Then
      '   If (SelDoor.nState >= Door.PartProgramStates.READY_FOR_PRODUCTION AndAlso SelDoor.nState < Door.PartProgramStates.MACHINE_1_END) Then Return
      'End If
      If MessageBox.Show("Sei sicuro di voler cancellare la porta selezionata?", "Avviso", MessageBoxButton.YesNo, MessageBoxImage.Warning) = MessageBoxResult.Yes Then
         ' cancello file
         Dim sPartProgramDirPath As String = ""
         GetPluginPrivateProfileString(S_GENERAL, "PartProgramDir", "", sPartProgramDirPath)
         Try
            File.Delete(sPartProgramDirPath & "\" & CurrDoor.sFileName)
         Catch ex As Exception

         End Try
         m_DoorList.Remove(SelDoor)
         WriteBackup()
      End If
   End Sub

#End Region ' Delete

#Region "DeleteAll"

   Public ReadOnly Property DeleteAll_Command As ICommand
      Get
         If m_cmdDeleteAll Is Nothing Then
            m_cmdDeleteAll = New Command(AddressOf DeleteAll)
         End If
         Return m_cmdDeleteAll
      End Get
   End Property

   Public Sub DeleteAll()
      If m_DoorList.Count = 0 Then Return
      Dim bReset As Boolean = True
      'If Not LuaGetGlobVar("bReset", bReset) OrElse Not bReset Then
      '   MessageBox.Show("Impossibile cancellare tutte le porte!" & Environment.NewLine & "La macchina deve essere in stato Reset per poter cancellare tutte le porte!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning)
      '   Return
      'End If
      '' verifico che tutte le porte siano in stato prodotto o da non produrre
      'Dim bAllProduced As Boolean = True
      'For Each CsvDoor In m_DoorList
      '   If CsvDoor.nState <> PartProgramStates.MACHINE_1_END AndAlso CsvDoor.nState <> PartProgramStates.PRODUCED AndAlso CsvDoor.nState <> PartProgramStates.NOTPRODUCE AndAlso CsvDoor.nState <> PartProgramStates.VERIFICATION_FAILED AndAlso CsvDoor.nState <> PartProgramStates.VERIFIED Then
      '      bAllProduced = False
      '   End If
      'Next
      'If Not bAllProduced Then
      '   MessageBox.Show("Impossibile cancellare tutte le porte!" & Environment.NewLine & "Per cancellare tutte le porte, devono essere tutte non in lavorazione!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning)
      '   Return
      'End If
      If MessageBox.Show("Sei sicuro di voler cancellare tutte le porte?", "Info", MessageBoxButton.YesNo, MessageBoxImage.Information) = MessageBoxResult.Yes Then
         For PartProgramIndex = m_DoorList.Count - 1 To 0 Step -1
            ' cancello file
            Dim sPartProgramDirPath As String = ""
            GetPluginPrivateProfileString(S_GENERAL, "PartProgramDir", "", sPartProgramDirPath)
            Try
               File.Delete(sPartProgramDirPath & "\" & m_DoorList(PartProgramIndex).sFileName)
            Catch ex As Exception

            End Try
            m_DoorList.RemoveAt(PartProgramIndex)
         Next
         WriteBackup()
         '' cancello backup lua per ripartire pulito
         'Try
         '   Dim sBackupDirPath As String = ""
         '   GetPluginPrivateProfileString(S_GENERAL, K_BACKUPDIR, "", sBackupDirPath)
         '   Dim sLuaBackupFilePath As String = sBackupDirPath & "\LuaBackup.json"
         '   File.Delete(sLuaBackupFilePath)
         'Catch ex As Exception
         '   Map.refSupervisorFunction.PlgOutLog("Fallita cancellazione del file LuaBackup!")
         'End Try
      End If
   End Sub

#End Region ' DeleteAll

#Region "Produce"

   Public ReadOnly Property Produce_Command As ICommand
      Get
         If m_cmdProduce Is Nothing Then
            m_cmdProduce = New Command(AddressOf Produce)
         End If
         Return m_cmdProduce
      End Get
   End Property

   Public Sub Produce(CurrDoor As Door)
      ProduceDoor(CurrDoor)
      WriteBackup()
   End Sub

   Public Sub ProduceDoor(DoorToProduce As Door, Optional bWriteBackup As Boolean = True)
      If IsNothing(DoorToProduce) Then Return
      ' la sposto dopo l'ultima da produrre
      Dim nNewIndex As Integer = m_DoorList.IndexOf(m_DoorList.FirstOrDefault(Function(x) x.nState < Door.PartProgramStates.READY_FOR_PRODUCTION))
      Dim nOldIndex As Integer = m_DoorList.IndexOf(DoorToProduce)
      m_DoorList.Move(nOldIndex, nNewIndex)
      DoorToProduce.SetState(Door.PartProgramStates.READY_FOR_PRODUCTION, bWriteBackup)
   End Sub

#End Region ' Produce

#Region "ProduceAll"

   Public ReadOnly Property ProduceAll_Command As ICommand
      Get
         If m_cmdProduceAll Is Nothing Then
            m_cmdProduceAll = New Command(AddressOf ProduceAll)
         End If
         Return m_cmdProduceAll
      End Get
   End Property

   Public Sub ProduceAll()
      If m_DoorList.Count = 0 Then Return
      For nDoorIndex = 0 To m_DoorList.Count - 1
         Dim CurrDoor As Door = m_DoorList(nDoorIndex)
         If CurrDoor.nState <> Door.PartProgramStates.MACHINE_START AndAlso CurrDoor.nState <> Door.PartProgramStates.PRODUCED Then
            ProduceDoor(CurrDoor, False)
         End If
      Next
      WriteBackup()
   End Sub

#End Region ' ProduceAll

#Region "ResetProductionQueue"

   Public ReadOnly Property ResetProductionQueue_Command As ICommand
      Get
         If m_cmdResetProductionQueue Is Nothing Then
            m_cmdResetProductionQueue = New Command(AddressOf ResetProductionQueueCmd)
         End If
         Return m_cmdResetProductionQueue
      End Get
   End Property

   Public Sub ResetProductionQueueCmd()
      ResetProductionQueue()
      WriteBackup()
   End Sub

   Public Sub ResetProductionQueue()
      For Each Door In m_DoorList
         If Door.nState = Door.PartProgramStates.READY_FOR_PRODUCTION OrElse Door.nState = Door.PartProgramStates.SENT_TO_NC Then
            Door.SetState(Door.PartProgramStates.NULL)
         End If
      Next
      m_bResetList = True
   End Sub

#End Region ' ResetProductionQueue

#End Region    ' COMMANDS

End Class

Public Class Door
   Inherits VMBase

   Public Enum SWINGTYPE As Integer
      UNDEFINED = 0
      RIGHT = 1
      LEFT = 2
   End Enum

   Private Shared m_refWriteBackup As Action

   Public Enum PartProgramStates As Integer
      NULL = 0
      READY_FOR_PRODUCTION = 1
      SENT_TO_NC = 2
      MACHINE_START = 3
      PRODUCED = 4
      NOTPRODUCE = 10
   End Enum

   Private m_nId As Integer
   Public ReadOnly Property nId As Integer
      Get
         Return m_nId
      End Get
   End Property

   Private m_sFileName As String
   Public Property sFileName As String
      Get
         Return m_sFileName
      End Get
      Set(value As String)
         m_sFileName = value
      End Set
   End Property
   Public ReadOnly Property sFileNameWithoutExtension As String
      Get
         Return If(String.IsNullOrWhiteSpace(m_sFileName), "", Path.GetFileNameWithoutExtension(m_sFileName))
      End Get
   End Property

   Private m_nTable As Integer
   Public ReadOnly Property nTable As Integer
      Get
         Return m_nTable
      End Get
   End Property

   Private m_dFileDimension As Double
   Public ReadOnly Property dFileDimension As Double
      Get
         Return m_dFileDimension
      End Get
   End Property
   Public ReadOnly Property sFileDimension As String
      Get
         Return DoubleToString(m_dFileDimension / 1000, 2) & "Kb"
      End Get
   End Property

   Private m_nState As PartProgramStates = PartProgramStates.NULL
   Public ReadOnly Property nState As PartProgramStates
      Get
         Return m_nState
      End Get
   End Property
   Friend Sub SetState(value As PartProgramStates, Optional bWriteBackup As Boolean = True, Optional bUpdate As Boolean = True)
      m_nState = value
      If bWriteBackup Then Map.refDoorListPageVM.WriteBackup()
      If bUpdate Then
         NotifyPropertyChanged(NameOf(nState))
         NotifyPropertyChanged(NameOf(Background))
      End If
   End Sub

#Region "Statistics"

   Private m_dtMachiningStart As DateTime = DateTime.MinValue
   Friend ReadOnly Property dtMachiningStart As DateTime
      Get
         Return m_dtMachiningStart
      End Get
   End Property
   Public ReadOnly Property sMachiningStart As String
      Get
         Return ConvertDateTimeToString(m_dtMachiningStart)
      End Get
   End Property
   Friend Sub SetMachiningStart(dtMachiningStart As DateTime)
      m_dtMachiningStart = dtMachiningStart
      NotifyPropertyChanged(NameOf(sMachiningStart))
   End Sub
   'Friend Sub SetMachiningStart(Machining1Start As Long)
   '   m_dtMachiningStart = New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Machining1Start).ToLocalTime()
   '   NotifyPropertyChanged(NameOf(sMachiningStart))
   'End Sub

   Private m_dtMachiningEnd As DateTime = DateTime.MinValue
   Friend ReadOnly Property dtMachiningEnd As DateTime
      Get
         Return m_dtMachiningEnd
      End Get
   End Property
   Public ReadOnly Property sMachiningEnd As String
      Get
         Return ConvertDateTimeToString(m_dtMachiningEnd)
      End Get
   End Property
   Friend Sub SetMachiningEnd(dtMachiningEnd As DateTime)
      m_dtMachiningEnd = dtMachiningEnd
      NotifyPropertyChanged(NameOf(sMachiningEnd))
      NotifyPropertyChanged(NameOf(sMachiningTime))
   End Sub

   Public ReadOnly Property sMachiningTime As String
      Get
         If m_dtMachiningStart = DateTime.MinValue OrElse m_dtMachiningEnd = DateTime.MinValue Then Return String.Format("{0:c}", TimeSpan.Zero)
         Return String.Format("{0:c}", (m_dtMachiningEnd - m_dtMachiningStart))
      End Get
   End Property

#End Region    ' Statistics

   Public ReadOnly Property Background As SolidColorBrush
      Get
         Select Case m_nState
            Case PartProgramStates.NULL
               Return Application.Current.FindResource("Omag_LightGray")
            Case PartProgramStates.READY_FOR_PRODUCTION
               Return Dictionary.Effector_LightBlue_Status
            Case PartProgramStates.NOTPRODUCE
               Return Dictionary.Effector_Yellow_Status
            Case PartProgramStates.MACHINE_START
               Return Dictionary.Effector_Blue_Status
            Case PartProgramStates.PRODUCED
               Return Dictionary.Effector_Gray_Status
            Case Else
               Return Brushes.LightGray
         End Select
      End Get
   End Property

   Public ReadOnly Property Foreground As SolidColorBrush
      Get
         Select Case m_nState
            Case PartProgramStates.NULL
               Return Brushes.White
            Case PartProgramStates.NOTPRODUCE
               Return Dictionary.Effector_Yellow_Status
            Case PartProgramStates.MACHINE_START
               Return Dictionary.Effector_Blue_Status
            Case PartProgramStates.PRODUCED
               Return Dictionary.Effector_Gray_Status
            Case Else
               Return Brushes.White
         End Select
      End Get
   End Property

   ' Definizione comandi
   Private m_cmdMoveUp As ICommand
   Private m_cmdMoveDown As ICommand
   Private m_cmdNotProduce As ICommand
   Private m_cmdCopyDoor As ICommand
   'Private m_cmdMultipleCopyDoor As ICommand
   Private m_cmdDelete As ICommand

#Region "CONSTRUCTOR"

   Sub New(nId As Integer, sFileName As String, nTable As Integer, dFileDimension As Double)
      m_nId = nId
      m_sFileName = sFileName
      m_nTable = nTable
      m_dFileDimension = dFileDimension
   End Sub

   Sub New(JsonDoor As JsonDoor)
      m_nId = JsonDoor.nId
      m_sFileName = JsonDoor.sFileName
      m_nTable = JsonDoor.nTable
      m_dFileDimension = JsonDoor.dFileDimension
   End Sub

#End Region    ' CONSTRUCTOR

#Region "METHODS"

   Friend Function Copy(nId As Integer) As Door
      Dim sPartProgramDirPath As String = ""
      GetPluginPrivateProfileString(S_GENERAL, "PartProgramDir", "", sPartProgramDirPath)
      Dim nCopyIndex As Integer = 1
      Dim sFileExtension As String = Path.GetExtension(m_sFileName)
      Dim sFileName As String = Path.GetFileNameWithoutExtension(m_sFileName)
      While File.Exists(sPartProgramDirPath & "\" & sFileName & "_" & nCopyIndex & sFileExtension)
         nCopyIndex += 1
      End While
      Dim bFileCopy As Boolean = False
      Try
         File.Copy(sPartProgramDirPath & "\" & m_sFileName, sPartProgramDirPath & "\" & sFileName & "_" & nCopyIndex & sFileExtension)
         bFileCopy = True
      Catch ex As Exception

      End Try
      If bFileCopy Then
         Return New Door(nId, sFileName & "_" & nCopyIndex & sFileExtension, m_nTable, m_dFileDimension)
      Else Return Nothing
      End If
   End Function

#End Region    ' METHODS

#Region "COMMANDS"

#Region "NotProduce"

   Public ReadOnly Property NotProduce_Command As ICommand
      Get
         If m_cmdNotProduce Is Nothing Then
            m_cmdNotProduce = New Command(AddressOf NotProduce)
         End If
         Return m_cmdNotProduce
      End Get
   End Property

   Public Sub NotProduce()
      SetState(If(nState = Door.PartProgramStates.NULL, Door.PartProgramStates.NOTPRODUCE, Door.PartProgramStates.NULL))
   End Sub

#End Region ' NotProduceDoor

#End Region    ' COMMANDS

End Class

Public Class CustomerParameter

   Private m_sHeader As String
   Public ReadOnly Property sHeader As String
      Get
         Return m_sHeader
      End Get
   End Property

   Private m_sValue As String
   Public ReadOnly Property sValue As String
      Get
         Return m_sValue
      End Get
   End Property
   Friend Sub SetValue(value As String)
      m_sValue = value
   End Sub

   Sub New(sHeader As String, sValue As String)
      m_sHeader = sHeader
      m_sValue = sValue
   End Sub

End Class

Public Class StateToSelColorConverter
   Implements IValueConverter

   Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
      If Not TypeOf value Is PartProgramStates Then Return Brushes.Aqua
      'Select Case value
      '   Case PartProgramStates.LOADED_FROM_CSV
      '      Return Dictionary.Effector_White_SelStatus
      '   Case PartProgramStates.VERIFIED
      '      Return Dictionary.Effector_Green_SelStatus
      '   Case PartProgramStates.VERIFICATION_FAILED
      '      Return Dictionary.Effector_Red_SelStatus
      '   Case PartProgramStates.SKIPPED, PartProgramStates.NOTPRODUCE
      '      Return Dictionary.Effector_Yellow_SelStatus
      '   Case PartProgramStates.READY_FOR_PRODUCTION, PartProgramStates.ON_LOAD_STATION
      '      Return Dictionary.Effector_LightBlue_SelStatus
      '   Case PartProgramStates.MACHINE_1_START
      '      Return Dictionary.Effector_Blue_SelStatus
      '   Case PartProgramStates.MACHINE_1_END, PartProgramStates.PRODUCED
      '      Return Dictionary.Effector_Gray_SelStatus
      '   Case Else
      '      Return Dictionary.Effector_White_SelStatus
      'End Select
   End Function

   Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
      Throw New NotImplementedException()
   End Function

End Class

Public Class DDTListToBooleanConverter
   Implements IValueConverter

   Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
      If Not TypeOf value Is ObservableCollection(Of String) Then Return False
      Return DirectCast(value, ObservableCollection(Of String)).Count = 0
   End Function

   Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
      Throw New NotImplementedException()
   End Function

End Class