Imports System.Collections.ObjectModel
Imports System.IO
Imports EgtUILib
Imports EgtWPFLib5
Imports EgtBEAMWALL.Core
Imports MS.Internal
Imports Org.BouncyCastle.X509
Imports Ionic.Zip
Imports Org.BouncyCastle.Bcpg
Imports Microsoft.VisualBasic.ApplicationServices
Imports Renci.SshNet.Security

Public Class ConfigurationPageVM
   Inherits VMBase

   Public Enum ConfigSubPages As Integer
      GENERAL = 0
      QPARAMETERS = 1
      MACHINE = 2
   End Enum

   Private m_QParametersModified As Boolean = False
   Friend ReadOnly Property QParametersModified As Boolean
      Get
         Return m_QParametersModified
      End Get
   End Property
   Friend Sub ResetQParametersModified()
      m_QParametersModified = False
   End Sub

   Private m_SelConfigSubPage As ConfigSubPages = ConfigSubPages.GENERAL
   Public Property SelConfigSubPage As Integer
      Get
         Return m_SelConfigSubPage
      End Get
      Set(value As Integer)
         m_SelConfigSubPage = value
      End Set
   End Property

   ' flag modifica parametri Macchina
   Friend bModifyMachParam As Boolean


   Public ReadOnly Property MachinePanelVM As MachinePanelVM
      Get
         Return Map.refMachinePanelVM
      End Get
   End Property

   Private m_ConfigMachTableList As New ObservableCollection(Of MachTable)
   Public Property ConfigMachTableList As ObservableCollection(Of MachTable)
      Get
         Return m_ConfigMachTableList
      End Get
      Set(value As ObservableCollection(Of MachTable))
         m_ConfigMachTableList = value
      End Set
   End Property

   Public ReadOnly Property Custom_Visibility As Visibility
      Get
         If QBTLParamVMList(0).bIsP Then
            Return Visibility.Collapsed
         Else
            Return Visibility.Visible
         End If
      End Get
   End Property

   ' lista dei parametri Q di un Process
   Private m_QBTLParamVMList_View As CollectionView = Nothing
   Protected m_QBTLParamVMList As New ObservableCollection(Of QBTLParamVM)
   Public Property QBTLParamVMList As ObservableCollection(Of QBTLParamVM)
      Get
         Return m_QBTLParamVMList
      End Get
      Set(value As ObservableCollection(Of QBTLParamVM))
         m_QBTLParamVMList = value
      End Set
   End Property

   Private m_PRCList As New ObservableCollection(Of PRC)
   Public ReadOnly Property PRCList As ObservableCollection(Of PRC)
      Get
         Return m_PRCList
      End Get
   End Property

   Private m_LanguageList As New ObservableCollection(Of Language)
   Public ReadOnly Property LanguageList As ObservableCollection(Of Language)
      Get
         Return m_LanguageList
      End Get
   End Property

   Private m_UnitMeasureList As List(Of String) = New List(Of String)({"inch", "mm"})
   Public ReadOnly Property UnitMeasureList As List(Of String)
      Get
         Return m_UnitMeasureList
      End Get
   End Property

   Private m_SelectedLanguage As Language
   Public Property SelectedLanguage As Language
      Get
         Return m_SelectedLanguage
      End Get
      Set(value As Language)
         If value IsNot m_SelectedLanguage Then
            m_SelectedLanguage = value
         End If
      End Set
   End Property

   Public Property SelMeasureUnit As Integer
      Get
         Return If(EgtUiUnitsAreMM(), MeasureUnitOpt.MM, MeasureUnitOpt.INCH)
      End Get
      Set(value As Integer)
         ' salvo unità di misura precedente
         Dim PrevMeasureUnit As MeasureUnitOpt = If(EgtUiUnitsAreMM(), MeasureUnitOpt.MM, MeasureUnitOpt.INCH)
         If value <> PrevMeasureUnit Then
            ' cambio unità di misura
            EgtSetUiUnits(DirectCast(value, MeasureUnitOpt) = MeasureUnitOpt.MM)
         End If
      End Set
   End Property

   Private m_SectionTime As Double
   Public Property SectionTime As String
      Get
         Return DoubleToString(m_SectionTime, 5)
      End Get
      Set(value As String)
         Dim dVal As Double = 0
         If StringToDoubleAdv(value, dVal, True) AndAlso dVal > 0 Then
            m_SectionTime = dVal
         End If
      End Set
   End Property

   Private m_PartTime As Double
   Public Property PartTime As String
      Get
         Return DoubleToString(m_PartTime, 5)
      End Get
      Set(value As String)
         Dim dVal As Double = 0
         If StringToDoubleAdv(value, dVal, True) AndAlso dVal > 0 Then
            m_PartTime = dVal
         End If
      End Set
   End Property

   Private m_bPrintLabel_IsChecked As Boolean
   Public Property bPrintLabel_IsChecked As String
      Get
         Return m_bPrintLabel_IsChecked
      End Get
      Set(value As String)
         m_bPrintLabel_IsChecked = value
      End Set
   End Property

   Private m_bBackupRunning As Boolean = False
   Public ReadOnly Property bBackupRunning As Boolean
      Get
         Return m_bBackupRunning
      End Get
   End Property

   Private m_bRestoreRunning As Boolean = False
   Public ReadOnly Property bRestoreRunning As Boolean
      Get
         Return m_bRestoreRunning
      End Get
   End Property

   Private m_bExternalBackupActive As Boolean
   Public Property bExternalBackupActive As Boolean
      Get
         Return m_bExternalBackupActive
      End Get
      Set(value As Boolean)
         m_bExternalBackupActive = value
         WriteMainPrivateProfileString(S_BACKUPANDRESTORE, K_EXTERNALBACKUPACTIVE, If(m_bExternalBackupActive, 1, 0))
      End Set
   End Property

   Private m_ExternalBackupFolderPath As String
   Public Property ExternalBackupFolderPath As String
      Get
         Return m_ExternalBackupFolderPath
      End Get
      Set(value As String)
         m_ExternalBackupFolderPath = value
         WriteMainPrivateProfileString(S_BACKUPANDRESTORE, K_EXTERNALFILEPATH, m_ExternalBackupFolderPath)
      End Set
   End Property

   Private m_ReminderList As New List(Of IdNameStruct)({New IdNameStruct(0, EgtMsg(61988)), New IdNameStruct(1, EgtMsg(61989)), New IdNameStruct(2, EgtMsg(61990)), New IdNameStruct(7, EgtMsg(61991)), New IdNameStruct(14, EgtMsg(61992)), New IdNameStruct(30, EgtMsg(61993))})
   Public ReadOnly Property ReminderList As List(Of IdNameStruct)
      Get
         Return m_ReminderList
      End Get
   End Property

   Private m_SelReminder As IdNameStruct
   Public Property SelReminder As IdNameStruct
      Get
         Return m_SelReminder
      End Get
      Set(value As IdNameStruct)
         m_SelReminder = value
         WriteMainPrivateProfileString(S_BACKUPANDRESTORE, K_REMINDERFREQUENCY, m_SelReminder.Id)
      End Set
   End Property

   ' Definizione comandi
   Private m_cmdSave As ICommand
   Private m_cmdChooseExternalBackupFolderPath As ICommand
   Private m_cmdBackup As ICommand
   Private m_cmdRestore As ICommand

