Imports System.IO
Imports System.Windows.Threading
Imports EgtBEAMWALL.Core
Imports EgtUILib
Imports EgtWPFLib5

Public Class NestingRunningWndVM
   Inherits VMBase

#Region "FIELDS & PROPERTIES"

   Public Enum OperationTypes
      ROTFLIP = 1
      NESTING = 2
      BTLNESTING = 3
   End Enum

   Friend Event m_CloseWindow(bDialogResult As Boolean)

   Private m_OperationType As OperationTypes

   Private m_Waiting_Timer As New DispatcherTimer
   Private m_TotIntervals As Integer = 0
   Private m_Step As Double = 1
   Private m_SectionIndex As Integer

   Private m_SelPartType As Integer
   Private m_SectionProgressList As New List(Of SectionProgress)
   Friend ReadOnly Property SectionProgressList As List(Of SectionProgress)
      Get
         Return m_SectionProgressList
      End Get
   End Property

   Private m_RotFlipList As New List(Of BTLPartVM)
   Friend ReadOnly Property RotFlipList As List(Of BTLPartVM)
      Get
         Return m_RotFlipList
      End Get
   End Property

   Public ReadOnly Property RunningSection As SectionProgress
      Get
         Dim CurrRunningSection As SectionProgress = m_SectionProgressList.FirstOrDefault(Function(x) x.nState <> SectionProgress.NestStates.DONE)
         Return If(Not IsNothing(CurrRunningSection), CurrRunningSection, Nothing)
      End Get
   End Property

   Private m_bLDIntersOther As Boolean
   Private m_nMinScore As Integer

   Private m_CurrProgress_Value As Double = 0
   Public Property CurrProgress_Value As Double
      Get
         Return m_CurrProgress_Value
      End Get
      Set(value As Double)
         m_CurrProgress_Value = value
      End Set
   End Property

   Private m_TotProgress_Value As Double = 0
   Public Property TotProgress_Value As Double
      Get
         Return Math.Floor(m_TotProgress_Value)
      End Get
      Set(value As Double)
         m_TotProgress_Value = value
      End Set
   End Property

   ' tempo parziale e totale del segmento corrente
   Friend Shared m_CurrState As SectionProgress.NestStates
   Friend Shared m_CurrTime As Double
   Friend Shared m_TotTime As Double
   ' indice avanzamento RotFlip
   Friend Shared m_RotFlipIndex As Integer = 0

#Region "Messages"

   Public ReadOnly Property Section_Msg As String
      Get
         Return If(m_SectionProgressList.Count() > 0, "Section: " & m_SectionProgressList(m_SectionIndex).Section.sSectionXMaterial & "(" & m_SectionIndex + 1 & "/" & m_SectionProgressList.Count() & ")", "")
      End Get
   End Property

   Public ReadOnly Property Nesting_Msg As String
      Get
         Select Case m_CurrState
            Case SectionProgress.NestStates.CALCORIENTATION
               Return "Calculating wall orientation..."
            Case SectionProgress.NestStates.ADDPART
               Return "Calculating wall outlines..."
            Case SectionProgress.NestStates.NEST
               Return "Nesting walls in rawparts..."
            Case SectionProgress.NestStates.CALCRAWPART
               Return "Generating rawparts with nested parts..."
            Case Else
               Return ""
         End Select
      End Get
   End Property

   Public ReadOnly Property TotProgress_Msg As String
      Get
         Return m_TotProgress_Value.ToString("0.#") & "%"
      End Get
   End Property

#End Region    ' Messages

   ' Definizione comandi
   Private m_cmdCancel As ICommand

