
function writeHeaders
{

    "Basket.Import.ADGroup.Create" | Out-File $DAWGruppenAnlegenOutFilePath -Encoding "utf8NoBOM"
    "GUIMandator" + ";" + "GUIADGroup.Name" + ";" + "GUIADGroup.Description" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.DisplayName" + ";" + "GUIProcessingNote" + ";" + "OldName" | Out-File $DAWGruppenAnlegenOutFilePath -Append -Encoding "utf8NoBom"
   
    "Basket.Import.ADGroup.ManageMembers" | Out-File "$DAWGruppenverwaltungOutFilePath" -Encoding "utf8NoBom"
    "GUIMandator" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.LDAP" + ";" + "GUIADGroup.ADGroups.Add.LDAPs" + ";" + "GUIADGroup.ADGroups.Rem.LDAPs" + ";" + "GUIADGroup.Accounts.Add.LDAPs" + ";" + "GUIADGroup.Accounts.Rem.LDAPs" + ";" + "GUIProcessingNote" + ";" + "ADObjectType" + ";" + "Quellgruppe" | Out-File $DAWGruppenverwaltungOutFilePath -Encoding "utf8NoBom" -Append 

    "Basket.Import.ADGroup.ManageMembers" | Out-File "$DAWGruppenverwaltungNeuOutFilePath" -Encoding "utf8NoBom"
    "GUIMandator" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.LDAP" + ";" + "GUIADGroup.ADGroups.Add.LDAPs" + ";" + "GUIADGroup.ADGroups.Rem.LDAPs" + ";" + "GUIADGroup.Accounts.Add.LDAPs" + ";" + "GUIADGroup.Accounts.Rem.LDAPs" + ";" + "GUIProcessingNote" + ";" + "ADObjectType" + ";" + "Quellgruppe" | Out-File $DAWGruppenverwaltungNeuOutFilePath -Encoding "utf8NoBom" -Append 
   
    "Profilnummer;Name;Beschreibung;Profilverantwortliche OE/Stelle;Info;InfoKurs" | Out-File $KURSSollProfileOutFilePath -Encoding "utf8NoBom"
    
    "Personalnummer;;Profilnummer;Name Berechtigungsprofil" | Out-File "$KURSIstMitarbeiterProfileOutFilePath" -Encoding "utf8NoBom"
}

function getObjectMemberships
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $adObjectEntryName,
        [Parameter(Mandatory = $true, Position = 1)]
        [string] $memberShipType,
        [Parameter(Mandatory = $true, Position = 2)]
        [string] $memberShipOriginalType
    )

    $key = getMembershipIndexKey $adObjectEntryName $memberShipType

    $bucket = $ADObjectMembershipsIndexed[$key]
    $objectMemberships = if ($null -eq $bucket)
    {
        @() 
    }
    else
    {
        ### outLogVerbose 'else -> true (if/elseif (?) -> false)'
        @($bucket) 
    }

    $objectMemberships | Add-Member -Force "Origin" $memberShipOriginalType
    $objectMemberships | Add-Member -Force "OriginName" $adObjectEntryName
    return $objectMemberships
}

function getAllUserMemberships
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        $adObjectEntry
    )

    $result = [System.Collections.Generic.List[object]]::new()
    $result.AddRange(@(getObjectMemberships $adObjectEntry.ObjectName "user" "user"))

    $groupMemberships = @(getObjectMemberships $adObjectEntry.ObjectName "group" "group")
    foreach ($groupMembership in $groupMemberships)
    {
        ### outLogVerbose 'foreach $groupMembership in $groupMemberships:' $groupMembership.MemberName
        $result.AddRange(@(getObjectMemberships $groupMembership.MemberName "user" "group"))
    }
    return @($result)
}

function getUsersByMemberGroup
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $GroupName
    )

    $bucket = $ADObjectMembershipsIndexed[(getMembershipIndexKey $GroupName "user")]
    $result = if ($null -eq $bucket)
    {
        @() 
    }
    else
    {
        ### outLogVerbose 'else -> true (if/elseif (?) -> false)'
        @($bucket) 
    }
    return $result
}







function getMembershipIndexKey
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $GroupName,
        [Parameter(Mandatory = $true, Position = 1)]
        [string] $MemberClass
    )
    return "$GroupName`0$MemberClass".ToLowerInvariant()
}