#Region "Messages"

   Public ReadOnly Property L_Msg As String
      Get
         Return EgtMsg(61803)
      End Get
   End Property

   Public ReadOnly Property T_Msg As String
      Get
         Return EgtMsg(61804)
      End Get
   End Property

   Public ReadOnly Property PRC_Msg As String
      Get
         Return EgtMsg(61805)
      End Get
   End Property

   Public ReadOnly Property Type_Msg As String
      Get
         Return EgtMsg(61850)
      End Get
   End Property

   Public ReadOnly Property NAM_Msg As String
      Get
         Return EgtMsg(61608)
      End Get
   End Property

   Public ReadOnly Property Description_Msg As String
      Get
         Return EgtMsg(61603)
      End Get
   End Property

   Public ReadOnly Property Default_Msg As String
      Get
         Return EgtMsg(61802)
      End Get
   End Property

   Public ReadOnly Property Min_Msg As String
      Get
         Return EgtMsg(61616)
      End Get
   End Property
   Public ReadOnly Property Max_Msg As String
      Get
         Return EgtMsg(61617)
      End Get
   End Property
   Public ReadOnly Property CurrentLanguage_Msg As String
      Get
         Return EgtMsg(6501)
      End Get
   End Property
   Public ReadOnly Property LanguageAdvert_Msg As String
      Get
         Return EgtMsg(6502)
      End Get
   End Property
   Public ReadOnly Property MMUnits_Msg As String
      Get
         Return EgtMsg(6540)
      End Get
   End Property
   Public ReadOnly Property Nesting_Msg As String
      Get
         Return EgtMsg(61829)
      End Get
   End Property
   Public ReadOnly Property SectionTime_Msg As String
      Get
         Return EgtMsg(61806)
      End Get
   End Property
   Public ReadOnly Property PartTime_Msg As String
      Get
         Return EgtMsg(61807)
      End Get
   End Property

   Public ReadOnly Property DisplayIndex_Msg As String
      Get
         Return EgtMsg(61842)
      End Get
   End Property

   Public ReadOnly Property ColumnName_Msg As String
      Get
         Return EgtMsg(61843)
      End Get
   End Property

   Public ReadOnly Property CanUserReorder_Msg As String
      Get
         Return EgtMsg(61844)
      End Get
   End Property

   Public ReadOnly Property CanUserResize_Msg As String
      Get
         Return EgtMsg(61845)
      End Get
   End Property

   Public ReadOnly Property CanUserSort_Msg As String
      Get
         Return EgtMsg(61846)
      End Get
   End Property

   Public ReadOnly Property IsReadOnly_Msg As String
      Get
         Return EgtMsg(61847)
      End Get
   End Property

   Public ReadOnly Property Visible_Msg As String
      Get
         Return EgtMsg(61848)
      End Get
   End Property

   Public ReadOnly Property PrintLabel_Msg As String
      Get
         Return EgtMsg(61894)
      End Get
   End Property

#End Region    ' Messages

#Region "Constructor"

   Sub New()
      ' imposto riferimento in Map
      Map.SetRefConfigurationPageVM(Me)
      ' Leggo nome lingua corrente
      Dim sLanguage As String = String.Empty
      GetMainPrivateProfileString(S_GENERAL, K_MESSAGES, "", sLanguage)
      ' Leggo elenco lingue disponibili da file ini e imposto lingua corrente
      Dim nIndex As Integer = 1
      While True
         Dim ReadLanguage As Language = GetMainPrivateProfileLanguage(S_LANGUAGES, K_LANGUAGE & nIndex)
         If IsNothing(ReadLanguage) Then Exit While
         m_LanguageList.Add(ReadLanguage)
         If String.Compare(ReadLanguage.Name, sLanguage, True) = 0 Then
            m_SelectedLanguage = ReadLanguage
         End If
         nIndex += 1
      End While
      ' Inizializzo la lingua corrente
      m_SelectedLanguage = m_LanguageList(0)
      Dim sMsgName As String = String.Empty
      IniFile.GetMainPrivateProfileString(S_GENERAL, K_MESSAGES, "", sMsgName)
      For Each Language In m_LanguageList
         If Language.Name = sMsgName Then
            m_SelectedLanguage = Language
            Exit For
         End If
      Next
      ' leggo SectionTime e PartTime
      GetMainPrivateProfileString(S_NEST, K_SECTIONTIME, "", SectionTime)
      GetMainPrivateProfileString(S_NEST, K_PARTTIME, "", PartTime)
      m_bPrintLabel_IsChecked = (GetMainPrivateProfileInt(S_PRINTER, K_ENABLE, 0) <> 0)
      ' assegno le liste dei parametri della macchina corrente alla ConfigMachTableList alla pagina di Configurazione
      ConfigMachTableList = CurrentMachine.MachTableList
      ' carico i parametri Q dei Process letti dall'ini
      GetQParamsList()
      ' carico la lista di tutti i parametri Q ciclando su ogni Process di PRCList
      If Not IsNothing(QBTLParamVMList) Then
         QBTLParamVMList.Clear()
      End If
      For Each PRCItem In PRCList
         For Each QBTLParam In PRCItem.QBTLParamVMList
            QBTLParamVMList.Add(QBTLParam)
         Next
      Next
      NotifyPropertyChanged(NameOf(QBTLParamVMList))
      ' setto il grouping a 2 livelli (GRP e, sotto, PRC col nome completo) per la DataGrid
      m_QBTLParamVMList_View = CollectionViewSource.GetDefaultView(m_QBTLParamVMList)
      m_QBTLParamVMList_View.GroupDescriptions.Add(New PropertyGroupDescription(NameOf(QBTLParamVM.GroupType)))
      m_QBTLParamVMList_View.GroupDescriptions.Add(New PropertyGroupDescription(NameOf(QBTLParamVM.ghDesc)))
      ' leggo dati per backup
      GetMainPrivateProfileString(S_BACKUPANDRESTORE, K_EXTERNALFILEPATH, "", m_ExternalBackupFolderPath)
      Dim nDefaultReminderFrequency As Integer = GetMainPrivateProfileInt(S_BACKUPANDRESTORE, K_REMINDERFREQUENCY, 1)
      SelReminder = m_ReminderList.FirstOrDefault(Function(x) x.Id = nDefaultReminderFrequency)
      Dim nExternalBackupActive As Integer = GetMainPrivateProfileInt(S_BACKUPANDRESTORE, K_EXTERNALBACKUPACTIVE, 0)
      m_bExternalBackupActive = nExternalBackupActive > 0
      NotifyPropertyChanged(NameOf(bExternalBackupActive))
   End Sub