#End Region    ' FIELDS & PROPERTIES

   Sub New(SelPartType As Integer, Optional bLDIntersOther As Boolean = False, Optional nMinScore As Integer = 0)
      m_OperationType = OperationTypes.NESTING
      m_SelPartType = SelPartType
      m_bLDIntersOther = bLDIntersOther
      m_nMinScore = nMinScore
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\NestLog.txt"
      Dim dSectionTime As Double = GetMainPrivateProfileDouble(S_NEST, K_SECTIONTIME, 1)
      Dim dPartTime As Double = GetMainPrivateProfileDouble(S_NEST, K_PARTTIME, 1)
      SectionProgress.SetPartSectionTimes(dPartTime, dSectionTime)
      ' disabilito interfaccia
      Map.refProjectVM.SetCalcRunning(True)
      ' se ho selezionato nest per materiale e c'è una sezione selezionata diversa da sezione nulla
      If SelPartType = OptimizePanelVM.PartType.MATERIAL AndAlso Not IsNothing(Map.refProjectVM.BTLStructureVM.SelSection) AndAlso Map.refProjectVM.BTLStructureVM.SelSection.dH <> -1 Then
         m_SectionProgressList.Add(New SectionProgress(Map.refProjectVM.BTLStructureVM.SelSection))
      Else
         ' altrimenti prendo tutte le sezioni
         For Each CurrSection In Map.refProjectVM.BTLStructureVM.SectionList
            If CurrSection.dH = -1 Then Continue For
            m_SectionProgressList.Add(New SectionProgress(CurrSection))
         Next
      End If
      ' imposto contatore
      Select Case m_OperationType
         Case OperationTypes.ROTFLIP
            m_Waiting_Timer.Interval = TimeSpan.FromMilliseconds(70)
         Case OperationTypes.NESTING
            m_Waiting_Timer.Interval = TimeSpan.FromMilliseconds(700)
      End Select
      AddHandler m_Waiting_Timer.Tick, AddressOf WaitingTimer_Tick
   End Sub

   Sub New(SelPartType As Integer)
      m_OperationType = OperationTypes.BTLNESTING
      m_SelPartType = SelPartType
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\BTLNestLog.txt"
      ' disabilito interfaccia
      Map.refProjectVM.SetCalcRunning(True)
      ' se ho selezionato nest per materiale e c'è una sezione selezionata diversa da sezione nulla
      If SelPartType = OptimizePanelVM.PartType.MATERIAL AndAlso Not IsNothing(Map.refProjectVM.BTLStructureVM.SelSection) AndAlso Map.refProjectVM.BTLStructureVM.SelSection.dH <> -1 Then
         m_SectionProgressList.Add(New SectionProgress(Map.refProjectVM.BTLStructureVM.SelSection))
      Else
         ' altrimenti prendo tutte le sezioni
         For Each CurrSection In Map.refProjectVM.BTLStructureVM.SectionList
            If CurrSection.dH = -1 Then Continue For
            m_SectionProgressList.Add(New SectionProgress(CurrSection))
         Next
      End If
      ' imposto contatore
      m_Waiting_Timer.Interval = TimeSpan.FromMilliseconds(70)
      AddHandler m_Waiting_Timer.Tick, AddressOf WaitingTimer_Tick
   End Sub

   Sub New(SelPartType As Integer, BTLPart As BTLPartVM)
      m_OperationType = OperationTypes.ROTFLIP
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\RotFlipLog.txt"
      ' disabilito interfaccia
      Map.refProjectVM.SetCalcRunning(True)
      If Not IsNothing(BTLPart) Then
         m_RotFlipList.Add(BTLPart)
         ' se ho selezionato nest per materiale e c'è una sezione selezionata diversa da sezione nulla
      ElseIf SelPartType = OptimizePanelVM.PartType.MATERIAL AndAlso Not IsNothing(Map.refProjectVM.BTLStructureVM.SelSection) AndAlso Map.refProjectVM.BTLStructureVM.SelSection.dH <> -1 Then
         m_RotFlipList = Map.refProjectVM.BTLStructureVM.BTLPartVMList.Where(Function(x) x.Section = Map.refProjectVM.BTLStructureVM.SelSection).ToList()
      Else
         ' altrimenti prendo tutti i pezzi
         m_RotFlipList = Map.refProjectVM.BTLStructureVM.BTLPartVMList.ToList()
      End If
      ' imposto contatore
      m_Waiting_Timer.Interval = TimeSpan.FromMilliseconds(70)
      AddHandler m_Waiting_Timer.Tick, AddressOf WaitingTimer_Tick
   End Sub

   Private Sub WaitingTimer_Tick()
      Select Case m_OperationType
         Case OperationTypes.ROTFLIP
            m_TotProgress_Value = m_RotFlipIndex / Map.refProjectVM.BTLStructureVM.BTLPartVMList.Count() * 100
         Case OperationTypes.NESTING
            Dim CurrTime As Double = 0
            Dim CurrTotTime As Double = 0
            Dim TotalTime As Double = 0
            For Index = 0 To m_SectionProgressList.Count - 1
               Dim SectionProgress As SectionProgress = m_SectionProgressList(Index)
               If Index < m_SectionIndex Then
                  CurrTime += SectionProgress.dTotSectionTime
               ElseIf Index = m_SectionIndex Then
                  Select Case m_CurrState
                     Case SectionProgress.NestStates.ADDPART
                        CurrTotTime = 100
                        CurrTime += m_CurrTime * SectionProgress.dAddPartTime / 100
                     Case SectionProgress.NestStates.NEST
                        CurrTotTime = 100
                        CurrTime += SectionProgress.dAddPartTime + m_CurrTime * SectionProgress.dCalcNestTime / 100
                     Case SectionProgress.NestStates.CALCRAWPART
                        CurrTotTime = 100
                        CurrTime += SectionProgress.dAddPartTime + SectionProgress.dCalcNestTime + m_CurrTime * SectionProgress.dCalcRawPartTime / 100
                  End Select
               End If
               TotalTime += SectionProgress.dTotSectionTime
            Next
            m_TotProgress_Value = CurrTime / TotalTime * 100
            m_CurrProgress_Value = m_CurrTime
      End Select
      NotifyPropertyChanged(NameOf(CurrProgress_Value))
      NotifyPropertyChanged(NameOf(TotProgress_Value))
      NotifyPropertyChanged(NameOf(Section_Msg))
      NotifyPropertyChanged(NameOf(Nesting_Msg))
      NotifyPropertyChanged(NameOf(TotProgress_Msg))
   End Sub

   Friend Sub StartCalculating()
      Select Case m_OperationType
         Case OperationTypes.ROTFLIP
            DoRotFlip()
         Case OperationTypes.NESTING
            DoNesting()
         Case OperationTypes.BTLNESTING
            DoBtlNesting()
      End Select
   End Sub

   Friend Sub DoNesting()
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\RawPartLog.txt"
      Dim dSectionTime As Double = GetMainPrivateProfileDouble(S_NEST, K_SECTIONTIME, 1)
      Dim dPartTime As Double = GetMainPrivateProfileDouble(S_NEST, K_PARTTIME, 1)
      ' recupero parametri di calcolo
      Dim dRawL As Double = 0
      Dim dRawW As Double = 0
      Dim WhType = GetMainPrivateProfileInt(S_WAREHOUSE, EgtBEAMWALL.Core.ConstIni.K_TYPE, 2)
      Dim sWarehouseIniPath As String = ""
      Dim dOffset As Double = WarehouseHelper.GetOffset(Map.refProjectVM.BTLStructureVM.nPROJTYPE)
      Dim dStartOffset As Double = 0
      Dim dKerf As Double = 0
      Select Case WhType
         Case WarehouseType.BASIC
            Dim nQuantity As Integer = WarehouseHelper.GetQuantity(Map.refProjectVM.BTLStructureVM.nPROJTYPE)
            If Map.refProjectVM.BTLStructureVM.nPROJTYPE = MachineType.BEAM Then
               ' leggo lunghezza barra
               WarehouseHelper.GetCurrentDimensions(Map.refProjectVM.BTLStructureVM.nPROJTYPE, 0, dRawL)
               ' leggo start offset e quantity
               dStartOffset = WarehouseHelper.GetStartOffset()
               ' riporto la stessa lunghezza in tutte le sezioni
               For Each SectionProgress In m_SectionProgressList
                  SectionProgress.SParamList.Add(New SParam(SectionProgress.Section, dRawL, nQuantity, True))
               Next
            ElseIf Map.refProjectVM.BTLStructureVM.nPROJTYPE = MachineType.WALL Then
               If Not WarehouseHelper.GetCurrentDimensions(Map.refProjectVM.BTLStructureVM.nPROJTYPE, dRawW, dRawL) Then
                  ' riabilito interfaccia
                  Map.refProjectVM.SetCalcRunning(False)
                  CloseNesting()
                  Return
               End If
               ' leggo kerf e quantity
               dKerf = WarehouseHelper.GetKerf()
               ' riporto le stesse dimensioni in tutte le sezioni
               For Each SectionProgress In m_SectionProgressList
                  SectionProgress.SParamList.Add(New SParam(SectionProgress.Section, dRawW, dRawL, nQuantity, True))
               Next
            End If
         Case WarehouseType.MEDIUM
            Dim nIndex As Integer = 1
            ' aggiungo le sezioni con diverse lunghezze in base al warehouse
            If Map.refProjectVM.BTLStructureVM.nPROJTYPE = MachineType.BEAM Then
               ' leggo start offset 
               dStartOffset = WarehouseHelper.GetStartOffset()
               ' leggo lunghezza barra e quantity
               WarehouseHelper.GetDimensionsAndQuantityForList(Map.refProjectVM.BTLStructureVM.nPROJTYPE, m_SectionProgressList)
            ElseIf Map.refProjectVM.BTLStructureVM.nPROJTYPE = MachineType.WALL Then
               dKerf = WarehouseHelper.GetKerf()
               If Not WarehouseHelper.GetDimensionsAndQuantityForList(Map.refProjectVM.BTLStructureVM.nPROJTYPE, m_SectionProgressList) Then
                  ' riabilito interfaccia
                  Map.refProjectVM.SetCalcRunning(False)
                  CloseNesting()
                  Return
               End If
            End If
      End Select
      ' calcolo lista dei pezzi per ogni sezione
      For Each SectionProgress In m_SectionProgressList
         'Dim SectionPartList As New List(Of BTLPartM)(Map.refProjectVM.BTLStructureVM.BTLPartVMList.Where(Function(x) x.Section = Section.SectXMat).ToList())
         Dim SectionPartList As List(Of BTLPartM) = (From x In Map.refProjectVM.BTLStructureVM.BTLPartVMList
                                                     Where x.Section = SectionProgress.Section AndAlso x.bDO AndAlso x.nCNT + x.nADDED > x.nINPROD AndAlso
                                                           (m_SelPartType = OptimizePanelVM.PartType.ALL OrElse ((Map.refProjectVM.BTLStructureVM.SelGroup = "" OrElse x.sGROUP = Map.refProjectVM.BTLStructureVM.SelGroup) AndAlso
                                                                                                                 (Map.refProjectVM.BTLStructureVM.SelSubGroup = "" OrElse x.sSTOREY = Map.refProjectVM.BTLStructureVM.SelSubGroup) AndAlso
                                                                                                                 (Map.refProjectVM.BTLStructureVM.SelSearchParam.Id = BTLPartParam.NULL OrElse Map.refProjectVM.BTLStructureVM.SearchText = String.Empty OrElse (
                                                                                                                 (Map.refProjectVM.BTLStructureVM.SelSearchParam.Id = BTLPartParam.PDN AndAlso x.nPDN.ToString.IndexOf(Map.refProjectVM.BTLStructureVM.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0) OrElse
                                                                                                                 (Map.refProjectVM.BTLStructureVM.SelSearchParam.Id = BTLPartParam.NAM AndAlso x.sNAM.IndexOf(Map.refProjectVM.BTLStructureVM.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0)))))
                                                     Select x.BTLPartM).ToList()
         SectionProgress.SetSectionPartList(SectionPartList)
      Next
      ' avvio timer avanzamento nesting
      m_CurrState = SectionProgress.NestStates.NULL
      m_TotTime = 0
      m_CurrTime = 0
      m_Waiting_Timer.Start()
      ' calcolo nesting
      For m_SectionIndex = 0 To m_SectionProgressList.Count - 1
         Dim SectionProgress As SectionProgress = m_SectionProgressList(m_SectionIndex)
         If Map.refProjectVM.BTLStructureVM.nPROJTYPE = BWType.WALL Then
            dOffset = WarehouseHelper.GetWallOffsetByWidth(SectionProgress.Section.dH)
         End If
         ' passo a lua lista id pezzi da nestare
         If SectionProgress.SectionPartList.Count > 0 AndAlso SectionProgress.SParamList.Count > 0 AndAlso
            Not ExecNesting(sLogPath, CurrentMachine.sMachineName, SectionProgress.SectionPartList, SectionProgress.SParamList, dStartOffset, dOffset, dKerf, m_bLDIntersOther, m_nMinScore, SectionProgress.dCalcNestTime) Then
            Exit For
         End If
      Next

      ' update liste grezzi e pezzi della grafica
      Map.refProjectVM.MachGroupPanelVM.RefreshMachGroupList()
      ' seleziono ultimo gruppo
      Map.refProjectVM.MachGroupPanelVM.SelLastMachGroup()
      ' riabilito interfaccia
      Map.refProjectVM.SetCalcRunning(False)
      ' fermo timer e chiudo finestra
      CloseNesting()
   End Sub

   Friend Sub DoBtlNesting()
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\RawPartLog.txt"
      ' verifico se presente layer con RawParts
      Dim nRawPartsLayerId As Integer = EgtGetFirstNameInGroup(GDB_ID.ROOT, "RawParts")
      If nRawPartsLayerId = GDB_ID.NULL Then Return
      ' avvio timer avanzamento nesting
      m_CurrState = SectionProgress.NestStates.NULL
      m_TotTime = 0
      m_CurrTime = 0
      m_Waiting_Timer.Start()
      ' calcolo nesting
      Dim nRawPartId As Integer = EgtGetFirstInGroup(nRawPartsLayerId)
      While nRawPartId <> GDB_ID.NULL
         ' ne recupero il frame
         Dim frRawFrame As New Frame3d
         EgtGetGroupGlobFrame(nRawPartId, frRawFrame)
         ' ne recupero lo spessore
         Dim nBoxLayerId As Integer = EgtGetFirstNameInGroup(nRawPartId, "Box")
         Dim b3Box As New BBox3d
         Dim dRawPartH As Double = 0
         If EgtGetBBoxGlob(nBoxLayerId, GDB_BB.STANDARD, b3Box) Then
            dRawPartH = b3Box.DimZ
         End If
         ' verifico che sia tra quelli da nestare
         If dRawPartH <= 0 Or Not m_SectionProgressList.Any(Function(x) x.Section.dH = dRawPartH) Then Continue While
         ' recupero il contorno
         Dim nOutlineLayerId As Integer = EgtGetFirstNameInGroup(nRawPartId, "Outline")
         Dim nOutlineId As Integer = EgtGetFirstInGroup(nOutlineLayerId)
         Dim bOutlineFound As Boolean = False
         While nOutlineId <> GDB_ID.NULL
            If EgtGetType(nOutlineId) = GDB_TY.CRV_COMPO Then
               bOutlineFound = True
               Exit While
            End If
            nOutlineId = EgtGetNext(nOutlineId)
         End While
         ' verifico se nesting XZ tipo BTL o XY standar
         Dim dRawPartBtlH As Double
         EgtGetInfo( nRawPartId, "H", dRawPartBtlH)
         Dim dRawPartBtlW As Double
         EgtGetInfo( nRawPartId, "W", dRawPartBtlW)
         Dim bRefBtl As Boolean = ( Map.refProjectVM.BTLStructureVM.nPROJTYPE = BWType.WALL AndAlso dRawPartBtlW > dRawPartBtlH)
         ' recupero i pezzi con frame
         Dim PartList As New List(Of NestPartWithFrame)
         Dim nPartPlaceHolderId As Integer = EgtGetFirstInGroup(nRawPartId)
         While nPartPlaceHolderId <> GDB_ID.NULL
            Dim nSourceId As Integer = GDB_ID.NULL
            If Not EgtGetInfo(nPartPlaceHolderId, "!SOU", nSourceId) OrElse nSourceId <= 0 Then
               nPartPlaceHolderId = EgtGetNext(nPartPlaceHolderId)
               Continue While
            End If
            ' creo duplo del pezzo
            Dim nPartDuploId As Integer = EgtDuploNew(nSourceId)
            If nPartDuploId = GDB_ID.NULL Then
               nPartPlaceHolderId = EgtGetNext(nPartPlaceHolderId)
               Continue While
            End If
            ' elimino valori calcolo dell'originale
            MyMachGroupPanelVM.DuploRemoveProjCalc(nPartDuploId)
            ' annullo rotazioni
            Dim b3Duplo As New BBox3d
            EgtGetBBoxGlob(nPartDuploId, GDB_BB.STANDARD, b3Duplo)
            Dim BTLPart As BTLPartVM = CALCPanelVM.GetBTLPartVMFromBTLPartId(nSourceId)
            EgtRotate(nPartDuploId, b3Duplo.Center, Vector3d.X_AX, -BTLPart.nINVERTED)
            EgtRotate(nPartDuploId, b3Duplo.Center, -Vector3d.Z_AX, -BTLPart.nROTATED)
            ' calcolo riferimento
            Dim frPlaceHolder As New Frame3d
            EgtFrame(nPartPlaceHolderId, nRawPartId, frPlaceHolder)
            Dim p3Origin As Point3d
            If bRefBtl Then
               p3Origin = New Point3d(frPlaceHolder.Orig.x, frPlaceHolder.Orig.z, 0)
            Else
               p3Origin = New Point3d(frPlaceHolder.Orig.x, frPlaceHolder.Orig.y, 0)
            End If
            ' lo rendo std
            EgtSetMode(nPartDuploId, GDB_MD.STD)
            PartList.Add(New NestPartWithFrame(nPartPlaceHolderId, p3Origin, frPlaceHolder, nPartDuploId, nSourceId))
            nPartPlaceHolderId = EgtGetNext(nPartPlaceHolderId)
         End While
         ' creo Machgroup
         Dim nCurrMachGroup As Integer = EgtAddMachGroup(NewMachGroupName(), CurrentMachine.sMachineName)
         ' scrivo dati per variabili P di comunicazione con la macchina in gruppo di lavorazione
         EgtSetInfo(nCurrMachGroup, MGR_RPT_PRODID, Map.refProdManagerVM.CurrProd.nProdId)
         EgtSetInfo(nCurrMachGroup, MGR_RPT_PATTID, nCurrMachGroup)
         ' aggiungo le info
         If bOutlineFound Then
            EgtSetInfo(nCurrMachGroup, "BTLNESTING", 1)
            EgtSetInfo(nCurrMachGroup, "RAWOUTLINEID", nOutlineId)
         Else
            ' recupero dati da box

            EgtSetInfo(nCurrMachGroup, MGR_RPT_PANELLEN, b3Box.DimX())
            EgtSetInfo(nCurrMachGroup, MGR_RPT_PANELWIDTH, b3Box.DimY())
         End If
         If PartList.Count > 0 Then
            Dim FirstPart = CALCPanelVM.GetBTLPartVMFromPartId(PartList(0).nSourcePartId)
            If Not IsNothing(FirstPart) Then EgtSetInfo(nCurrMachGroup, MGR_RPT_MATERIAL, FirstPart.sMATERIAL)
         End If
         Dim nPartIndex = 0
         For Each NestPart In PartList
            Dim dRot As Double = 0
            Dim dFlip As Double = 0
            Dim dPosX As Double = NestPart.p3Orig.x
            Dim dPosY As Double = NestPart.p3Orig.y
            Dim b3Duplo As New BBox3d
            Dim nBoxLayId As Integer = EgtGetFirstNameInGroup( NestPart.nPartDuploId, "Box")
            EgtGetBBoxGlob(nBoxLayId, GDB_BB.STANDARD, b3Duplo)
            ' calcolo rot ed invert da frame
            If Map.refProjectVM.BTLStructureVM.nPROJTYPE = BWType.BEAM Then
               If AreSameVectorApprox(NestPart.frFrame.VersX, Vector3d.X_AX) Then
                  dFlip = 0
                  dRot = GetAngle(NestPart.frFrame.VersZ, Vector3d.Z_AX)
               ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, -Vector3d.X_AX) Then
                  dFlip = 180
                  dRot = -GetAngle(NestPart.frFrame.VersZ, Vector3d.Z_AX)
               Else
                  ' errore, la trave deve essere allineata all'asse X
                  EgtOutLog(String.Format("Errore in BTL nesting, pezzo {0} non allineato all'asse X ({1})", NestPart.nPartDuploId, NestPart.frFrame.VersX))
                  Continue For
               End If
               ' faccio rotazioni
               EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.X_AX, dRot)
               EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.Z_AX, dFlip)
            ElseIf Map.refProjectVM.BTLStructureVM.nPROJTYPE = BWType.WALL Then
               If bRefBtl Then
                  Dim vtFrVersX As Vector3d = NestPart.frFrame.VersX
                  Dim vtFrVersY As Vector3d = NestPart.frFrame.VersY
                  If Math.Abs( vtFrVersX.z) > EPS_SMALL Or
                     Math.Abs( vtFrVersX.z) > EPS_SMALL Then
                     vtFrVersX = New Vector3d( vtFrVersX.y, vtFrVersX.z, 0)
                     vtFrVersY = New Vector3d( vtFrVersY.y, vtFrVersY.z, 0)
                  End If
                  If AreSameVectorApprox(vtFrVersX, Vector3d.X_AX) AndAlso
                     AreSameVectorApprox(vtFrVersY, Vector3d.Y_AX) Then
                     dFlip = 0
                     dRot = 0
                     dPosX = dPosX
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(vtFrVersX, -Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, Vector3d.Y_AX) Then
                     dFlip = 0
                     dRot = 180
                     dPosX = dPosX - b3Duplo.DimX()
                     dPosY = dPosY - b3Duplo.DimY()
                  ElseIf AreSameVectorApprox(vtFrVersX, Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, -Vector3d.Y_AX) Then
                     dFlip = 180
                     dRot = 0
                     dPosX = dPosX
                     dPosY = dPosY - b3Duplo.DimY()
                  ElseIf AreSameVectorApprox(vtFrVersX, -Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, -Vector3d.Y_AX) Then
                     dFlip = 180
                     dRot = 180
                     dPosX = dPosX - b3Duplo.DimX()
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(vtFrVersX, Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, Vector3d.X_AX) Then
                     dFlip = 0
                     dRot = -90
                     dPosX = dPosX - b3Duplo.DimY()
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(vtFrVersX, -Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, Vector3d.X_AX) Then
                     dFlip = 0
                     dRot = 90
                     dPosX = dPosX
                     dPosY = dPosY - b3Duplo.DimX()
                  ElseIf AreSameVectorApprox(vtFrVersX, Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, -Vector3d.X_AX) Then
                     dFlip = 180
                     dRot = -90
                     dPosX = dPosX
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(vtFrVersX, -Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(vtFrVersY, -Vector3d.X_AX) Then
                     dFlip = 180
                     dRot = 90
                     dPosX = dPosX - b3Duplo.DimY()
                     dPosY = dPosY - b3Duplo.DimX()
                  Else
                     ' errore, la parete deve stare nel piano del grezzo
                     EgtOutLog(String.Format("Errore in BTL nesting, pezzo {0} non giace nel piano XY ({1})", NestPart.nPartDuploId, NestPart.frFrame.VersZ))
                     Continue For
                  End If
               Else
                  If AreSameVectorApprox(NestPart.frFrame.VersX, Vector3d.X_AX) AndAlso
                     AreSameVectorApprox(NestPart.frFrame.VersY, Vector3d.Y_AX) Then
                     dFlip = 0
                     dRot = 0
                     dPosX = dPosX
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, -Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, Vector3d.Y_AX) Then
                     dFlip = 180
                     dRot = 180
                     dPosX = dPosX - b3Duplo.DimX()
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, -Vector3d.Y_AX) Then
                     dFlip = 180
                     dRot = 0
                     dPosX = dPosX
                     dPosY = dPosY - b3Duplo.DimY()
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, -Vector3d.X_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, -Vector3d.Y_AX) Then
                     dFlip = 0
                     dRot = 180
                     dPosX = dPosX - b3Duplo.DimX()
                     dPosY = dPosY - b3Duplo.DimY()
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, Vector3d.X_AX) Then
                     dFlip = 180
                     dRot = -90
                     dPosX = dPosX
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, -Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, Vector3d.X_AX) Then
                     dFlip = 0
                     dRot = 90
                     dPosX = dPosX
                     dPosY = dPosY - b3Duplo.DimX()
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, -Vector3d.X_AX) Then
                     dFlip = 0
                     dRot = -90
                     dPosX = dPosX - b3Duplo.DimY()
                     dPosY = dPosY
                  ElseIf AreSameVectorApprox(NestPart.frFrame.VersX, -Vector3d.Y_AX) AndAlso
                         AreSameVectorApprox(NestPart.frFrame.VersY, -Vector3d.X_AX) Then
                     dFlip = 180
                     dRot = 90
                     dPosX = dPosX - b3Duplo.DimY()
                     dPosY = dPosY - b3Duplo.DimX()
                  Else
                     ' errore, la parete deve stare nel piano del grezzo
                     EgtOutLog(String.Format("Errore in BTL nesting, pezzo {0} non giace nel piano XY ({1})", NestPart.nPartDuploId, NestPart.frFrame.VersZ))
                     Continue For
                  End If
               End If
               ' Se pannello ribaltato
               Dim bSideOppo As Boolean = (GetMainPrivateProfileInt(S_IMPORT, K_WALLOPPOSITESIDENESTING, 0) <> 0)
               If bSideOppo Then
                  dFlip = ( dFlip + 180) Mod 360
                  Dim bUseX As Boolean
                  If Math.Abs( dRot Mod 180) < EPS_ANG_SMALL Then
                     dRot = ( dRot + 180) Mod 360
                     bUseX = True
                  Else
                     dRot = dRot
                     bUseX = False
                  End If
                  dPosX = -dPosX - If( bUseX, b3Duplo.DimX(), b3Duplo.DimY()) + b3Box.DimX()
                  dPosY = dPosY
               End If
               ' Applico le rotazioni
               EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, Vector3d.X_AX, dFlip)
               EgtRotate(NestPart.nPartDuploId, b3Duplo.Center, -Vector3d.Z_AX, dRot)
            End If
            nPartIndex += 1
            EgtSetInfo(nCurrMachGroup, MGR_RPT_PART & nPartIndex,
                       NestPart.nPartDuploId & "," & DoubleToString(dPosX, 3) & "," & DoubleToString(dPosY,3) & ",0,0")
            ' scrivo dati pezzo
            EgtSetInfo(NestPart.nPartDuploId, MGR_PRT_POSX, dPosX)
            EgtSetInfo(NestPart.nPartDuploId, MGR_PRT_POSY, dPosY)
            EgtSetInfo(NestPart.nPartDuploId, MGR_PRT_ROT, dRot)
            EgtSetInfo(NestPart.nPartDuploId, MGR_PRT_FLIP, dFlip)
         Next
         ' scrivo info per segnalare gruppo nestato e quindi da caricare in grafica
         EgtSetInfo(nCurrMachGroup, "UPDATEUI", 1)
         ' eseguo script creazione grezzo
         If Not ExecWall(sLogPath, Map.refMachinePanelVM.SelectedMachine.Name, CalcIntegration.CmdTypes.RAWPART, False, True) Then
            ' elimino duplo e gruppo di lavorazione
            For Each Duplo In PartList
               EgtErase(Duplo.nPartDuploId)
            Next
            EgtRemoveMachGroup(nCurrMachGroup)
            If File.Exists(sLogPath) Then
               Dim LogFile As String() = File.ReadAllLines(sLogPath)
               If LogFile.Count >= 2 AndAlso Not IsNothing(LogFile(1)) Then
                  MessageBox.Show(LogFile(1), EgtMsg(30007), MessageBoxButton.OK, MessageBoxImage.Error)
               End If
            Else
               MessageBox.Show("Impossible creating rawpart", EgtMsg(30007), MessageBoxButton.OK, MessageBoxImage.Error)
            End If
            ' se nessun grezzo, rimetto tutti i pezzi invisibili
            If Map.refProjectVM.MachGroupPanelVM.MachGroupVMList.Count = 0 Then
               Map.refProjectVM.BTLStructureVM.HideAll(True)
            End If
            Exit While
         End If

         ' passo al prossimo RawPart
         nRawPartId = EgtGetNext(nRawPartId)
      End While
      ' update liste grezzi e pezzi della grafica
      Map.refProjectVM.MachGroupPanelVM.RefreshMachGroupList()
      ' seleziono ultimo gruppo
      Map.refProjectVM.MachGroupPanelVM.SelLastMachGroup()
      ' riabilito interfaccia
      Map.refProjectVM.SetCalcRunning(False)
      ' fermo timer e chiudo finestra
      CloseNesting()
   End Sub

   Private Function NewMachGroupName() As Integer
      Dim nMachGroupId = EgtGetFirstMachGroup()
      If nMachGroupId = GDB_ID.NULL Then Return 1
      Dim nMaxMachGroup = 0
      While nMachGroupId <> GDB_ID.NULL
         Dim sMachGroupName = ""
         Dim nMachGroupName = 0
         EgtGetMachGroupName(nMachGroupId, sMachGroupName)
         Integer.TryParse(sMachGroupName, nMachGroupName)
         If nMachGroupName > nMaxMachGroup Then
            nMaxMachGroup = nMachGroupName
         End If
         nMachGroupId = EgtGetNextMachGroup(nMachGroupId)
      End While
      Return nMaxMachGroup + 1
   End Function

   Private Class NestPartWithFrame

      Private m_nSourcePartId As Integer
      Public ReadOnly Property nSourcePartId As Integer
         Get
            Return m_nSourcePartId
         End Get
      End Property
      Private m_nPartId As Integer
      Public ReadOnly Property nPartId As Integer
         Get
            Return m_nPartId
         End Get
      End Property
      Private m_nPartDuploId As Integer
      Public ReadOnly Property nPartDuploId As Integer
         Get
            Return m_nPartDuploId
         End Get
      End Property
      Private m_p3Orig As Point3d
      Public ReadOnly Property p3Orig As Point3d
         Get
            Return m_p3Orig
         End Get
      End Property
      Private m_frFrame As Frame3d
      Public ReadOnly Property frFrame As Frame3d
         Get
            Return m_frFrame
         End Get
      End Property

      Sub New(nPartId As Integer, p3Orig As Point3d, frFrame As Frame3d, nPartDuploId As Integer, nSourcePartId As Integer)
         m_nPartId = nPartId
         m_p3Orig = p3Orig
         m_frFrame = frFrame
         m_nPartDuploId = nPartDuploId
         m_nSourcePartId = nSourcePartId
      End Sub

   End Class

   Friend Sub DoRotFlip()
      Dim sLogPath As String = Map.refMainWindowVM.MainWindowM.sTempDir & "\RawPartLog.txt"
      ' avvio timer avanzamento nesting
      m_CurrState = SectionProgress.NestStates.CALCORIENTATION
      m_TotTime = 0
      m_CurrTime = 0
      m_RotFlipIndex = 0
      m_Waiting_Timer.Start()
      ' calcolo RotFlip
      For Each BTLPart In m_RotFlipList
         ' eseguo funzione RotFlip
         If Not ExecRotFlip(BTLPart.nPartId) Then
            Exit For
         End If
         ' rileggo parametri Q per mostrare modifiche
         For Each Feature In BTLPart.BTLFeatureVMList
            Feature.BTLFeatureM.ReadQValues()
         Next
         ' incremento indice ProgressBar
         m_RotFlipIndex += 1
         UpdateUI()
      Next
      ' rileggo ROTATED e INVERTED di tutti i pezzi
      Map.refProjectVM.BTLStructureVM.RefreshRotFlip()
      '' aggiorno eventuali Q della feature selezionata
      If Not IsNothing(Map.refProjectVM.BTLStructureVM.SelBTLPart) AndAlso Not IsNothing(Map.refProjectVM.BTLStructureVM.SelBTLPart.SelBTLFeatureVM) Then
         For Each QParam In Map.refProjectVM.BTLStructureVM.SelBTLPart.SelBTLFeatureVM.QBTLParamVMList
            QParam.NotifyPropertyChanged(NameOf(QParam.sValue))
            QParam.NotifyPropertyChanged(NameOf(QParam.bCustom))
         Next
      End If
      'Map.refProjectVM.MachGroupPanelVM.SelLastMachGroup()
      EgtDraw()
      ' riabilito interfaccia
      Map.refProjectVM.SetCalcRunning(False)
      ' fermo timer e chiudo finestra
      CloseNesting()
   End Sub

   Private Sub CloseNesting()
      ' fermo timer e chiudo finestra
      m_Waiting_Timer.Stop()
      RaiseEvent m_CloseWindow(True)
   End Sub

