Classes/Extensions/ExtensionContainer.psm1

using module "..\Dialog.psm1"
using module "..\OpenAiChat.psm1"
using module "..\Options.psm1"
using module "..\..\Private\OutHelper.psm1"

class ExtensionContainer {
    [OpenAiChat]$ChatApi
    [Options]$Options
    [object[]]$Extensions

    ExtensionContainer($chatApi, $options, $extensions) {
        $this.ChatApi = $chatApi
        $this.Options = $options
        $this.Extensions = $extensions
        $this.InjectOptionalProperties()
        $this.InitializeParameters()
    }

    # If Extension has a property of type OpenAiChat or Options, inject the instance
    InjectOptionalProperties() {
        $this.Extensions | ForEach-Object {
            $type = $_.GetType()
            $props = $type.GetProperties()
            foreach($prop in $props) {
                if($prop.PropertyType -eq [OpenAiChat]) {
                    $prop.SetValue($_, $this.ChatApi)
                }
                if($prop.PropertyType -eq [Options]) {
                    $prop.SetValue($_, $this.Options)
                }
            }
        }
    }

    # Inject additional parameters from the command line, e.g. -WordCountWarning_Threshold 1000
    InitializeParameters() {
        foreach($ext in $this.Extensions) {
            $type = $ext.GetType()
            Write-Debug "ExtensionContainer: Loaded $($type.Name)"
            $opt = $this.Options.AdditionalArguments
            for($i = 0; $i -lt $opt.Length; $i+=2) {
                $optName = $opt[$i] # -ExtenionName_PropertyName
                if(!($optName -is [string])) {
                    [OutHelper]::NonCriticalError("ExtensionContainer: Parameter name must be a String: $($optName)")
                    continue
                }

                if(!$optName.StartsWith("-")) {
                    continue
                }
                $optName = $optName.Substring(1)

                if($optName.IndexOf("_") -eq -1 -or $optName.Split("_")[0] -ne $type.Name) {
                    # Write-Debug "ExtensionContainer: Skipping $($optName)"
                    continue
                }

                $propName = $optName.Substring($optName.IndexOf("_")+1)
                $propValue = $opt[$i+1]

                $prop = $ext.GetType().GetProperty($propName)
                if($prop) {
                    Write-Debug "ExtensionContainer: Setting $($propName) ($($prop.PropertyType)) on $($type.Name) with value: $($propValue)"
                    $propValue = $this.EnsureType($prop.PropertyType, $propValue)
                    $prop.SetValue($ext, $propValue)
                } else {
                    [OutHelper]::NonCriticalError("ExtensionContainer: $propName not found on $($type.Name)")
                }
            }
        }
    }

    [object] EnsureType($propertType, $value) {
        if($value -is $propertType) {
            return $value
        }
        return [System.Convert]::ChangeType($value, $propertType)
    }

    [object] Invoke([string]$eventName, [object]$inputObject) {
        $outputObject = $inputObject
        $this.Extensions | ForEach-Object {
            $method = $_.GetType().GetMethod($eventName)
            if($method) {
                Write-Debug "ExtensionContainer: Invoking $($method.Name) on $($_.GetType().Name)"
                $arguments = @( $inputObject )
                $outputObject = $method.Invoke($_, $arguments)
            }
        }
        return $outputObject
    }
}