function newObjectMembershipsIndex
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        [AllowEmptyCollection()]
        $ADObjectMemberships
    )

    $index = @{}

    foreach ($entry in $ADObjectMemberships)
    {
        ### outLogVerbose 'foreach $entry in $ADObjectMemberships:' $entry.GroupName
        $key = getMembershipIndexKey $($entry.GroupName) $($entry.MemberClass)
        $key0 = "$($entry.GroupName)`0$($entry.MemberClass)".ToLowerInvariant()
        if (-not($key -eq $key0))
        {
            ### outLogVerbose 'if (-not($key -eq $key0)) -> true'
            throw "this is BAAAD"
        }
        $bucket = $index[$key]
        if ($null -eq $bucket)
        {
            ### outLogVerbose 'if ($null -eq $bucket) -> true'
            $bucket = [System.Collections.Generic.List[object]]::new()
            $index[$key] = $bucket
        }
        $bucket.Add($entry)
    }

    return $index
}

function getRoleType
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0)]
        $adObjectEntry,
        [Parameter(Mandatory = $true, Position = 1)]
        [String] $typeTag
    )
    $roleType = "unknown"
    $relevantRoleTypes = $SPK.GroupManagementMappings | Where-Object {$_.OU -ieq $typeTag} | Sort-Object { [int]$_.Order }
    foreach ($relevantRoleType in $relevantRoleTypes)
    {
        ### outLogVerbose 'foreach $relevantRoleType in $relevantRoleTypes:' $relevantRoleType.TypeName
        if ($adObjectEntry.OldName -cmatch $relevantRoleType.TagRegExp)
        {
            ### outLogVerbose 'if ($adObjectEntry.OldName -cmatch $relevantRoleType.TagRegExp) -> true'
            $roleType = $relevantRoleType.TypeName 
            break
        }
    }
    if (($typeTag -ieq $FI.TypeTagSecurity) -and ($roleType -ieq "unknown"))
    {
        ### outLogVerbose 'if (($typeTag -ieq $FI.TypeTagSecurity) -and ($roleType -ieq "unknown")) -> true'
        $roleType = "GPO"
    }
    return $roletype
}

function buildPrefixMatcher
{
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$CsvPath
    )

    if (-not (Test-Path $CsvPath))
    {
        ### outLogVerbose 'if (-not (Test-Path $CsvPath)) -> true'
        throw "Prefix CSV not found: $CsvPath" 
    }

    $byLength = [System.Collections.Generic.Dictionary[int, System.Collections.Generic.HashSet[string]]]::new()

    foreach ($row in $SPK.ProfilPrefixes)
    {
        ### outLogVerbose 'foreach $row in $SPK.ProfilPrefixes:' $row
        $prefix = $row.Prefix
        if ([string]::IsNullOrEmpty($prefix)) 
        {
            ### outLogVerbose 'if ([string]::IsNullOrEmpty($prefix)) -> true'
            continue 
        }

        $len = $prefix.Length
        $bucket = $null
        if (-not $byLength.TryGetValue($len, [ref] $bucket))
        {
            ### outLogVerbose 'if (-not $byLength.TryGetValue($len, [ref] $bucket)) -> true'
            $bucket = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
            $byLength[$len] = $bucket
        }
        [void]$bucket.Add($prefix)
    }

    $lengths = $byLength.Keys | Sort-Object -Descending

    return [pscustomobject]@{
        ByLength = $byLength
        Lengths  = $lengths
    }
}

function testKnownPrefixes
{
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Value,
        [Parameter(Mandatory = $true, Position = 1)]
        $Matcher
    )

    if ([string]::IsNullOrEmpty($Value))
    {
        ### outLogVerbose 'if ([string]::IsNullOrEmpty($Value)) -> true'
        return $false 
    }

    $valueLen = $Value.Length
    foreach ($len in $Matcher.Lengths)
    {
        ### outLogVerbose 'foreach $len in $Matcher.Lengths:' $len
        if ($len -gt $valueLen)
        {
            ### outLogVerbose 'if ($len -gt $valueLen) -> true'
            continue 
        }
        $candidate = $Value.Substring(0, $len)
        $bucket = $Matcher.ByLength[$len]
        if ($bucket.Contains($candidate))
        {
            ### outLogVerbose 'if ($bucket.Contains($candidate)) -> true'
            return $true
        }
    }
    return $false
}



function postProcess
{
    param (
        [Parameter(Mandatory = $false, Position = 0)]  
        [bool]$convertToAscii = $false
    ) 
    


 

    
}