#End Region ' Constructor

#Region "COMMANDS"

#Region "SaveCommand"

   ''' <summary>
   ''' Returns a command that do Save.
   ''' </summary>
   Public ReadOnly Property SaveCommand As ICommand
      Get
         If m_cmdSave Is Nothing Then
            m_cmdSave = New Command(AddressOf Save)
         End If
         Return m_cmdSave
      End Get
   End Property

   ''' <summary>
   ''' Execute the Save.  This method is invoked by the SaveCommand.
   ''' </summary>
   Public Sub Save()
      VerifyConfigPageModification()
   End Sub

#End Region ' SaveCommand

#Region "ChooseExternalBackupFolderPath"

   ''' <summary>
   ''' Returns a command that do Save.
   ''' </summary>
   Public ReadOnly Property ChooseExternalBackupFolderPath_Command As ICommand
      Get
         If m_cmdChooseExternalBackupFolderPath Is Nothing Then
            m_cmdChooseExternalBackupFolderPath = New Command(AddressOf ChooseExternalBackupFolderPath)
         End If
         Return m_cmdChooseExternalBackupFolderPath
      End Get
   End Property

   ''' <summary>
   ''' Execute the Save.  This method is invoked by the SaveCommand.
   ''' </summary>
   Public Sub ChooseExternalBackupFolderPath()
      'Dim FileDialog As New System.Windows.Forms.FolderBrowserDialog With {.SelectedPath = m_ExternalBackupFolderPath,
      '                                                                     .Description = "Backup & Restore File Path"}
      Dim FileDialog As New Microsoft.Win32.SaveFileDialog() With {.InitialDirectory = m_ExternalBackupFolderPath,
                                                                   .CheckFileExists = False,
                                                                   .CheckPathExists = False,
                                                                   .DefaultExt = ".folder",
                                                                   .OverwritePrompt = False,
                                                                   .FileName = "Select this ",
                                                                   .Title = "Backup & Restore File Path"}
      If FileDialog.ShowDialog() Then
         m_ExternalBackupFolderPath = Path.GetDirectoryName(FileDialog.FileName)
         WriteMainPrivateProfileString(S_BACKUPANDRESTORE, K_EXTERNALFILEPATH, m_ExternalBackupFolderPath)
         NotifyPropertyChanged(NameOf(ExternalBackupFolderPath))
      End If
   End Sub

#End Region ' ChooseExternalBackupFolderPath

