#Requires -Modules 'powershell-yaml'
[CmdletBinding()]
Param(
    [Parameter(Mandatory)]
    [ValidateScript({
            If ([boolean]($_.IndexOfAny([io.path]::GetInvalidFileNameChars()) -lt 0)) {
                $True
            } Else {
                Throw 'Provided input contains invalid characters; aborting.'
            }
        })]
    [string]$TemplateName,
    [Parameter(Mandatory)]
    [ValidateScript({
            If (Test-Path($_)) {
                $True
            } Else {
                Throw "'$_' is not a valid filename (within working directory '$PWD'), or access denied; aborting."
            }
        })]
    [string]$OVFFile
)

$GetContentSplat = @{
    Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', ".$($TemplateName).yml")
    Raw  = $True
}
$RawContent = Get-Content @GetContentSplat
$ConvertFromYamlSplat = @{
    Yaml         = $RawContent
    AllDocuments = $True
}
$OVFConfig = ConvertFrom-Yaml @ConvertFromYamlSplat

$SourceFile = Get-Item -Path $OVFFile
$GetContentSplat = @{
    Path = $SourceFile.FullName
}
$XML = [xml](Get-Content @GetContentSplat)
$NS = [System.Xml.XmlNamespaceManager]$XML.NameTable
[void]$NS.AddNamespace('Any', $XML.DocumentElement.xmlns)

If ($OVFConfig.DeploymentConfigurations.Count -gt 0) {
    $XMLSection = $XML.CreateElement('DeploymentOptionSection', $XML.DocumentElement.xmlns)
    $XMLSectionInfo = $XML.CreateElement('Info', $XML.DocumentElement.xmlns)
    $XMLSectionInfo.InnerText = 'Deployment Type'
    [void]$XMLSection.AppendChild($XMLSectionInfo)
    
    ForEach ($Configuration in $OVFConfig.DeploymentConfigurations) {
        $XMLConfig = $XML.CreateElement('Configuration', $XML.DocumentElement.xmlns)
    
        $XMLConfigAttrId = $XML.CreateAttribute('id', $XML.DocumentElement.ovf)
        $XMLConfigAttrId.Value = $Configuration.Id
    
        $XMLConfigLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
        $XMLConfigLabel.InnerText = $Configuration.Label
    
        $XMLConfigDescription = $XML.CreateElement('Description', $XML.DocumentElement.xmlns)
        $XMLConfigDescription.InnerText = $Configuration.Description
    
        [void]$XMLConfig.Attributes.Append($XMLConfigAttrId)
        [void]$XMLConfig.AppendChild($XMLConfigLabel)
        [void]$XMLConfig.AppendChild($XMLConfigDescription)
    
        [void]$XMLSection.AppendChild($XMLConfig)
    }
    [void]$XML.SelectSingleNode('//Any:Envelope', $NS).InsertAfter($XMLSection, $XML.SelectSingleNode('//Any:NetworkSection', $NS))
    Write-Host "Inserted 'DeploymentOptionSection' with $($Configuration.Count) nodes"
}

$XMLAttrTransport = $XML.CreateAttribute('transport', $XML.DocumentElement.ovf)
$XMLAttrTransport.Value = 'com.vmware.guestInfo'
[void]$XML.SelectSingleNode('//Any:VirtualHardwareSection', $NS).Attributes.Append($XMLAttrTransport)

$XMLProductSection = $XML.SelectSingleNode('//Any:ProductSection', $NS)
If ($XMLProductSection -eq $Null) {
    $XMLProductSection = $XML.CreateElement('ProductSection', $XML.DocumentElement.xmlns)
    [void]$XML.SelectSingleNode('//Any:VirtualSystem', $NS).AppendChild($XMLProductSection)
    Write-Host "Inserted 'ProductSection'"
} Else {
    ForEach ($Child in $XMLProductSection.SelectNodes('//Any:ProductSection/child::*', $NS)) {
        [void]$Child.ParentNode.RemoveChild($Child)
    }
    Write-Host "Destroyed pre-existing children in 'ProductSection'"
}
$XMLProductSectionInfo = $XML.CreateElement('Info', $XML.DocumentElement.xmlns)
$XMLProductSectionInfo.InnerText = 'Information about the installed software'
[void]$XMLProductSection.AppendChild($XMLProductSectionInfo)
Write-Host "Inserted new 'Info' into 'ProductSection'"

