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_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_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

#Region "Messages"

   Public ReadOnly Property Id_Msg As String
      Get
         Return EgtMsg(12001)
      End Get
   End Property

   Public ReadOnly Property FileName_Msg As String
      Get
         Return EgtMsg(12002)
      End Get
   End Property

   Public ReadOnly Property Table_Msg As String
      Get
         Return EgtMsg(12003)
      End Get
   End Property

   Public ReadOnly Property FileDimension_Msg As String
      Get
         Return EgtMsg(12004)
      End Get
   End Property

   Public ReadOnly Property State_Msg As String
      Get
         Return EgtMsg(12005)
      End Get
   End Property

   Public ReadOnly Property MachiningStart_Msg As String
      Get
         Return EgtMsg(12006)
      End Get
   End Property

   Public ReadOnly Property MachiningEnd_Msg As String
      Get
         Return EgtMsg(12007)
      End Get
   End Property

   Public ReadOnly Property MachiningTime_Msg As String
      Get
         Return EgtMsg(12008)
      End Get
   End Property

#End Region    ' Messages

#Region "Tooltip"

   Public ReadOnly Property ProduceAll_Tooltip As String
      Get
         Return EgtMsg(12021)
      End Get
   End Property
   Public ReadOnly Property ResetProductionQueue_Tooltip As String
      Get
         Return EgtMsg(12022)
      End Get
   End Property
   Public ReadOnly Property DeleteAll_Tooltip As String
      Get
         Return EgtMsg(12023)
      End Get
   End Property

#End Region    ' Tooltip

   ' 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)
      ResetProductionQueue()
      m_bResetList = False
      m_BackupTimer.Interval = New TimeSpan(0, 0, 1)
      AddHandler m_BackupTimer.Tick, AddressOf BackupTimer_Tick
      m_BackupTimer.Start()

      ' leggo path cartella dei partprogram
      GetPluginPrivateProfileString(S_GENERAL, "PartProgramFolder", "", m_sPartProgramFolderPath)
      If Directory.Exists(m_sPartProgramFolderPath) Then
         ' avvio timer di copia file
         AddHandler m_ReadPartProgramFolderTimer.Tick, AddressOf ReadPartProgramFolderTimer_Tick
         m_ReadPartProgramFolderTimer.Interval = TimeSpan.FromSeconds(1)
         m_ReadPartProgramFolderTimer.Start()
      Else
         MessageBox.Show(EgtMsg(12051), EgtMsg(12041), MessageBoxButton.OK, MessageBoxImage.Error)
      End If
   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 bFileDelete As Boolean = False

         Dim sNewFilePath As String = sPartProgramDirPath & "\" & Path.GetFileName(FileList(nFileIndex))
         If File.Exists(sNewFilePath) Then
            If MessageBox.Show(String.Format(EgtMsg(12055), FileList(0)), EgtMsg(12042), MessageBoxButton.YesNo, MessageBoxImage.Information) = MessageBoxResult.Yes Then
               Try
                  File.Delete(sNewFilePath)
                  bFileDelete = True
               Catch ex As Exception
                  MessageBox.Show(String.Format(EgtMsg(12056), FileList(0)), EgtMsg(12041), MessageBoxButton.YesNo, MessageBoxImage.Error)
               End Try
               If bFileDelete Then
                  Dim bFileOverride As Boolean = False
                  Try
                     File.Move(FileList(nFileIndex), sNewFilePath)
                     bFileOverride = True
                  Catch ex As Exception
                     MessageBox.Show(String.Format(EgtMsg(12052), FileList(0)), EgtMsg(12041), MessageBoxButton.OK, MessageBoxImage.Error)
                  End Try
                  If Not bFileOverride Then
                     ' elimino riga
                     Dim sFileName As String = Path.GetFileName(FileList(nFileIndex))
                     Dim FailedDoor As Door = m_DoorList.FirstOrDefault(Function(x) x.sFileName = Path.GetFileName(sFileName))
                     If Not IsNothing(FailedDoor) Then
                        m_DoorList.Remove(FailedDoor)
                     End If
                  End If
               End If
            End If
         Else
            Try
               File.Move(FileList(nFileIndex), sNewFilePath)
               bFileMove = True
            Catch ex As Exception
               MessageBox.Show(String.Format(EgtMsg(12052), FileList(0)), EgtMsg(12041), MessageBoxButton.OK, MessageBoxImage.Error)
            End Try
         End If
         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))
         Else
            Try
               File.Delete(FileList(nFileIndex))
            Catch ex As Exception
            End Try
         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

   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

#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(CopyDoor)
      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 AndAlso m_DoorList(nOldIndex - 1).nState <> Door.PartProgramStates.NOTPRODUCE) 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 AndAlso CurrDoor.nState <> Door.PartProgramStates.NOTPRODUCE) 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) OrElse CurrDoor.nState = PartProgramStates.READY_FOR_PRODUCTION OrElse CurrDoor.nState = PartProgramStates.SENT_TO_NC OrElse CurrDoor.nState = PartProgramStates.MACHINE_START 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(EgtMsg(12053), EgtMsg(12042), 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(CurrDoor)
         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 MessageBox.Show(EgtMsg(12054), EgtMsg(12042), 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()
      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 DoorToProduce.nState <> PartProgramStates.NULL Then Return
      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 OrElse x.nState = Door.PartProgramStates.NOTPRODUCE))
      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.READY_FOR_PRODUCTION AndAlso CurrDoor.nState <> Door.PartProgramStates.SENT_TO_NC AndAlso 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 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
   Public ReadOnly Property sState As String
      Get
         Select Case m_nState
            Case PartProgramStates.NULL
               Return EgtMsg(12031)
            Case PartProgramStates.READY_FOR_PRODUCTION
               Return EgtMsg(12032)
            Case PartProgramStates.SENT_TO_NC
               Return EgtMsg(12033)
            Case PartProgramStates.MACHINE_START
               Return EgtMsg(12034)
            Case PartProgramStates.PRODUCED
               Return EgtMsg(12035)
            Case PartProgramStates.NOTPRODUCE
               Return EgtMsg(12036)
            Case Else
               Return EgtMsg(12037)
         End Select
      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(sState))
         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

   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)
         Dim Time As TimeSpan = m_dtMachiningEnd - m_dtMachiningStart
         Return String.Format("{0}:{1:00}:{2:00}", (Time.Days * 24 + Time.Hours), Time.Minutes, Time.Seconds + 1)
      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

   ' Definizione comandi
   Private m_cmdNotProduce 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
      m_nState = JsonDoor.nState
      m_dtMachiningStart = JsonDoor.dtMachiningStart
      m_dtMachiningEnd = JsonDoor.dtMachiningEnd
   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()
      If nState = Door.PartProgramStates.PRODUCED Then Return
      SetState(If(nState = Door.PartProgramStates.NULL OrElse nState = Door.PartProgramStates.MACHINE_START, Door.PartProgramStates.NOTPRODUCE, Door.PartProgramStates.NULL))
   End Sub

#End Region ' NotProduceDoor

#End Region    ' COMMANDS

End Class