#Region "Backup"

   ''' <summary>
   ''' Returns a command that do Save.
   ''' </summary>
   Public ReadOnly Property Backup_Command As ICommand
      Get
         If m_cmdBackup Is Nothing Then
            m_cmdBackup = New Command(AddressOf BackupCmd)
         End If
         Return m_cmdBackup
      End Get
   End Property

   ''' <summary>
   ''' Execute the Save.  This method is invoked by the SaveCommand.
   ''' </summary>
   Public Sub BackupCmd()
      Backup(True)
   End Sub

   Public Sub Backup(bLoadingWindow As Boolean)
      m_bBackupRunning = True
      ' verifico se esiste cartella backup, altrimenti la creo
      Dim sBackupFolder As String = Map.refMainWindowVM.MainWindowM.sDataDir & "\Backup"
      If Directory.Exists(sBackupFolder) Then
         ' verifico se devo cancellare backup vecchi
         Dim nBackupCount As Integer = 0
         Dim YearDirList() As String = Directory.GetDirectories(sBackupFolder)
         For YearIndex = YearDirList.Length - 1 To 0 Step -1
            Dim nYear As Integer = 0
            Integer.TryParse(Path.GetFileName(YearDirList(YearIndex)), nYear)
            Dim sYearDir As String = sBackupFolder & "\" & nYear
            Dim MonthDirList() As String = Directory.GetDirectories(sYearDir)
            For MonthIndex = MonthDirList.Length - 1 To 0 Step -1
               Dim nMonth As Integer = 0
               Integer.TryParse(Path.GetFileName(MonthDirList(MonthIndex)), nMonth)
               Dim sMonthDir As String = sYearDir & "\" & nMonth
               Dim DayDirList() As String = Directory.GetDirectories(sMonthDir)
               For DayIndex = DayDirList.Length - 1 To 0 Step -1
                  Dim nDay As Integer = 0
                  Integer.TryParse(Path.GetFileName(DayDirList(DayIndex)), nDay)
                  If nBackupCount > 3 Then
                     Directory.Delete(sBackupFolder & "\" & nYear & "\" & nMonth & "\" & nDay, True)
                  End If
               Next
               DayDirList = Directory.GetDirectories(sMonthDir)
               If IsNothing(DayDirList) OrElse DayDirList.Length = 0 Then
                  Directory.Delete(sMonthDir)
               End If
            Next
            MonthDirList = Directory.GetDirectories(sYearDir)
            If IsNothing(MonthDirList) OrElse MonthDirList.Length = 0 Then
               Directory.Delete(sYearDir)
            End If
         Next
      Else
         Directory.CreateDirectory(sBackupFolder)
      End If
      sBackupFolder &= "\" & DateTime.Now().Year
      If Not Directory.Exists(sBackupFolder) Then
         Directory.CreateDirectory(sBackupFolder)
      End If
      sBackupFolder &= "\" & DateTime.Now().Month
      If Not Directory.Exists(sBackupFolder) Then
         Directory.CreateDirectory(sBackupFolder)
      End If
      sBackupFolder &= "\" & DateTime.Now().Day
      If Not Directory.Exists(sBackupFolder) Then
         Directory.CreateDirectory(sBackupFolder)
      End If
      sBackupFolder &= "\" & DateTime.Now().ToString("yyyyMMddHHmmss") & ".bwbck"
      Dim sBackupZipPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\DbBackup.zip"
      ' apro finestra di caricamento
      If bLoadingWindow Then LoadingWndHelper.OpenLoadingWnd(ActiveIds.BACKUP, 2, EgtMsg(63022), EgtMsg(63023), 15)           ' Backup     ' Database backup
      Dim bOk As Boolean = True
      ' backup del Db corrente
      Try
         DataLayer.DbConfig.DataBaseDumpToFile(sBackupZipPath)
      Catch ex As Exception
         MessageBox.Show(EgtMsg(62103), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)
         bOk = False
      End Try
      If bOk AndAlso File.Exists(sBackupZipPath) Then
         If bLoadingWindow Then LoadingWndHelper.UpdateLoadingWnd(ActiveIds.BACKUP, 2, EgtMsg(63024), 15, 100)        ' Projects backup
         ' backup dei progetti correnti
         Try
            Using zip As New Ionic.Zip.ZipFile(sBackupFolder, Console.Out)
               zip.AlternateEncodingUsage = ZipOption.Always
               zip.AlternateEncoding = Text.Encoding.UTF8
               zip.CompressionMethod = CompressionMethod.None
               ' aggiungo Db
               zip.AddItem(sBackupZipPath, "")
               ' aggiungo cartella progetti
               Dim sProjectFolder As String = Map.refMainWindowVM.MainWindowM.sDataDir & "\"
               Dim sKey As String = ""
               EgtGetKeyInfo(sKey)
               sKey = sKey.Replace(" ", "")
               Dim sUserFolder As String = ""
               If Not IsNothing(sKey) AndAlso sKey.Length > 11 Then
                  sUserFolder = sKey.Substring(3, 6)
                  sProjectFolder &= sUserFolder
               End If
               zip.AddItem(sProjectFolder, sUserFolder)
               ' salvo lo zip
               zip.Save()
            End Using
         Catch ex As Exception
            MessageBox.Show(EgtMsg(62103), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)
            bOk = False
         End Try
      End If
      ' se esiste zip del Db, lo elimino
      If File.Exists(sBackupZipPath) Then
         File.Delete(sBackupZipPath)
      End If
      ' se richiesto faccio copia su external source
      If m_bExternalBackupActive Then
         If Directory.Exists(m_ExternalBackupFolderPath) Then
            CopyForExternalBackup(sBackupFolder, m_ExternalBackupFolderPath)
         Else
            MessageBox.Show(EgtMsg(62104), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)                      ' External folder does not exist or is not reachable! Copy to external folder impossible!
         End If
      End If
      'chiudo finestra di caricamento
      If bLoadingWindow Then LoadingWndHelper.CloseLoadingWnd(ActiveIds.BACKUP)
      m_bBackupRunning = False
   End Sub

   Private Async Sub CopyForExternalBackup(sBackupFile As String, sExternalBackupFolder As String)
      Using SourceStream As FileStream = File.Open(sBackupFile, FileMode.Open)
         Dim sExternalBackupFile As String = sExternalBackupFolder & "\" & Path.GetFileName(sBackupFile)
         Using DestinationStream As FileStream = File.Create(sExternalBackupFile)
            Await SourceStream.CopyToAsync(DestinationStream)
         End Using
      End Using
   End Sub

#End Region ' Backup

#Region "Restore"

   ''' <summary>
   ''' Returns a command that do Save.
   ''' </summary>
   Public ReadOnly Property Restore_Command As ICommand
      Get
         If m_cmdRestore Is Nothing Then
            m_cmdRestore = New Command(AddressOf Restore)
         End If
         Return m_cmdRestore
      End Get
   End Property

   ''' <summary>
   ''' Execute the Save.  This method is invoked by the SaveCommand.
   ''' </summary>
   Public Sub Restore()
      ' recupero processo del supervisore
      Dim sSupervisorName As String = "EgtBEAMWALL.SupervisorR32"
      Dim localProc As Process() = Process.GetProcessesByName(sSupervisorName)
      ' verifico che sia chiuso
      If localProc.Length > 0 Then
         MessageBox.Show(EgtMsg(62105), EgtMsg(15002), MessageBoxButton.OK, MessageBoxImage.Warning)          ' Please verify the machine is stoped and close the supervisor before proceding with the restore operation!
         Return
      End If
      m_bRestoreRunning = True
      ' verifico se esiste backup
      Dim sBackupFolder As String = Map.refMainWindowVM.MainWindowM.sDataDir & "\Backup"
      Dim nYear As Integer = 0
      Dim nMonth As Integer = 0
      Dim nDay As Integer = 0
      Dim sRestorePath As String = ""
      If Directory.Exists(sBackupFolder) Then
         Dim YearDirList() As String = Directory.GetDirectories(sBackupFolder)
         For YearIndex = YearDirList.Length - 1 To 0 Step -1
            nYear = 0
            Integer.TryParse(Path.GetFileName(YearDirList(YearIndex)), nYear)
            Dim sYearDir As String = sBackupFolder & "\" & nYear
            Dim MonthDirList() As String = Directory.GetDirectories(sYearDir)
            For MonthIndex = MonthDirList.Length - 1 To 0 Step -1
               nMonth = 0
               Integer.TryParse(Path.GetFileName(MonthDirList(MonthIndex)), nMonth)
               Dim sMonthDir As String = sYearDir & "\" & nMonth
               Dim DayDirList() As String = Directory.GetDirectories(sMonthDir)
               For DayIndex = DayDirList.Length - 1 To 0 Step -1
                  nDay = 0
                  Integer.TryParse(Path.GetFileName(DayDirList(DayIndex)), nDay)
                  Dim sDayDir As String = sMonthDir & "\" & nDay
                  Dim VersionList() As String = Directory.GetFiles(sDayDir)
                  If VersionList.Length > 0 Then
                     Dim nMaxVersion As Int64 = VersionList.Max(Function(x) Int64.Parse(Path.GetFileNameWithoutExtension(x)))
                     sRestorePath = sDayDir & "\" & nMaxVersion & ".bwbck"
                     Exit For
                  End If
               Next
               If Not String.IsNullOrEmpty(sRestorePath) Then Exit For
            Next
            If Not String.IsNullOrEmpty(sRestorePath) Then Exit For
         Next
      End If
      If String.IsNullOrEmpty(sRestorePath) Then
         MessageBox.Show(EgtMsg(62106), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)            ' Restore impossible! Last backup not found!
      End If
      If MessageBox.Show(String.Format(EgtMsg(62107), nYear, nMonth, nDay), EgtMsg(15002), MessageBoxButton.OKCancel, MessageBoxImage.Warning) <> MessageBoxResult.OK Then               'Are you sure you want to restore the last backup from {0}\{1}\{2}? If you proceed you will lose actual data!
         m_bRestoreRunning = False
         Return
      End If
      Dim bDoBackup = False
      Select Case MessageBox.Show(EgtMsg(62108), EgtMsg(15003), MessageBoxButton.YesNoCancel, MessageBoxImage.Information)                  ' Do you want to do a backup of the current project before proceding with the restore?
         Case MessageBoxResult.Yes
            bDoBackup = True
         Case MessageBoxResult.No
            bDoBackup = False
         Case MessageBoxResult.Cancel
            MessageBox.Show(EgtMsg(62109), EgtMsg(15002), MessageBoxButton.OK, MessageBoxImage.Warning)
            m_bRestoreRunning = False
            Return
      End Select
      ' apro finestra di caricamento
      LoadingWndHelper.OpenLoadingWnd(ActiveIds.RESTORE, 3, EgtMsg(63025), EgtMsg(63026), If(bDoBackup, 40, 10))           ' Restore     ' Database Restore
      If bDoBackup Then
         Backup(False)
      End If
      ' recupero cartella corrente dei progetti 
      Dim sProjectFolder As String = Map.refMainWindowVM.MainWindowM.sDataDir
      Dim sKey As String = ""
      EgtGetKeyInfo(sKey)
      sKey = sKey.Replace(" ", "")
      Dim sUserFolder As String = ""
      If Not IsNothing(sKey) AndAlso sKey.Length > 11 Then
         sUserFolder = sKey.Substring(3, 6)
      End If
      If Directory.Exists(sProjectFolder & "\" & sUserFolder) Then
         ' elimino cartella dei progetti
         Directory.Delete(sProjectFolder & "\" & sUserFolder, True)
      End If
      LoadingWndHelper.UpdateLoadingWnd(ActiveIds.RESTORE, 2, EgtMsg(63027), If(bDoBackup, 40, 10), 80)        ' Restore data extraction
      ' estraggo lo zip nella cartella Temp
      Dim sRestoreDir As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\Restore"
      If Directory.Exists(sRestoreDir) Then
         Directory.Delete(sRestoreDir, True)
      End If
      Directory.CreateDirectory(sRestoreDir)
      Dim bOk As Boolean = True
      ' apro file zip
      Try
         Using zip As New Ionic.Zip.ZipFile(sRestorePath, Console.Out)
            ' estraggo file da zip
            zip.ExtractAll(sRestoreDir)
         End Using
      Catch ex1 As Exception
         EgtOutLog("Error! Impossible importing Recovery file " & sRestorePath & "!")
         EgtOutLog("Exception in zip: " & ex1.ToString())
         MessageBox.Show(EgtMsg(62110), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)             ' Impossible importing Recovery file!
         bOk = False
      End Try
      Dim sBackupTempPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\DbRestore"
      LoadingWndHelper.UpdateLoadingWnd(ActiveIds.RESTORE, 3, EgtMsg(63028), 80, 100)        ' Database restore
      If bOk Then
         ' restore del Db
         Try
            DataLayer.DbConfig.DataBaseRestoreFromFile(sRestoreDir & "\DbBackup.zip", "", sBackupTempPath)
         Catch ex As Exception
            MessageBox.Show(EgtMsg(62111), EgtMsg(15001), MessageBoxButton.OK, MessageBoxImage.Error)                 ' Restore failed! Impossible to restore the backup Database
            bOk = False
         End Try
      End If
      If File.Exists(sRestoreDir & "\DbBackup.zip") Then
         File.Delete(sRestoreDir & "\DbBackup.zip")
      End If
      If bOk Then
         ' sposto il backup al posto giusto
         Directory.Move(sRestoreDir & "\" & sUserFolder, sProjectFolder & "\" & sUserFolder)
      End If
      If Directory.Exists(sRestoreDir) Then
         Directory.Delete(sRestoreDir)
      End If
      LoadingWndHelper.CloseLoadingWnd(ActiveIds.RESTORE)
      m_bRestoreRunning = False
   End Sub

#End Region ' Restore

#End Region    ' COMMANDS

#Region "Methods"

   ' funzione che scrive i parametri modificati sul file INI
   Public Sub WriteMachParams()
      If String.IsNullOrWhiteSpace(CurrentMachine.sMachParamIniFile) OrElse Not File.Exists(CurrentMachine.sMachParamIniFile) Then
         EgtOutLog("Impossible loading Mach parameters: MachData.ini file not found or not readable")
         Return
      End If
      For Each MachTableItem In ConfigMachTableList
         For Each MachParamItem In MachTableItem.MachParamList
            If MachParamItem.IsModified Then
               Dim sMachParamType As String = String.Empty
               Select Case MachParamItem.nType
                  Case MachParamType.DOUBLE_
                     sMachParamType = "d"
                  Case MachParamType.STRING_
                     sMachParamType = "s"
                  Case MachParamType.LENGTH
                     sMachParamType = "l"
               End Select
               Dim MachParamString As String = sMachParamType & "," & MachParamItem.sName & "," & MachParamItem.sValue & "," & MachParamItem.sDescription
               Dim bOk = WriteMachPrivateProfileString(MachParamItem.nParentTable, MachParamItem.nParamIndex, MachParamString)
               If Not bOk Then
                  MessageBox.Show(EgtMsg(61859), EgtMsg(30007))
                  Return
               End If
               MachParamItem.IsModifiedReset()
            End If
         Next
      Next
      ' se macchina di tipo BOTH copio il file appena scritto nella cartella Beam nella cartella Wall o viceversa
      If CurrentMachine.nType = MachineType.BOTH Then
         If CurrentMachine.sMachParamIniFile.Contains("\" & K_BEAM & "\") Then
            File.Copy(CurrentMachine.sMachParamIniFile, CurrentMachine.sMachParamIniFile.Replace("\" & K_BEAM & "\", "\" & K_WALL & "\"), True)
         Else
            File.Copy(CurrentMachine.sMachParamIniFile, CurrentMachine.sMachParamIniFile.Replace("\" & K_WALL & "\", "\" & K_BEAM & "\"), True)
         End If
      End If
   End Sub

   ' funzione che scrive i parametri modificati sul file LUA
   Private Function WriteMachParamsLua() As Boolean
      If String.IsNullOrWhiteSpace(CurrentMachine.sMachParamIniFile) OrElse Not File.Exists(CurrentMachine.sMachParamIniFile) Then
         EgtOutLog("Impossible loading Mach parameters: MachData.ini file not found or not readable")
         Return False
      End If
      ' inizio routine di scrittura
      Dim NewTs3DataFileContent = New List(Of String)
      Dim sMachineStr As String = String.Empty
      For Each MachTableItem In ConfigMachTableList
         NewTs3DataFileContent.Add("local " & MachTableItem.sName & " = {")
         sMachineStr &= " " & MachTableItem.sName & "=" & MachTableItem.sName & ","
         For Each MachParamItem In MachTableItem.MachParamList
            NewTs3DataFileContent.Add("   " & MachParamItem.sName & "=" & MachParamItem.sValue & ",")
         Next
         NewTs3DataFileContent.Last().TrimEnd(",")
         NewTs3DataFileContent.Add("}")
         NewTs3DataFileContent.Add("")
      Next
      NewTs3DataFileContent.Add("local Machine = {" & sMachineStr.TrimEnd(",") & "}")
      NewTs3DataFileContent.Add("return Machine")
      Dim FilePath As String = CurrentMachine.sMachDir & "\" & If(CurrentMachine.nType = MachineType.BEAM, BEAM_DIR, WALL_DIR) & "\" & TS3DATA_FILE
      Dim sBakFilePath As String = Path.ChangeExtension(FilePath, ".bak")
      If File.Exists(sBakFilePath) Then File.Delete(sBakFilePath)
      If File.Exists(FilePath) Then File.Move(FilePath, sBakFilePath)
      File.WriteAllLines(FilePath, NewTs3DataFileContent, Text.Encoding.UTF8)
      ' se macchina di tipo BOTH copio il file appena scritto nella cartella Wall nella cartella Beam
      If CurrentMachine.nType = MachineType.BOTH Then
         Dim DestinationFilePath As String = CurrentMachine.sMachDir & "\" & BEAM_DIR & "\" & TS3DATA_FILE
         sBakFilePath = Path.ChangeExtension(DestinationFilePath, ".bak")
         If File.Exists(sBakFilePath) Then File.Delete(sBakFilePath)
         File.Move(DestinationFilePath, Path.ChangeExtension(DestinationFilePath, ".bak"))
         File.Copy(FilePath, DestinationFilePath, True)
      End If
      Return True
   End Function

   ' funzione che verifica la modifica dei valori in Configurazione e ne chiede il salvataggio
   Friend Sub VerifyConfigPageModification()
      Dim bExitFor = False
      Select Case m_SelConfigSubPage
         Case ConfigSubPages.GENERAL
            ' aggiorno tutti i valori senza verificare modifiche
            WriteMainPrivateProfileString(S_GENERAL, K_MESSAGES, m_SelectedLanguage.Name)
            WriteMainPrivateProfileString(S_SCENE, K_MMUNITS, SelMeasureUnit.ToString())
            WriteMainPrivateProfileString(S_NEST, K_SECTIONTIME, DoubleToString(m_SectionTime, 5))
            WriteMainPrivateProfileString(S_NEST, K_PARTTIME, DoubleToString(m_PartTime, 5))
            WriteMainPrivateProfileString(S_PRINTER, K_ENABLE, If(m_bPrintLabel_IsChecked, 1, 0))
         Case ConfigSubPages.QPARAMETERS
            ' ciclo sui parametri Q
            Dim bAskToSaveDefaultValues As Boolean = False
            For Each PRCItem In PRCList
               For Each QParam In PRCItem.QBTLParamVMList
                  If QParam.bIsModified Then
                     bAskToSaveDefaultValues = True
                     ' esco dai For
                     bExitFor = True
                     Exit For
                  End If
               Next
               If bExitFor Then Exit For
            Next
            ' se nel ciclo è stato trovato almeno un valore modificato chiedo il salvataggio
            If bAskToSaveDefaultValues Then
               If MessageBox.Show(EgtMsg(61861), "", MessageBoxButton.YesNo, MessageBoxImage.Question) = MessageBoxResult.Yes Then
                  ' se si è scelto di salvare i valori li scrivo
                  WriteProcessParams(PRCList.ToList())
                  m_QParametersModified = True
               End If
               ' in ogni caso ricarico i valori (in modo da resettare i flag di modifica)
               GetQParamsList()
               ' carico la lista di tutti i parametri Q ciclando su ogni Process di PRCList
               If Not IsNothing(QBTLParamVMList) Then
                  QBTLParamVMList.Clear()
               End If
               For Each PRCItem In PRCList
                  For Each QBTLParam In PRCItem.QBTLParamVMList
                     QBTLParamVMList.Add(QBTLParam)
                  Next
               Next
               NotifyPropertyChanged(NameOf(QBTLParamVMList))
            End If
         Case ConfigSubPages.MACHINE
            ' ciclo sui parametri Macchina
            For Each MachTableItem In ConfigMachTableList
               For Each MachParamItem In MachTableItem.MachParamList
                  If MachParamItem.IsModified Then
                     If MessageBox.Show(EgtMsg(61860), "", MessageBoxButton.YesNo, MessageBoxImage.Question) = MessageBoxResult.Yes Then
                        ' scrivo i parametri Macchina
                        WriteMachParams()
                        WriteMachParamsLua()
                     Else
                        ' se da non salvare li resetto ed esco dai For
                        CreateMachParams()
                        bExitFor = True
                        Exit For
                     End If
                  End If
               Next
               If bExitFor Then Exit For
            Next
      End Select
   End Sub

   ' funzione che verifica l'inserimento della password quando si edita un parametro Macchina
   Friend Function VerifyConfigPagePassword() As Boolean
      ' Display message, title, and default value.
      Dim InputPwdWndVM As New InputPwdWndVM()
      Dim InputPwdWnd As New InputPwdWndV(Application.Current.MainWindow, InputPwdWndVM)
      If InputPwdWnd.ShowDialog() Then
         ' Lettura PWD dall'INI e confronto
         Dim sPwdIni As String = String.Empty
         If GetMainPrivateProfileString(S_MACH, K_PASSWORD, "", sPwdIni) <> 0 Then
            If sPwdIni = InputPwdWndVM.sPassword Then Return True
         End If
      End If
      Return False
   End Function

   Public Sub GetQParamsList()
      m_PRCList.Clear()
      ' carico lista process
      Dim TempPRCList As New ObservableCollection(Of Integer)
      ' prima cerco i process L (GRPType = 0)
      Dim GRPType As Integer = 0
      GetBeamPrivateProfileProcessList(GRPType, TempPRCList)
      For Each PRC In TempPRCList
         ' leggo gruppi 
         Dim GRPList As New ObservableCollection(Of Integer)
         GetBeamPrivateProfileGRPList(GRPType, PRC, GRPList)
         m_PRCList.Add(New PRC(If(GRPList.Count > 0, GRPList(0), GRPType), PRC, GetBeamPrivateProfileName(GRPType, PRC), CreateProcessParams(GRPType, PRC, False)))
      Next
      ' ora cerco i process T (GRPType = 1)
      GRPType = 1
      GetBeamPrivateProfileProcessList(GRPType, TempPRCList)
      For Each PRC In TempPRCList
         ' leggo gruppi 
         Dim GRPList As New ObservableCollection(Of Integer)
         GetBeamPrivateProfileGRPList(GRPType, PRC, GRPList)
         m_PRCList.Add(New PRC(If(GRPList.Count > 0, GRPList(0), GRPType), PRC, GetBeamPrivateProfileName(GRPType, PRC), CreateProcessParams(GRPType, PRC, False)))
      Next
   End Sub

   ' funzione che crea l'elenco dei parametri P o Q di un Process
   Public Function CreateProcessParams(GRPType As Integer, PRC As Integer, IsP As Boolean)
      Dim ParamIndex As Integer = 1
      Dim TempList As New List(Of QBTLParamVM)
      Dim NewBTLParam As BTLParamM = Nothing
      ' leggo tutti i parametri P o Q del Process
      ParamIndex = 1
      TempList = New List(Of QBTLParamVM)
      If IsP Then
         While BTLIniFile.GetBeamPrivateProfileParam(GRPType, PRC, IsP, ParamIndex, Nothing, NewBTLParam)
            TempList.Add(New QBTLParamVM(NewBTLParam, GRPType, PRC))
            ParamIndex += 1
         End While
      Else
         If CurrentMachine.nType = MachineType.BOTH Then
            ' se macchina di tipo BOTH carico i Q di tipo BEAM e poi di tipo WALL
            While BTLIniFile.GetBeamPrivateProfileParam(GRPType, PRC, IsP, ParamIndex, Nothing, NewBTLParam, MachineType.BEAM)
               TempList.Add(New QBTLParamVM(NewBTLParam, GRPType, PRC))
               ParamIndex += 1
            End While
            ParamIndex = 1
            While BTLIniFile.GetBeamPrivateProfileParam(GRPType, PRC, IsP, ParamIndex, Nothing, NewBTLParam, MachineType.WALL)
               TempList.Add(New QBTLParamVM(NewBTLParam, GRPType, PRC))
               ParamIndex += 1
            End While
         Else
            ' altrimenti carico solo quelli corrispondenti al tipo della macchina selezionata
            While BTLIniFile.GetBeamPrivateProfileParam(GRPType, PRC, IsP, ParamIndex, Nothing, NewBTLParam, CurrentMachine.nType)
               TempList.Add(New QBTLParamVM(NewBTLParam, GRPType, PRC))
               ParamIndex += 1
            End While
         End If
      End If
      Return TempList
   End Function

   ' funzione che scrive l'elenco dei parametri P o Q di un Process
   Public Sub WriteProcessParams(PRCList As List(Of PRC))
      For Each PRCItem In PRCList
         Dim nPRCParamIndex As Integer = 1
         Dim sPrevType As String = String.Empty
         For Each PRCParam In PRCItem.QBTLParamVMList
            If PRCParam.bIsModified Then
               ' costruisco la stringa da scrivere nel parametro 
               Dim sType As String = ""
               If PRCParam.nType = 1 Then
                  sType = "d"
               ElseIf PRCParam.nType = 4 Then
                  sType = "l"
               End If
               If sPrevType <> PRCParam.sBWType Then nPRCParamIndex = 1
               Dim sPRCParam As String = sType & "," & If(PRCParam.bIsP, "P", "Q") & PRCParam.nId.ToString("D2") & "," & PRCParam.sMin & "," & PRCParam.sMax & "," & PRCParam.sDefault & "," & PRCParam.sDescription
               WritePrivateProfileString(CalcBeamPrivateProfileGRP(PRCItem.nGRP) & "." & PRCItem.nPRC, If(PRCParam.bIsP, "P", "Q" & PRCParam.sBWType) & nPRCParamIndex, sPRCParam, m_sBTLIniFile)
               ' resetto il flag per il valore modificato
               PRCParam.bIsModified = False
            End If
            sPrevType = PRCParam.sBWType
            nPRCParamIndex += 1
         Next
      Next
   End Sub

#End Region ' Methods

End Class

Public Class MachParam
   Inherits VMBase

   ' table a cui appartiene il parametro
   Private m_nParentTable As Integer
   Friend ReadOnly Property nParentTable As Integer
      Get
         Return m_nParentTable
      End Get
   End Property

   ' indice del parametro
   Private m_nParamIndex As Integer
   Friend ReadOnly Property nParamIndex As Integer
      Get
         Return m_nParamIndex
      End Get
   End Property

   ' tipo della variabile
   Private m_nType As MachParamType
   Friend ReadOnly Property nType As MachParamType
      Get
         Return m_nType
      End Get
   End Property

   ' parametri da struttura
   Private m_sName As String
   Public Property sName As String
      Get
         Return m_sName
      End Get
      Set(value As String)
         m_sName = value
      End Set
   End Property

   ' parametri da geometria
   Private m_IsModifiedValue As Boolean = False
   Private m_dValue As Double
   Private m_sValue As String
   Public Property sValue As String
      Get
         Select Case nType
            Case MachParamType.DOUBLE_
               Return DoubleToString(m_dValue, 3)
            Case MachParamType.LENGTH
               Return LenToString(m_dValue, 3)
            Case Else      ' stringhe
               Return m_sValue
         End Select
         Return If(nType = MachParamType.LENGTH, LenToString(m_dValue, 3), DoubleToString(m_dValue, 3))
      End Get
      Set(value As String)
         Dim dNewValue As Double
         ' verifico se valore immesso è diverso dall'originale
         If nParentTable > 0 Then
            ' trasformo valori
            Select Case nType
               Case MachParamType.DOUBLE_
                  StringToDoubleAdv(value, dNewValue, True)
                  m_IsModifiedValue = Math.Abs(dNewValue - m_dValue) > EPS_SMALL
               Case MachParamType.LENGTH
                  StringToLenAdv(value, dNewValue, True)
                  m_IsModifiedValue = Math.Abs(dNewValue - m_dValue) > EPS_SMALL
               Case Else
                  m_IsModifiedValue = String.Compare(value, m_sValue) <> 0
            End Select
         End If
         ' se valore immesso è diverso e password non inserita
         If m_IsModifiedValue AndAlso Not Map.refConfigurationPageVM.bModifyMachParam Then
            Map.refConfigurationPageVM.bModifyMachParam = Map.refConfigurationPageVM.VerifyConfigPagePassword()
            If Not Map.refConfigurationPageVM.bModifyMachParam Then
               NotifyPropertyChanged("sValue")
               m_IsModifiedValue = False
               Return
            End If
         End If

         Select Case nType
            Case MachParamType.DOUBLE_, MachParamType.LENGTH
               UpdateParamValue(dNewValue, "")
            Case Else
               UpdateParamValue(0, value)
         End Select
      End Set
   End Property
   Public Property dValue As Double
      Get
         Return m_dValue
      End Get
      Set(value As Double)
         m_dValue = value
         NotifyPropertyChanged("sValue")
      End Set
   End Property

   ' descrizione del parametro
   Private m_sDescription As String
   Public Property sDescription As String
      Get
         Return m_sDescription
      End Get
      Set(value As String)
         m_sDescription = value
      End Set
   End Property

   Private Sub StdInit(nParentTable As Integer, nParamIndex As Integer, Type As MachParamType, sName As String, sDescription As String)
      m_nParentTable = nParentTable
      m_nParamIndex = nParamIndex
      m_nType = Type
      m_sName = sName
      m_sDescription = MsgToString(sDescription)
   End Sub

   ' new per double e length
   Sub New(nParentTable As Integer, nParamIndex As Integer, nType As MachParamType, sParamName As String, dValue As Double, sDescription As String)
      StdInit(nParentTable, nParamIndex, nType, sParamName, sDescription)
      m_dValue = dValue 'sValue = DoubleToString(dValue, 3)
   End Sub

   ' new per stringhe
   Sub New(nParentTable As Integer, nParamIndex As Integer, nType As MachParamType, sParamName As String, sDescription As String)
      StdInit(nParentTable, nParamIndex, nType, sParamName, sDescription)
   End Sub

   ' new per parametro vuoto
   Sub New(nType As MachParamType, nParamIndex As Integer, sParamName As String)
      StdInit(Nothing, nParamIndex, nType, sParamName, "")
   End Sub

   Friend Sub UpdateParamValue(dNewValue As Double, sNewValue As String, Optional bDraw As Boolean = True)
      Select Case nType
         Case MachParamType.DOUBLE_, MachParamType.LENGTH
            m_dValue = dNewValue
         Case MachParamType.STRING_
            m_sValue = sNewValue
      End Select
   End Sub

   Public ReadOnly Property IsModified() As Boolean
      Get
         Return m_IsModifiedValue
      End Get
   End Property

   Public Sub IsModifiedReset()
      m_IsModifiedValue = False
   End Sub

End Class

Public Class MachTable
   Inherits VMBase

   ' nome della table
   Private m_sName As String
   Public Property sName As String
      Get
         Return m_sName
      End Get
      Set(value As String)
         m_sName = value
      End Set
   End Property

   Private m_MachParamList As New ObservableCollection(Of MachParam)
   Public Property MachParamList As ObservableCollection(Of MachParam)
      Get
         Return m_MachParamList
      End Get
      Set(value As ObservableCollection(Of MachParam))
         m_MachParamList = value
      End Set
   End Property

   Sub New(Name As String, ParamList As ObservableCollection(Of MachParam))
      sName = Name
      MachParamList = ParamList
   End Sub

End Class

' Tipo parametro nel file di configurazione Macchina
Public Enum MachParamType As Integer
   DOUBLE_ = 1
   STRING_ = 2
   COMBO = 3
   LENGTH = 4
   CHECKBOX = 5
End Enum