ForEach ($Category in $OVFConfig.PropertyCategories) {
    If ($Category.Name -ne '') {
        $XMLCategory = $XML.CreateElement('Category', $XML.DocumentElement.xmlns)
        $XMLCategory.InnerText = $Category.Name
        [void]$XMLProductSection.AppendChild($XMLCategory)
        Write-Host "Inserted new 'Category' into 'ProductSection'"
    }

    ForEach ($Property in $Category.ProductProperties) {
        $XMLProperty = $XML.CreateElement('Property', $XML.DocumentElement.xmlns)
    
        $XMLPropertyAttrKey = $XML.CreateAttribute('key', $XML.DocumentElement.ovf)
        $XMLPropertyAttrKey.Value = $Property.Key
        $XMLPropertyAttrType = $XML.CreateAttribute('type', $XML.DocumentElement.ovf)
        Switch -regex ($Property.Type) {
            'boolean' {
                $XMLPropertyAttrType.Value = 'boolean'
            }
            'int' {
                $XMLPropertyAttrType.Value = 'uint8'
                $Qualifiers = @()
                If ($Property.Type -match 'int\((\d*)\.\.(\d*)\)') {
                    If ($Matches[1]) {
                        $Qualifiers += "MinValue($($Matches[1]))"
                    }
                    If ($Matches[2]) {
                        $Qualifiers += "MaxValue($($Matches[2]))"
                    }
                    $XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
                    $XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
                    [void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
                }
            }
            'ip' {
                $XMLPropertyAttrType.Value = 'string'
                $XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.vmw)
                $XMLPropertyAttrQualifiers.Value = 'Ip'
                [void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
            }
            'password' {
                $XMLPropertyAttrType.Value = 'string'
                $XMLPropertyAttrPassword = $XML.CreateAttribute('password', $XML.DocumentElement.ovf)
                $XMLPropertyAttrPassword.Value = 'true'
                [void]$XMLProperty.Attributes.Append($XMLPropertyAttrPassword)

                $Qualifiers = @()
                If ($Property.Type -match 'password\((\d*)\.\.(\d*)\)') {
                    If ($Matches[1]) {
                        $Qualifiers += "MinLen($($Matches[1]))"
                    }
                    If ($Matches[2]) {
                        $Qualifiers += "MaxLen($($Matches[2]))"
                    }
                    $XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
                    $XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
                    [void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
                }
            }
            'string' {
                $XMLPropertyAttrType.Value = 'string'
                $Qualifiers = @()
                If ($Property.Type -match 'string\((\d*)\.\.(\d*)\)') {
                    If ($Matches[1]) {
                        $Qualifiers += "MinLen($($Matches[1]))"
                    }
                    If ($Matches[2]) {
                        $Qualifiers += "MaxLen($($Matches[2]))"
                    }
                    $XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
                    $XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
                    [void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
                } ElseIf ($Property.Type -match 'string\[(.*)\]') {
                    $XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
                    $XMLPropertyAttrQualifiers.Value = "ValueMap{$($Matches[1] -replace '","', '", "')}"
                    [void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
                }
            }
        }
        $XMLPropertyAttrUserConfigurable = $XML.CreateAttribute('userConfigurable', $XML.DocumentElement.ovf)
        $XMLPropertyAttrUserConfigurable.Value = "$([boolean]$Property.UserConfigurable)".ToLower()
        $XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
        If ($Property.Type -eq 'boolean') {
            $XMLPropertyAttrValue.Value = "$([boolean]$Property.DefaultValue)".ToLower()
        } Else {
            $XMLPropertyAttrValue.Value = $Property.DefaultValue
        }
        [void]$XMLProperty.Attributes.Append($XMLPropertyAttrKey)
        [void]$XMLProperty.Attributes.Append($XMLPropertyAttrType)
        [void]$XMLProperty.Attributes.Append($XMLPropertyAttrUserConfigurable)
        [void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)

        If ($Property.Label) {
            $XMLPropertyLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
            $XMLPropertyLabel.InnerText = $Property.Label
            [void]$XMLProperty.AppendChild($XMLPropertyLabel)
        }
        If ($Property.Description) {
            $XMLPropertyDescription = $XML.CreateElement('Description', $XML.DocumentElement.xmlns)
            $XMLPropertyDescription.InnerText = $Property.Description
            [void]$XMLProperty.AppendChild($XMLPropertyDescription)
        }

        If (($Property.Configurations.Count -eq 1) -and ($Property.Configurations -eq '*')) {
            $XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
            $XMLPropertyAttrConfiguration.Value = $OVFConfig.DeploymentConfigurations.Id -join ' '
            [void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)       
        } ElseIf ($Property.Configurations.Count -gt 0) {
            $XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
            $XMLPropertyAttrConfiguration.Value = $Property.Configurations -join ' '
            [void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)       
        }

        If ($Property.Value.Count -eq 1) {
            $XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
            $XMLPropertyAttrValue.Value = $Property.Value
            [void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)       
        } ElseIf ($Property.Value.Count -gt 1) {
            ForEach ($Value in $Property.Value) {
                $XMLValue = $XML.CreateElement('Value', $XML.DocumentElement.xmlns)

                $XMLValueAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
                $XMLValueAttrValue.Value = $Value
                $XMLValueAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
                $XMLValueAttrConfiguration.Value = $Value

                [void]$XMLValue.Attributes.Append($XMLValueAttrValue)
                [void]$XMLValue.Attributes.Append($XMLValueAttrConfiguration)

                [void]$XMLProperty.AppendChild($XMLValue)
            }
        }

        [void]$XMLProductSection.AppendChild($XMLProperty)
    }
    Write-Host "Inserted $($Category.ProductProperties.Count) new node(s) into 'ProductSection'"
}

$XML.Save($SourceFile.FullName)