#Region "COMMANDS"

#Region "Cancel"

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

   ''' <summary>
   ''' Execute the Open.  This method is invoked by the OpenCommand.
   ''' </summary>
   Friend Sub Cancel()
      ' chiedo conferma
      If MessageBox.Show("Are you sure you want to stop the nesting?", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning) = MessageBoxResult.No Then Return
      ' fermo il nesting
      Map.refOptimizePanelVM.m_StopNesting = True
      '' fermo timer e chiudo finestra
      'CloseNesting()
   End Sub

#End Region ' Cancel

#End Region    ' COMMANDS

End Class

Public Class SectionProgress

   Public Enum NestStates As Integer
      NULL = 0
      START = 1
      CALCORIENTATION = 3
      ADDRAWPART = -1
      ADDPART = -2
      NEST = 4
      CALCRAWPART = -3
      DONE = -4
   End Enum

   Private Shared m_dSectionTime As Double
   Private Shared m_dPartTime As Double

   Private m_Section As SectionXMaterial
   Public ReadOnly Property Section As SectionXMaterial
      Get
         Return m_Section
      End Get
   End Property

   Private m_SParamList As New List(Of SParam)
   Public ReadOnly Property SParamList As List(Of SParam)
      Get
         Return m_SParamList
      End Get
   End Property

   Private m_SectionPartList As New List(Of BTLPartM)
   Public ReadOnly Property SectionPartList As List(Of BTLPartM)
      Get
         Return m_SectionPartList
      End Get
   End Property

   Private m_nState As NestStates = 0
   Public ReadOnly Property nState As NestStates
      Get
         Return m_nState
      End Get
   End Property
   Public Sub SetState(value As NestStates)
      m_nState = value
   End Sub

   Public ReadOnly Property dTotSectionTime As Double
      Get
         Return If(SectionPartList.Count > 0 AndAlso SParamList.Count > 0, dAddPartTime + dCalcNestTime + dCalcRawPartTime, 0)
      End Get
   End Property

   Public ReadOnly Property dAddPartTime As Double
      Get
         Return If(SectionPartList.Count > 0 AndAlso SParamList.Count > 0, Math.Max(SectionPartList.Count / 5, 2), 0)
      End Get
   End Property

   Public ReadOnly Property dCalcNestTime As Double
      Get
         Return If(SectionPartList.Count > 0 AndAlso SParamList.Count > 0, Math.Max(5, m_dSectionTime) + (m_dPartTime * SectionPartList.Count), 0)

      End Get
   End Property

   Public ReadOnly Property dCalcRawPartTime As Double
      Get
         Return If(SectionPartList.Count > 0 AndAlso SParamList.Count > 0, Math.Max(SectionPartList.Count / 20, 2), 0)
      End Get
   End Property

   Sub New(Section As SectionXMaterial)
      m_Section = Section
   End Sub

   Friend Shared Sub SetPartSectionTimes(PartTime As Double, SectionTime As Double)
      m_dPartTime = PartTime
      m_dSectionTime = SectionTime
   End Sub

   Friend Sub SetSectionPartList(SectionPartList As List(Of BTLPartM))
      m_SectionPartList = SectionPartList
   End Sub

End Class