Imports System.Xml
Imports Inventor

Public Class Form1
    ' Have the user select the xml configuration file.
    Private Sub btnBrowse_Click(sender As Object, e As EventArgs) Handles btnBrowse.Click
        ' Allow the end-user to specify the xml file.
        Dim fd As OpenFileDialog = New OpenFileDialog()
        fd.InitialDirectory = "C:\"
        fd.Filter = "XML File (*.xml)|*.xml"
        fd.FilterIndex = 0
        fd.Multiselect = False
        fd.AddExtension = True
        fd.CheckFileExists = True
        fd.ShowReadOnly = True
        fd.InitialDirectory = System.Windows.Forms.Application.StartupPath
        fd.Title = "Select XML Configuration File"

        If fd.ShowDialog() = DialogResult.OK Then
            Me.txtFilename.Text = fd.FileName
        End If
    End Sub

    Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
        ' Read the configuration information from the xml file.
        Dim configs As New Configurations
        Dim templatePath As String = ""
        Dim resultsPath As String = ""
        If Not ReadConfigsFromXML(Me.txtFilename.Text, templatePath, resultsPath, configs) Then
            Exit Sub
        End If

        Dim inventor As Inventor.Application = Nothing
        If configs.Count > 0 Then
            ' Connect to Inventor, starting it if necessary.
            Try
                inventor = GetObject(, "Inventor.Application")
            Catch ex As Exception
                Try
                    inventor = CreateObject("Inventor.Application")
                    inventor.Visible = True
                Catch ex2 As Exception
                    MsgBox("Unable to start or connect to Inventor.")
                    Exit Sub
                End Try
            End Try
        End If

        ' Begin processing each of the configurations.
        Dim config As Configuration
        For Each config In configs
            Me.lstStatus.Items.Add("Beginning processing of configuration: " & config.Name)
            Me.lstStatus.Items.Add("    Creating new files.")
            Me.lstStatus.TopIndex = Me.lstStatus.Items.Count - 2
            Me.Refresh()

            ' Check to see if the result directory already exists and delete it.
            If System.IO.Directory.Exists(resultsPath & config.Name) Then
                System.IO.Directory.Delete(resultsPath & config.Name, True)
            End If

            ' Create a new directory in the path defined by the results path.  The new
            ' directory is the name of the configuration.
            System.IO.Directory.CreateDirectory(resultsPath & config.Name)

            '*
            '* This portion is very specific to this sample code.  It will need to be 
            '* expanded for more general cases.
            '*

            ' Copy the template part and drawing, renaming them to the configuration name.
            ' This uses apprentice to create the copies so the referencing is also updated.
            Dim apprentice As Inventor.ApprenticeServerComponent = CreateObject("Inventor.ApprenticeServer")
            Dim apprenticeDoc As ApprenticeServerDocument = apprentice.Open(System.IO.Path.Combine(templatePath, "Drawing.idw"))

            Dim fileSaveAs As FileSaveAs = apprentice.FileSaveAs

            ' Add the drawing and the referenced part to the FileSaveAs object, specifying the
            ' new filename.
            fileSaveAs.AddFileToSave(apprenticeDoc, System.IO.Path.Combine(resultsPath, config.Name, config.Name & ".idw"))
            fileSaveAs.AddFileToSave(apprenticeDoc.ReferencedFiles.Item(1), System.IO.Path.Combine(resultsPath, config.Name, config.Name & ".ipt"))

            ' Create the copies.
            fileSaveAs.ExecuteSaveCopyAs()

            fileSaveAs = Nothing
            apprenticeDoc.Close()
            apprenticeDoc = Nothing
            apprentice = Nothing

            Me.lstStatus.Items.Add("    Opening and modifying the part.")
            Me.lstStatus.TopIndex = Me.lstStatus.Items.Count - 2
            Me.Refresh()

            ' Open the part.
            Dim partDoc As PartDocument = Nothing
            partDoc = inventor.Documents.Open(resultsPath & config.Name & "\" & config.Name & ".ipt")

            ' Iterate through the configuration parameters and update the part.
            Dim params As Parameters
            params = partDoc.ComponentDefinition.Parameters

            Dim paramValue As ParameterValue
            For Each paramValue In config
                Dim param As Parameter
                Try
                    param = params.Item(paramValue.Name)
                Catch ex2 As Exception
                    MsgBox("The parameter """ & paramValue.Name & """ does not exist in the part.")
                    Exit Sub
                End Try

                param.Expression = paramValue.Expression
            Next

            ' Update the part.
            partDoc.Update()

            ' Save the part.
            partDoc.Save()

            Me.lstStatus.Items.Add("    Opening and modifying the drawing.")
            Me.lstStatus.TopIndex = Me.lstStatus.Items.Count
            Me.Refresh()

            ' Open the drawing and activate each sheet to allow them to update.
            Dim drawDoc As DrawingDocument
            drawDoc = inventor.Documents.Open(resultsPath & config.Name & "\" & config.Name & ".idw")
            Dim sheet As Sheet = Nothing
            For Each sheet In drawDoc.Sheets
                sheet.Activate()
            Next

            ' Loop until the view has had a chance to fully compute.
            For i As Integer = 1 To 200
                Dim isUpToDate As Boolean = True
                For Each sht As Inventor.Sheet In drawDoc.Sheets
                    For Each view As DrawingView In sht.DrawingViews
                        Debug.Print("View " & view.Name & ": " & view.UpToDate)
                        If Not view.UpToDate Then
                            isUpToDate = False
                        End If
                    Next
                Next

                If isUpToDate Then
                    Exit For
                End If

                My.Application.DoEvents()
                System.Threading.Thread.Sleep(100)
            Next

            ' Save and close the drawing.
            drawDoc.Save()
            drawDoc.Close()

            ' Close the part.
            partDoc.Close()

            Me.lstStatus.Items.Add("    Finished processing.")
            Me.lstStatus.TopIndex = Me.lstStatus.Items.Count - 2
            Me.Refresh()
        Next

        Me.lstStatus.Items.Add("Finished processing xml file.")
        Me.lstStatus.TopIndex = Me.lstStatus.Items.Count - 2
        Me.Refresh()
    End Sub

    ' Read the configuration information from the supplied xml file, checking for problems
    ' with the xml data.
    Private Function ReadConfigsFromXML(ByVal XMLFilename As String, ByRef TemplatePath As String, ByRef ResultsPath As String, ByRef Configs As Configurations) As Boolean
        ' Read the specified xml file.
        Dim xmlDoc As New XmlDocument
        Try
            xmlDoc.Load(XMLFilename)
        Catch ex As Exception
            MsgBox("Could not open the specified xml file.")
            Return False
        End Try

        ' Verify that the "ConfiguratorData" element is the first element in the select xml file.
        If xmlDoc.FirstChild.Name <> "ConfiguratorData" Then
            MsgBox("Invalid xml file specified.  It does not contain the ""ConfiguratorData"" element.")
            Return False
        End If

        ' Read through the nested elements of the ConfiguratorData element.
        Dim xmlNode As XmlNode
        xmlNode = xmlDoc.FirstChild.FirstChild
        Do While Not xmlNode Is Nothing
            ' Look for the "TemplatePath", "ResultsPath", and "Configuration" elements.
            Select Case xmlNode.Name
                Case "TemplatePath"
                    TemplatePath = xmlNode.InnerText

                    ' Make sure the path ends with a back slash.
                    If TemplatePath.Chars(TemplatePath.Length - 1) <> "\" Then
                        TemplatePath = TemplatePath & "\"
                    End If

                    ' Make sure the directory exists.
                    If Not System.IO.Directory.Exists(TemplatePath) Then
                        MsgBox("The specified template path does not exist.")
                        Return False
                    End If

                    ' Verify that the required template files exist.  This sample is
                    ' hardcode for a single drawing and part.
                    If Not System.IO.File.Exists(TemplatePath & "Part.ipt") Then
                        MsgBox("The template file """ & TemplatePath & "Part.ipt"" does not exist.")
                        Return False
                    End If

                    If Not System.IO.File.Exists(TemplatePath & "Drawing.idw") Then
                        MsgBox("The template file """ & TemplatePath & "Drawing.idw"" does not exist.")
                        Return False
                    End If
                Case "ResultsPath"
                    ResultsPath = xmlNode.InnerText

                    ' Make sure the path ends with a back slash.
                    If ResultsPath.Chars(ResultsPath.Length - 1) <> "\" Then
                        ResultsPath = ResultsPath & "\"
                    End If

                    ' Make sure the directory exists.
                    If Not System.IO.Directory.Exists(ResultsPath) Then
                        MsgBox("The specified result path does not exist.")
                        Return False
                    End If
                Case "Configuration"
                    ' Save the configuration name.
                    Dim configName As String
                    Try
                        configName = xmlNode.Attributes.ItemOf("name").Value
                    Catch ex As Exception
                        MsgBox("Attribute ""name"" does not exist for a configuration: " & xmlNode.OuterXml)
                        Return False
                    End Try

                    ' Add this configuration.
                    Dim config As Configuration
                    Try
                        config = Configs.Add(configName)
                    Catch ex As Exception
                        ' If an exception occurred assume it was because of a duplicate configuration name.
                        MsgBox("Duplicate configuration name """ & configName & """.")
                        Return False
                    End Try

                    ' Get the parameter values.
                    Dim paramNode As XmlNode
                    paramNode = xmlNode.FirstChild
                    Do While Not paramNode Is Nothing
                        Dim attribName As String
                        Try
                            attribName = paramNode.Attributes.ItemOf("name").Value()
                        Catch ex As Exception
                            MsgBox("Attribute ""name"" does not exist for a parameter: " & paramNode.OuterXml)
                            Return False
                        End Try

                        Try
                            config.AddParameter(attribName, paramNode.InnerText)
                        Catch ex As Exception
                            ' If an exception occurred assume it was because of a duplicate parameter name.
                            MsgBox("Duplicate parameter name """ & attribName & """ within """ & configName & """.")
                            Return False
                        End Try

                        ' Get the next parameter node.
                        paramNode = paramNode.NextSibling
                    Loop
            End Select

            xmlNode = xmlNode.NextSibling()
        Loop

        Return True
    End Function

    Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
        Me.Close()
    End Sub
End Class


' This class represents a single parameter value as it
' was defined in the xml input file.
Public Class ParameterValue
    Public Name As String
    Public Expression As String
End Class


' This class represents a single configuration.  A configuration
' consists of a name and a set of parameter names and their associated values.
' This object serves as a collection for the associated parameters.  It derives
' from CollectionBase to inherit the collection functionality.
Public Class Configuration
    Inherits CollectionBase

    Public m_Name As String

    ' Add a new drilling feature to the collection.
    Public Function AddParameter(ByVal Name As String, ByVal Expression As String) As ParameterValue
        ' Create a new object
        Dim newMember As ParameterValue
        newMember = New ParameterValue

        ' Set the properties passed into the method
        newMember.Name = Name
        newMember.Expression = Expression

        ' Add the object to the collection.
        MyBase.List.Add(newMember)

        ' Return the object created
        AddParameter = newMember
        newMember = Nothing
    End Function

    Public Property Name() As String
        Get
            Name = m_Name
        End Get
        Set(ByVal Value As String)
            m_Name = Value
        End Set
    End Property

    ' Get an item by index.
    Default Public ReadOnly Property Item(ByVal Index As Integer) As ParameterValue
        Get
            Return CType(MyBase.List.Item(Index), ParameterValue)
        End Get
    End Property
End Class

' This class is a collection of all of the configurations defined in the
' input xml file.  It derives from CollectionBase to inhereit the collection
' functionality.
Public Class Configurations
    Inherits CollectionBase

    ' Add a new drilling feature to the collection.
    Public Function Add(ByVal Name As String) As Configuration
        ' Create a new object
        Dim newMember As Configuration
        newMember = New Configuration

        ' Set the properties passed into the method
        newMember.Name = Name

        ' Add the object to the collection.
        MyBase.List.Add(newMember)

        ' Return the object created
        Add = newMember
        newMember = Nothing
    End Function

    ' Get an item by index.
    Default Public ReadOnly Property Item(ByVal Index As Integer) As Configuration
        Get
            Return CType(MyBase.List.Item(Index), Configuration)
        End Get
    End Property
End Class
