VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "ShapeCreator"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private problemIndex_ As Long
Private frontIndex_ As Long

Private data_ As CDS_StaticHierarchy
Private storage_ As API_ShapeStorage
Private destination_ As Visio.Page

Public Function Init(iData As CDS_StaticHierarchy, iDestination As Visio.Page)
  problemIndex_ = 0
  frontIndex_ = 0
  
  Set data_ = iData
  Set destination_ = iDestination
  Set storage_ = New API_ShapeStorage
  Call storage_.Init(0, iDestination.PageSheet.Cells("PageHeight"))
End Function

Public Function CreateAllShapes() As API_ShapeStorage
  Dim iNode As CDS_NodeSH
  For Each iNode In data_.nodes_
    If iNode.rank_ = 1 Then _
      Call CreateRecursive(iNode)
  Next iNode
  Set CreateAllShapes = storage_
End Function

' ========
Private Function CreateRecursive(target As CDS_NodeSH)
  Call CreateItemShape(target)
  Call storage_.Store(target.data_.shape_)
  Call CSE_ProgressBar.IncrementA
  
  If target.children_.Count = 0 Then _
    Exit Function
    
  Dim iChild As CDS_NodeSH
  Dim iChildren As Collection: Set iChildren = OrderChildren(target)
  For Each iChild In iChildren
    Call CreateRecursive(iChild)
  Next iChild
End Function

Private Function CreateItemShape(ByRef target As CDS_NodeSH)
  Dim iShape As Visio.Shape: Set iShape = destination_.Drop(GetMasterFor(target.data_.type_), 0, 0)
  Set target.data_.shape_ = iShape
  iShape.Text = target.data_.text_
  If target.data_.type_ = T_ITEM_PROBLEM Then
    Call AddProblemPrefix(target, iShape)
    iShape.Shapes(2).CellsU("Width") = iShape.Shapes(2).CellsU("Width")
  ElseIf target.data_.type_ = T_ITEM_FRONT Then
    Call AddFrontPrefix(target, iShape)
    iShape.Shapes(2).CellsU("Width") = iShape.Shapes(2).CellsU("Width")
  Else
    Call CellsSetValue(iShape, CELLSU_POSTYPE, target.data_.position_)
    iShape.CellsU("Width") = iShape.CellsU("Width")
  End If
End Function

Private Function AddProblemPrefix(iNode As CDS_NodeSH, ByRef iShape As Visio.Shape)
  problemIndex_ = problemIndex_ + 1
  Dim sPrefix$: sPrefix = Fmt("{1} {2}. ", PROBLEM_PREFIX, problemIndex_)
  iShape.Text = sPrefix & iShape.Text

  Dim cCursor As Visio.Characters: Set cCursor = iShape.Shapes(2).Characters
  cCursor.Begin = 0
  cCursor.End = VBA.Len(sPrefix)
  cCursor.CharProps(visCharacterStyle) = visBold
End Function

Private Function AddFrontPrefix(iNode As CDS_NodeSH, ByRef iShape As Visio.Shape)
  frontIndex_ = frontIndex_ + 1
  Dim sPrefix$: sPrefix = Fmt("{1} {2}. ", FRONT_PREFIX, frontIndex_)
  iShape.Text = sPrefix & iShape.Text

  Dim cCursor As Visio.Characters: Set cCursor = iShape.Shapes(2).Characters
  cCursor.Begin = 0
  cCursor.End = VBA.Len(sPrefix)
  cCursor.CharProps(visCharacterStyle) = visBold
End Function

Private Function OrderChildren(ByRef target As CDS_NodeSH) As Collection
  If target.data_.type_ = T_ITEM_FRONT Then _
    Set target.children_ = OrderByDescendants(target.children_) ' Note: Modifying graph!!
  Set OrderChildren = target.children_
End Function

Private Function OrderByDescendants(iInput As Collection) As Collection
  Dim iDescCounts As New Scripting.Dictionary
  Dim iOut As New Collection
  
  Dim iNode As CDS_NodeSH
  Dim nDescendants&
  Dim nIndex&
  Dim aKey As Variant
  For Each iNode In iInput
    nDescendants = iNode.descendantsCount_
    If iDescCounts.Count = 0 Then
      Call iOut.Add(iNode, CStr(iNode.id_))
      Call iDescCounts.Add(iNode.id_, nDescendants)
    Else
      nIndex = 1
      For Each aKey In iDescCounts
        If iDescCounts(aKey) <= nDescendants Then _
          nIndex = nIndex + 1
      Next aKey
      
      Call iDescCounts.Add(iNode.id_, nDescendants)
      
      If nIndex > iOut.Count Then
        Call iOut.Add(iNode, CStr(iNode.id_))
      Else
        Call iOut.Add(iNode, CStr(iNode.id_), Before:=nIndex)
      End If
    End If
  Next iNode
  
  Set OrderByDescendants = iOut
End Function
