Functions.ps1


function Backup-MySettings {
    [CmdletBinding()]
    param (
        [Parameter()] [ValidateSet("VsCode", "WindowsTerminal", "Notepad++", "PowerToys")] [string[]] $App,
        [Parameter()] [switch] $All,
        [Parameter()] [int] $RetentionDays = 14

    )

    $AppList = [PSCustomObject]@{

        VsCode          = @("$($env:APPDATA)\Code\User\settings.json"
            "$($env:APPDATA)\Code\User\snippets\powershell.json")

        WindowsTerminal = "$($env:LOCALAPPDATA)\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"

        "Notepad++"     = "$($env:APPDATA)\Notepad++\config.xml"

        # PowerToys = Get-ChildItem "$($env:LOCALAPPDATA)\Microsoft\PowerToys" -Recurse -Include *.json
        PowerToys       = "$($env:LOCALAPPDATA)\Microsoft\PowerToys\*.json"

    }


    if ($All) {
        $App = $AppList.psobject.Properties.Name
    }







    $RestoreCommands = ""
    $BackupRoot = "$($env:OneDrive)\Backup"
    $BackupPathNow = "$($BackupRoot)\$($env:COMPUTERNAME)_$(Get-Date -Format "yyyyMMdd-HHmmss")"




    #Remove old backups

    $Folders = Get-ChildItem $BackupRoot
    foreach ($f in $folders) {
        if ($f.LastWriteTime -lt (Get-Date).AddDays(-$RetentionDays)) {
            $FullName = $f.FullName
            Move-Item $FullName "$($env:TEMP)\trash"
            Remove-Item "$($env:TEMP)\trash" -Recurse -Force
        }
    }

    #End remove old backups




    foreach ($a in $App) {


        $SettingsPath = $AppList.$a

        $BackupPath = "$($BackupPathNow)\$($a)"
        # $BackupPath = "C:\Temp\Backup\$($a)\$($env:computername)"
        # $InfoFile = "$($BackupPath)\info.txt"


        if (-not(Test-Path $BackupPath)) {
            New-Item $BackupPath -ItemType Directory | Out-Null
        }
        # $null | Out-File $InfoFile

        foreach ($path in $SettingsPath) {
            if (Test-Path $path) {

                $SettingsFile = (Get-Item $path).Name

                $PathType = ((Get-Item $path).GetType()).Name
                if ($PathType -eq "FileInfo") {
                    $BackupPath2 = Join-Path $BackupPath $SettingsFile
                } else {
                    $BackupPath2 = $BackupPath
                }
                # Copy-Item $path $BackupPath2 -Recurse -Force
                robocopy $path $BackupPath2 /NFL /NDL /MIR /W:1 /R:1

                Write-Output "$path > $BackupPath2"
                $RestoreCommands += "Copy-Item `"$BackupPath2`" `"$path`" -Recurse -Force`n"

            } else {

                Write-Warning "Settings file not found at `"$($path)"

            }
        }



        # "-------------------------"

        # $SettingsPath
        # $SettingsFile
        # $BackupPath

    }

    $RestoreCommands | Out-File "$($BackupPathNow)\Restore_$($env:computername).ps1"




}

function Backup-NAS01 {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $DriveLetter = "M"
    )

    $Source = "M:"
    $Destination = "D:\NAS01Backup"

    if ($env:COMPUTERNAME -ne "ZOLDER") {
        break
    }
    if (!(Test-Path $Source)) {
        break
    }

    if (!(Test-Path $Destination)) {
        New-Item $Destination -ItemType Directory
    }

    robocopy $Source $Destination /MIR /W:1 /R:1 /NFL


}


function Backup-VstFolder {

    [CmdletBinding()]
    param (
        # [Parameter(Mandatory)] [string] $ParameterName
    )

    $VstFolder = "C:\Program Files\Common Files\VST3"

    $BackupLocation = "$($env:OneDrive)\Guitar\VstBackup-$($env:COMPUTERNAME)"

    if (!(Test-Path $BackupLocation)) {

        New-Item $BackupLocation -ItemType Directory -Force

    }

    robocopy $VstFolder $BackupLocation /MIR /R:1 /W:1

}

function Clear-JRecycleBin {

    Clear-RecycleBin -Force -ea 0

}

function Clear-Notifications {
    [CmdletBinding()]
    param (
    )

    Start-Job -ScriptBlock {
        powershell -noprofile -Command {
            [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null

            # get the list of all registry keys
            $notifications = Get-ChildItem HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings | Select-Object Name

            # iterate through the keys, extract the name that will be used in the clear function, and clear the notifications
            for ($index = 0; $index -lt $notifications.Count; $index++) {
                $name = $notifications[$index]
                $split = $name -split "\\"
                $last = $split[$split.Count - 1]
                $last = $last.Substring(0, $last.Length - 1)
        ([Windows.UI.Notifications.ToastNotificationManager]::History).clear($last)
            }
        }
    } | Out-Null
}

function Connect-NAS01 {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $DriveLetter = "M"
    )

    $DeviceName = "NAS01"

    $TestConnect = Test-NetConnection $DeviceName

    if ($TestConnect.PingSucceeded -eq $true) {

        mount.exe -o anon \\$DeviceName\i-data\ea12954a\nfs\Shared M:

        $AppPath = "powershell.exe"
        $TaskName = "Launch $AppPath"
        $Argument = '-NoProfile -command "mount.exe -o anon \\NAS01\i-data\ea12954a\nfs\Shared M:"'
        $action = New-ScheduledTaskAction -Execute $AppPath -Argument $Argument
        $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date)
        Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $TaskName | Out-Null
        Start-ScheduledTask -TaskName $TaskName

        Start-Sleep -s 3
        Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false


    } else {

        # Write-Error "NAS01 not online"
        break

    }


}


function Convert-CmdletToSplat {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $CommandToProcess,
        [Parameter()] [switch] $CopyToClipBoard
    )


    function Find-LongestStringLength {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory)] $Array
        )

        # $Array

        $StrLen = @()
        foreach ($a in $Array) {
            $StrLen += $a.Length
        }



        $Return = $StrLen | Sort-Object | Select-Object -Last 1

        return $Return

    }


    # Ignore the following Parameters
    $CmdletBindings = @("Verbose", "Debug", "ErrorAction", "WarningAction", "InformationAction", "ErrorVariable", "WarningVariable", "InformationVariable", "OutVariable", "OutBuffer", "PipelineVariable")



    $CmdLet = $CommandToProcess.Trim().Split(" ")[0]

    $Verbouwen = @{}
    $i = 1

    $Verbouwen.$i = $CommandToProcess
    $i++; $Verbouwen.$i = ($Verbouwen.($i - 1)).replace($CmdLet, "")
    $i++; $Verbouwen.$i = ($Verbouwen.($i - 1)).replace("``", "")
    # $i++; $Verbouwen.$i = ($Verbouwen.($i - 1)).replace("`n", "")
    # $i++; $Verbouwen.$i = ($Verbouwen.($i - 1)).replace(" ", "")

    # $Verbouwen | Format-List


    $ParamArray = $Verbouwen.$i -Split " -"

    if ($ParamArray.Count -gt 1) {
        $Params = @()
        $ParamArray | ForEach-Object {

            $p = $_.Trim().split(" ")

            if ($p) {
                $obj = [PSCustomObject]@{
                    Parameter = $p[0]
                    Value     = $p[1..50]
                }
                $Params += $obj
            }

        }
        # $Params

        $Output = "`n"

        if ($Params) {
            $Output += "`$Params = @{`n"

            $MaxKeyLength = Find-LongestStringLength ($Params).Parameter
            $MaxKeyLength

            $Params | ForEach-Object {
                if ($_.Value) {
                    $Value = $_.Value
                } else {
                    $Value = "`$true"
                }
                if ($Value -like "*`$*") {
                    $quotes = ""
                } elseif ($Value -like "*`"*") {
                    $quotes = ""
                } else {
                    $quotes = "`""
                }

                $Output += " $($_.Parameter)"
                $FormatFillSpaces = $MaxKeyLength - ($_.Parameter).Length
                0..$FormatFillSpaces | ForEach-Object {
                    $output += " "
                }
                $Output += "= $($quotes)$($Value)$($quotes)`n"

            }

            $Output += "}`n`n"
        }
        $Output += "$($CmdLet) @Params`n`n"

        Write-Host $Output -ForegroundColor Yellow
        if ($CopyToClipBoard) {
            $Output | clip
        }
    }

    # Write-Host "##################################################" -ForegroundColor Green



    $Command = Get-Command $Cmdlet -ea 0

    if ($Command) {
        # $Command
        $Output = "`n"
        $Output += "`$Params = @{`n"

        foreach ($c in $Command) {

            $MaxKeyLength = Find-LongestStringLength ($c.Parameters).Keys
            # $MaxKeyLength

            ($c.Parameters).getenumerator() | ForEach-Object {
                if ($CmdletBindings -notcontains $_.Key) {

                    $Output += " $($_.Key)"
                    $FormatFillSpaces = $MaxKeyLength - ($_.Key).Length
                    0..$FormatFillSpaces | ForEach-Object {
                        $output += " "
                    }
                    $Output += "= "
                    if ($_.Value.SwitchParameter) {
                        $Output += "`$true"
                    } elseif ($_.Value.Attributes.ValidValues) {
                        $Output += "`"$(($_.Value.Attributes.ValidValues) -join ",")`""
                    } else {
                        $Output += "`"`""
                    }
                    $Output += "`n"

                }
            }
        }
        $Output += "}`n`n"
        $Output += "$($CmdLet) @Params`n`n"

        Write-Host $Output -ForegroundColor Yellow
    }

}



$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $o = Get-Command $wordToComplete*
    $o.Name | ForEach-Object {
        if ($_ -match " ") {
            "'$_'"
        } else {
            $_
        }
    }
}
Register-ArgumentCompleter -CommandName Convert-CmdletToSplat -ParameterName CommandToProcess -ScriptBlock $scriptblock


function Copy-WithRobocopy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Source,
        [Parameter(Mandatory)] [string] $Destination,
        [Parameter()] [switch] $Force
    )



    $Source = (Get-Item $Source).FullName
    if ($Source[($Source.Length - 1)] -eq "\") {
        $Source = $Source.Substring(0, ($Source.Length - 1))
    }


    if ($Destination[($Destination.Length - 1)] -eq "\") {
        $Destination = $Destination.Substring(0, ($Destination.Length - 1))
    }




    $Source
    $Destination


    if (Test-Path $Source) {
        if ((Test-Path $Destination) -and !($Force)) {
            Write-Warning "$($Destination) already exists"
        } else {
            robocopy "$Source" "$Destination" /MIR /W:1 /R:1
        }
    } else {
        Write-Warning "$($Source) does not exist"
    }


}




function Disconnect-NAS01 {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $DriveLetter = "M"
    )


    umount.exe M: -f


    $AppPath = "powershell.exe"
    $TaskName = "Launch $AppPath"
    $Argument = '-NoProfile -command "umount.exe M: -f"'
    $action = New-ScheduledTaskAction -Execute $AppPath -Argument $Argument
    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date)
    Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $TaskName | Out-Null
    Start-ScheduledTask -TaskName $TaskName

    Start-Sleep -s 3
    Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false


}

function Enable-NFS {
    [CmdletBinding()]
    param (
        # [Parameter()] [string] $ParameterName
    )


    Enable-WindowsOptionalFeature -Online -FeatureName ServicesForNFS-ClientOnly
    Enable-WindowsOptionalFeature -Online -FeatureName ClientForNFS-Infrastructure
    Enable-WindowsOptionalFeature -Online -FeatureName NFS-Administration


    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name AnonymousUid -Value 0 -Type DWORD
    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name AnonymousGid -Value 0 -Type DWORD


    nfsadmin client stop
    nfsadmin client start

    nfsadmin client localhost config fileaccess=755 SecFlavors=+sys -krb5 -krb5i

}

function Expand-MyArchive {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)] [string] $Path,
        [Parameter(Mandatory = $false, Position = 1)] [string] $DestinationPath
    )

    #region Checks

    # Check if the path is a valid file and exists
    if (-not (Test-Path $Path)) {
        Write-Error "The specified path '$Path' does not exist."
        return
    }

    # Check if the path is a file
    if (-not (Test-Path $Path -PathType Leaf)) {
        Write-Error "The specified path '$Path' is not a file."
        return
    }

    # Check if the destination path is provided, if not, use the same directory as the archive with archive name
    if (-not $DestinationPath) {
        $archiveFile = Get-Item $Path
        $DestinationPath = $archiveFile.DirectoryName + "\" + $archiveFile.BaseName
        Write-Verbose $DestinationPath
    }

    # Check if the destination path exists, if not, create it
    if (-not (Test-Path $DestinationPath)) {
        New-Item -Path $DestinationPath -ItemType Directory -Force | Out-Null
    }
    #endregion

    Write-Verbose $Path
    Write-Verbose $DestinationPath


    if (Test-Path "C:\Program Files\7-Zip\7z.exe") {

        Write-Host "Using 7z.exe to extract the archive." -ForegroundColor Green

        Start-Process -FilePath "7z.exe" -ArgumentList "x", "-y", "-o`"$DestinationPath`"", "`"$Path`"" -NoNewWindow -Wait -ErrorAction Stop


    } else {

        Write-Host "7z.exe not found, using Expand-Archive." -ForegroundColor Yellow

        Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force -ErrorAction Stop

    }

}


function Find-EmptyFolders {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $Path = "."

    )

    # Get-ChildItem $Path

    $Result = @()

    $items = Get-ChildItem $Path -Directory -Recurse -ea 0

    foreach ($item in $items) {
        # if ($item.PSIsContainer) {
        $subitems = Get-ChildItem -Recurse -Path $item.FullName
        if (!($subitems)) {
            $Result += $item.FullName
            # Remove-Item $item.FullName -Recurse
        }
        Remove-Variable subitems -Force -ea 0
        # }
    }

    return $Result

}

function Find-File {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $SearchString,
        [Parameter()] [switch] $ExactMatch,
        [Parameter()] [switch] $AndDirectories,
        [Parameter()] [string] $Path = "."
    )

    $Param = @{}
    if (!($AndDirectories)) {
        $Param.File = $true
    }
    if(!($ExactMatch)) {
        $SearchString = "*$SearchString*"
    }
    $Results = Get-ChildItem $Path $SearchString -Recurse @Param

    $Results = $Results.FullName


    return $Results

}

function Get-DeadLinks {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $Folder = ".",
        [Parameter()] [switch] $Remove
    )

    $shell = New-Object -ComObject WScript.Shell

    $Links = Get-ChildItem -Path $Folder -Recurse -Force -Filter "*.lnk" -ea 0
    # $Links

    foreach ($l in $Links) {

        $LinkProperties = $shell.CreateShortcut($l.FullName)
        if (!(Test-Path $LinkProperties.TargetPath)) {
            Write-Output "Link broken: `"$($LinkProperties.FullName)`" => `"$($LinkProperties.TargetPath)`""
            if ($Remove) {
                Remove-Item $LinkProperties.FullName -Force
                Write-Output "Removed link $($LinkProperties.FullName)"
            }
        }
    }

}

function Get-EnvironmentVariables {
    [CmdletBinding()]
    param (
        # [Parameter(Mandatory)] [string] $ParameterName
    )

    Get-ChildItem Env: | Sort-Object name

}

function Get-ExternalIpAddress {
    [CmdletBinding()]
    param (

    )

    $ip = Invoke-RestMethod http://ipinfo.io/json | Select-Object -ExpandProperty ip

    return $ip

}

function Get-FolderSize {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $Path = ".",
        [Parameter()] [int] $Depth = 0,
        [Parameter()] [switch] $IncludeFiles
    )


    function Get-FolderSizePerItem {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory)] [string] $Path
        )

        $Path = Resolve-Path $Path

        function Format-ItemSize {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory)] [float] $size
            )

            if ($size -gt 1024 * 1024 * 1024) {
                $size = $size / 1024 / 1024 / 1024
                $UnitOfMeasurement = "GB"
            } elseif ($size -gt 1024 * 1024) {
                $size = $size / 1024 / 1024
                $UnitOfMeasurement = "MB"
            } elseif ($size -gt 1024) {
                $size = $size / 1024
                $UnitOfMeasurement = "kB"
            } else {
                $UnitOfMeasurement = "B"
            }
            $size = [math]::Round($size, 2)
            $returnSize = "$($size.ToString()) $UnitOfMeasurement"

            return $returnSize

        }



        $Results = @()

        $TotalSize = 0
        $SizeBytes = 0
        $Files = Get-ChildItem $Path -Recurse -File -Force -ErrorAction SilentlyContinue

        $Files | ForEach-Object {
            # $_.Length
            $TotalSize += $_.Length
            $SizeBytes += $_.Length
        }


        $Results += [PSCustomObject]@{
            Path       = $Path
            Type       = "Folder"
            TotalSize  = Format-ItemSize $TotalSize
            TotalItems = ($Files).Count
            SizeBytes  = $SizeBytes
        }

        if ($IncludeFiles) {
            $Files = Get-ChildItem $Path -File -Force -ErrorAction SilentlyContinue
            $Files | ForEach-Object {
                $Results += [PSCustomObject]@{
                    Path       = $_.FullName
                    Type       = "File"
                    TotalSize  = Format-ItemSize $_.Length
                    TotalItems = 1
                    SizeBytes  = $_.Length
                }
            }
        }

        return $Results


    }

    if ($Depth -ge 1) {
        $Result = @()
        Get-ChildItem -Path $Path -Depth ($Depth - 1) -Directory -ErrorAction SilentlyContinue | ForEach-Object {
            $Result += Get-FolderSizePerItem $_.FullName
        }
    } else {
        $Result = Get-FolderSizePerItem $Path
    }


    return $Result


}

function Get-InstalledModuleVersion {
    [CmdletBinding()]
    param (
    )

    $result = @()

    $excludeList = "Az.|Microsoft.Graph.Beta."

    Get-InstalledModule | Where-Object Name -NotMatch $excludeList | ForEach-Object {
        Write-Verbose "$($_.Name) $($_.Version)"
        $foundModule = Find-Module $_.Name -ea 0
        Write-Verbose "$($foundModule.Name) $($foundModule.Version))"
        if ($_.Version -eq $foundModule.Version) {
            $updateNeeded = $false
        } else {
            $updateNeeded = $true
        }
        $obj = [PSCustomObject]@{
            Name           = $_.Name
            CurrentVersion = $_.Version
            NewVersion     = $foundModule.Version
            UpdateNeeded   = $updateNeeded
        }
        $result += $obj
    }

    return $result

}

function Get-InstalledPrograms {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $SearchString,
        [Parameter()] [switch] $ExtendedInfo,
        [Parameter()] [switch] $CopyToClipboard
    )

    # $Installed1 = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
    # $Installed2 = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
    # $Installed3 = Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*

    # $Installed = $Installed1 + $Installed2 + $Installed3

    # $Installed = $Installed | Where-Object DisplayName

    $Installed = (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*) +
    (Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*) +
    (Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*)

    $Installed = $Installed | Where-Object { $_.DisplayName }


    if ($SearchString) {
        $Apps = $Installed | Where-Object { ($_.DisplayName -Like "*$($SearchString)*") -or ($_.Publisher -Like "*$($SearchString)*") }
    } else {
        $Apps = $Installed
    }

    $Apps = $Apps | Select-Object PSChildName, DisplayName, DisplayVersion, Publisher, InstallDate, UninstallString | Sort-Object DisplayName

    $return = foreach ($r in $Apps) {
        if ($r.UninstallString -like "*msiexec*") {
            $PSUninstallString = "cmd /c msiexec.exe /X$(($r.PSChildName)) /qn"
        } else {
            $PSUninstallString = "Start-Process '$($r.UninstallString)'"
        }

        $obj = [PSCustomObject]@{
            DisplayName    = $r.DisplayName
            DisplayVersion = $r.DisplayVersion
            Publisher      = $r.Publisher
        }

        if ($ExtendedInfo) {
            $obj | Add-Member -MemberType NoteProperty -Name PSChildName -Value $r.PSChildName
            $obj | Add-Member -MemberType NoteProperty -Name InstallDate -Value $r.InstallDate
            $obj | Add-Member -MemberType NoteProperty -Name UninstallString -Value $r.UninstallString
            $obj | Add-Member -MemberType NoteProperty -Name PSUninstallString -Value $PSUninstallString
        }

        $obj
    }

    if ($CopyToClipboard) {
        $return[-1].PSUninstallString | clip
    }

    return $return
}
function Get-MyAlias {
    [CmdletBinding()]
    param (

    )

    Get-Alias | Where-Object Source -EQ "JaapsTools" | Select-Object Name, ResolvedCommand | Format-Table

}


function Get-OsStartupsAndShutdowns {
    [CmdletBinding()]
    param (

    )

    $filter = @{
        logname   = 'System'
        starttime = (Get-Date).AddDays(-10).date
    }
    $events = Get-WinEvent -FilterHashtable $filter | Where-Object { $_.Id -eq 12 -or $_.Id -eq 13 -and $_.Message -notmatch "Credential" }

    # $filter = @{
    # logname = 'System'
    # starttime = (Get-Date).date
    # id = 12
    # }

    # $events = Get-WinEvent -FilterHashtable $filter

    # $filter = @{
    # logname = 'System'
    # id = 12
    # starttime = (Get-Date).date
    # }

    # $events += Get-WinEvent -FilterHashtable $filter
    # Get-WinEvent -FilterHashtable @{logname = 'security'; id = 4624; starttime = (Get-Date).date } | Where-Object { $_.properties[8].value -eq 2 }

    $returnEvents = @()
    $events | ForEach-Object {

        if ($_.Id -eq 12) {
            $eventType = "Startup"
        } elseif ($_.Id -eq 13) {
            $eventType = "Shutdown"
        }
        $eventDate = ($_.TimeCreated).Date -replace " 00:00:00"
        if($eventDate -ne $previousEventDate) {
            $obj = [PSCustomObject]@{
                Event = "------------"
                Date  = "------------"
                Time  = "------------"
            }
            $returnEvents += $obj
        }

        $obj = [PSCustomObject]@{
            Event = $eventType
            Date  = $eventDate
            # Time = "$(($_.TimeCreated).TimeOfDay.Hours):$(($_.TimeCreated).TimeOfDay.Minutes):$(($_.TimeCreated).TimeOfDay.Seconds)"
            Time  = $(($_.TimeCreated).TimeOfDay -split "\.")[0]
        }
        $returnEvents += $obj

        $previousEventDate = $eventDate

    }

    return $returnEvents

}


function Get-PrinterDetails {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $Name
    )


    $printers = Get-Printer
    if ($Name) {
        $printers = $printers | Where-Object Name -EQ $Name
    }

    $Result = @()

    foreach ($p in $printers) {

        $PortDetails = Get-PrinterPort -Name $p.PortName -ea 0
        # $PrinterProperties = Get-PrinterProperty -PrinterName $p.Name

        $obj = [PSCustomObject]@{
            PrinterName           = $p.Name
            ComputerName          = $p.ComputerName
            PrinterType           = $p.Type
            PrinterShareName      = $p.ShareName
            PrinterPortName       = $p.PortName
            PrinterDriverName     = $p.DriverName
            PrinterLocation       = $p.Location
            PrinterComment        = $p.Comment
            PrinterPrintProcessor = $p.PrintProcessor
            PrinterDatatype       = $p.Datatype
            PrinterShared         = $p.Shared
            PrinterPublished      = $p.Published
            PrinterDeviceType     = $p.DeviceType
            PrinterPrinterStatus  = $p.PrinterStatus
            Name                  = $PortDetails.Name
            PortDescription       = $PortDetails.Description
            PortMonitor           = $PortDetails.PortMonitor
            PrinterHostAddress    = $PortDetails.PrinterHostAddress
            PrinterHostIP         = $PortDetails.PrinterHostIP
            Protocol              = $PortDetails.Protocol
        }

        $Result += $obj


        # Get-PrinterDriver
        # Get-PrintConfiguration -PrinterName $p.Name
    }

    return $Result
}


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $o = Get-Printer | Where-Object { $_.Name -like "$wordToComplete*" }
    $o.Name | ForEach-Object {
        if ($_ -match " ") {
            "'$_'"
        } else {
            $_
        }
    }
}
Register-ArgumentCompleter -CommandName Get-PrinterDetails -ParameterName Name -ScriptBlock $scriptblock


function Get-Quser {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $UserName,
        [Parameter()] [string] $Identity,
        [Parameter()] [string] [ValidateSet("Active", "Disc")] $State,
        [Parameter()] [int] $IdleTimeHours,
        [Parameter()] [int] $IdleTimeMinutes
        # [Parameter()] [string] $LogonTime
    )




    $SessionInfo = @()

    $quser = quser 2>$null

    ForEach ($line in $quser) {
        If ($line -match "logon time") {
            Continue
        }

        # Parse idle time
        $Idle = $line.SubString(54, 9).Trim().Replace('+', '.')
        $IdleTimeRaw = $Idle

        if ($IdleTimeRaw -like "*.*") {
            $IdleSplit1 = $Idle.split(".")
            $IdleDays = $IdleSplit1[0]
            $IdleTimeRaw = $IdleSplit1[1]
            # $IdleDays
        }
        if ($IdleTimeRaw -like "*:*") {
            $IdleSplit2 = $IdleTimeRaw.split(":")
            $IdleHours = $IdleSplit2[0]
            $IdleMinutes = $IdleSplit2[1]
        } elseif ($IdleTimeRaw -eq "none") {
            $IdleDays = 0
            $IdleHours = 0
            $IdleMinutes = 0
        } else {
            $IdleDays = 0
            $IdleHours = 0
            $IdleMinutes = $IdleTimeRaw
        }




        $SessionInfo += [PSCustomObject]@{
            Username    = $line.SubString(1, 20).Trim()
            SessionName = $line.SubString(23, 17).Trim()
            ID          = $line.SubString(42, 2).Trim()
            State       = $line.SubString(46, 6).Trim()
            Idle        = $Idle
            IdleDays    = [int]$IdleDays
            IdleHours   = [int]$IdleHours
            IdleMinutes = [int]$IdleMinutes
            LogonTime   = [datetime]::Parse($line.SubString(65))
        }

    }



    if ($State) {
        $SessionInfo = $SessionInfo | Where-Object State -EQ $State
    }
    if ($Identity) {
        $SessionInfo = $SessionInfo | Where-Object ID -EQ $Identity
    }
    if ($UserName) {
        $SessionInfo = $SessionInfo | Where-Object Username -EQ $UserName
    }
    if ($IdleTimeHours) {
        $SessionInfo = $SessionInfo | Where-Object IdleHours -GE $IdleTimeHours
    }
    if ($IdleTimeMinutes) {
        $SessionInfo = $SessionInfo | Where-Object IdleMinutes -GE $IdleTimeMinutes
    }


    return $SessionInfo

}


function Get-SpecialFolders {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $SpecialFolderName
    )



    $SpecialFolders = @()

    [Enum]::GetNames([Environment+SpecialFolder]) | Sort-Object | ForEach-Object {
        $ThisSpecialFolder = [PSCustomObject]@{
            Name      = $_
            PSCommand = "[Environment]::GetFolderPath(`"$_`")"
            Path      = [Environment]::GetFolderPath($_)
        }
        $SpecialFolders += $ThisSpecialFolder
    }
    if ($SpecialFolderName) {
        $SpecialFolders = $SpecialFolders | Where-Object Name -EQ $SpecialFolderName
    }



    return $SpecialFolders

}


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $o = [Enum]::GetNames([Environment+SpecialFolder]) | Sort-Object | Where-Object { $_ -like "$wordToComplete*" }
    $o | ForEach-Object {
        if ($_ -match " ") {
            "'$_'"
        } else {
            $_
        }
    }
}
Register-ArgumentCompleter -CommandName Get-SpecialFolders -ParameterName SpecialFolderName -ScriptBlock $scriptblock



function Get-SystemInfo {
    [CmdletBinding()]
    param (

    )



    # BatteryStatus
    # EstimatedChargeRemaining : 100
    # EstimatedRunTime : 71582788


    # $NetAdapters = Get-NetAdapter | Where-Object { $_.HardwareInterface -eq $true -and $_.Status -eq "Up" }
    # $NetAdapters


    # $counters = @(
    # "\Processor(*)\% Processor Time"
    # # "\Memory\Available Bytes"
    # # "\PhysicalDisk(**)\% Idle Time" # SLOW
    # # "\PhysicalDisk(**)\Avg. Disk sec/Read" # SLOW
    # # "\PhysicalDisk(**)\Avg. Disk sec/Write" # SLOW
    # # "\PhysicalDisk(**)\Current Disk Queue Length" # SLOW
    # # "\Paging File(**)\% Usage" # NOT USED
    # # "\Network Interface(*)\Bytes Total/sec" # SLOW
    # # "\Network Interface(*)\Bytes Sent/sec" # SLOW
    # # "\Network Interface(*)\Bytes received/sec" # SLOW
    # )
    # $counters

    # Write-Progress -Activity "Starting jobs..."
    # $jobs = $counters | ForEach-Object {
    # Start-Job -ScriptBlock { ((Get-Counter -Counter $using:_).CounterSamples) } -ArgumentList $_
    # }

    # Write-Verbose ($Perf | Select-Object Path, InstanceName, CookedValue | Format-Table -AutoSize | Out-String) -Verbose


    # $Perf | Where-Object { $_.Path -match "physicaldisk" -and $_.InstanceName -ne "_total" } | Format-Table -AutoSize

    # $Perf | Where-Object { $_.Path -Match "processor time" -and $_.InstanceName -eq "_total" } | ft -AutoSize

    $win32_Processor_job = Start-Job -ScriptBlock { Get-CimInstance Win32_Processor }
    # $ProcessorTime = (Get-Counter "\Processor(_Total)\% Processor Time").CounterSamples.CookedValue
    $win32_operatingsystem = Get-CimInstance -Class Win32_OperatingSystem
    $win32_battery = Get-CimInstance win32_battery
    $Win32_ComputerSystemProduct = Get-CimInstance Win32_ComputerSystemProduct
    $Processes = Get-Process -IncludeUserName

    if (Get-Command Get-Uptime -ea 0) {
        $uptime = (Get-Uptime).TotalSeconds
        $uptime_ts = [timespan]::fromseconds($uptime)
        $uptime = ("{0:dd\.hh\:mm\:ss}" -f $uptime_ts)
    }

    $reg_windowsversion = Get-ItemProperty "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion"

    $win32_Processor = $win32_Processor_job | Wait-Job | Receive-Job
    $win32_Processor_job | Remove-Job


    $CPUPercentageTotal = $win32_Processor.LoadPercentage
    if (!($CPUPercentageTotal)) {
        $CPUPercentageTotal = "0"
    }
    $CPUmodel = $win32_Processor.Name
    # $CPUClockspeed = $($Perf | Where-Object { $_.Path -Match "processor frequency" -and $_.InstanceName -eq "_total" } | Select-Object -ExpandProperty CookedValue) / 1000
    $MemoryAvailable = [math]::round($win32_operatingsystem.FreePhysicalMemory / 1024 / 1024, 1)
    $TotalPhysicalRAM = [math]::round(($win32_operatingsystem | Select-Object -ExpandProperty TotalVisibleMemorySize) / 1024 / 1024, 1)
    $MemoryInUse = ($TotalPhysicalRAM - $MemoryAvailable)


    $volumes = Get-Volume | Where-Object DriveLetter | Sort-Object DriveLetter

    # $IpInfo = gip -all | ?

    # $DiskInfo = $Perf | Where-Object { $_.Path -Match "physicaldisk" -and $_.InstanceName -ne "_total" } | Sort-Object InstanceName
    # $NetworkInfo = $Perf | Where-Object { $_.Path -Match "network interface" -and $_.CookedValue -gt 0 } | Sort-Object InstanceName

    # $MemoryAvailable

    # $DiskInfo | ForEach-Object {
    # $counter = ($_.Path -split "\\")[-1]
    # $cookedValue = $([math]::round($_.CookedValue, 2))
    # if ($_.Path -match "idle time") {
    # $counter = "disk activity"
    # $cookedValue = [math]::round(100 - $cookedValue, 2)
    # $cookedValue = "$($cookedValue)%"
    # }
    # Write-Output "Disk $($_.InstanceName) $counter - $cookedvalue"
    # }

    # $NetworkInfo | ForEach-Object {
    # $counter = ($_.Path -split "\\")[-1]
    # $cookedValue = $([math]::round($_.CookedValue / 1024 , 2))
    # Write-Output "$($_.InstanceName) M$($counter) - $($cookedvalue)Mbps"
    # }


    $Result = @()

    # Write-Host "----------" -ForegroundColor Green
    $Result += [pscustomobject]@{
        Name  = "CPU"
        Value = "$($CPUPercentageTotal)% ($($CPUmodel))"
    }
    $Result += [pscustomobject]@{
        Name  = "Memory"
        Value = "$($MemoryInUse) / $TotalPhysicalRAM GB"
    }
    $Result += [pscustomobject]@{
        Name  = "Processes"
        Value = ($Processes).Count
    }

    foreach ($v in $volumes) {

        $sizeUsed = $v.Size - $v.SizeRemaining

        $Result += [pscustomobject]@{
            Name  = "Volume $($v.DriveLetter) used"
            Value = "$([math]::Round($sizeUsed / 1GB)) / $([math]::Round($v.Size / 1GB)) GB"
        }
    }

    foreach ($adapter in Get-NetAdapter | Where-Object Status -EQ Up | Sort-Object Name, Type) {
        foreach ($ipinterface in Get-NetIPAddress -AddressFamily IPv4 | Where-Object InterfaceAlias -EQ $adapter.Name) {
            $Result += [PSCustomObject]@{
                Name  = $adapter.name
                # Type = $ipinterface.AddressFamily
                Value = $ipinterface.IPAddress
            }
        }
    }


    $Result += [pscustomobject]@{
        Name  = "Logged on users:"
        Value = ($Processes | Where-Object ProcessName -EQ "ShellExperienceHost").Count
    }

    $Result += [pscustomobject]@{
        Name  = "OS"
        Value = "$($win32_operatingsystem.Caption) $($reg_windowsversion.DisplayVersion) ($($win32_operatingsystem.Version).$($reg_windowsversion.UBR))"
    }
    $Result += [pscustomobject]@{
        Name  = "Computer Name"
        Value = "$($win32_operatingsystem.CSName)"
    }
    $Result += [pscustomobject]@{
        Name  = "Computer Model"
        Value = "$($Win32_ComputerSystemProduct.Name)"
    }
    if ($win32_battery) {
        $Result += [pscustomobject]@{
            Name  = "Battery"
            Value = "$($win32_battery.EstimatedChargeRemaining)%"
        }
    }
    if ($uptime) {
        $Result += [pscustomobject]@{
            Name  = "Uptime"
            Value = $uptime
        }
    }

    return $Result


    # Write-Host "----------" -ForegroundColor Green
    # $Processes = Get-Process
    # $Processes | Sort-Object CPU -Descending | Select-Object -First 10 | Format-Table -AutoSize
    # Write-Host "----------"
    # $Processes | Sort-Object PM -Descending | Select-Object -First 10 | Format-Table -AutoSize

}


function Get-UserProfiles {
    [CmdletBinding()]
    param (

    )


    $Profiles = Get-CimInstance Win32_UserProfile
    $ProfilesFiltered = $Profiles | Where-Object { $_.LastUseTime -and $_.Special -eq $false }
    # $Profiles | Select-Object LocalPath, LastUseTime, SID, Loaded, Special, HealthStatus | ft
    return $ProfilesFiltered



}

# Get-UserProfiles | Where-Object { $_.LastUseTime -lt (Get-Date).AddDays(-180) } | Remove-CimInstance

function Get-WifiNetworks {
    [CmdletBinding()]
    param (
        [Parameter()] [switch] $SaveToOneDrive
    )


    $result = @()

    $wifi = $(netsh.exe wlan show profiles)

    if ($wifi -match "There is no wireless interface on the system.") {
        Write-Output $wifi
        exit
    }

    $ListOfSSID = ($wifi | Select-String -Pattern "\w*All User Profile.*: (.*)" -AllMatches).Matches | ForEach-Object { $_.Groups[1].Value }
    # $NumberOfWifi = $ListOfSSID.count
    # Write-Warning "[$(Get-Date)] I've found $NumberOfWifi Wi-Fi Connection settings stored in your system $($env:computername) : "
    foreach ($SSID in $ListOfSSID) {
        try {
            $passphrase1 = ($(netsh.exe wlan show profiles name="$SSID" key=clear) | Select-String -Pattern ".*Key Content.*:(.*)" -AllMatches).Matches.Value
            Write-Verbose $passphrase1
            $passphrase = ($passphrase1 -split ":")[1]
            Write-Verbose $passphrase
        } catch {
            $passphrase = "N/A"
        }
        $obj = [PSCustomObject]@{
            Name       = ($SSID).Trim()
            PassPhrase = ($passphrase).Trim()
        }
        $result += $obj
    }

    if ($SaveToOneDrive) {
        $OutFile = "$($env:OneDrive)\WiFi_$($env:COMPUTERNAME).csv"
        Remove-Item $OutFile -Force -ea 0
        $result | Export-Csv $OutFile -NoClobber -Force -Verbose
    }

    return $result

}


function Get-WorkingTime {

    # version 1


    # $Date = Get-Date

    # $Today = [string]$Date.Day + [string]$Date.Month + [string]$Date.Year

    #$ComputerinfoTempFile =

    # $ComputerinfoTempFile = "$($env:TEMP)\ComputerInfo_$($Today).csv"

    # if (!(Test-Path $ComputerinfoTempFile)) {
    # Get-ComputerInfo | Export-Csv -Path $ComputerinfoTempFile -NoTypeInformation
    # }

    $WT = [ordered]@{}

    # $Computerinfo = Import-Csv $ComputerinfoTempFile

    # $OsLastBootUpTime = Get-Date -Date $Computerinfo.OsLastBootUpTime

    $WTFolderPath = "C:\temp\WorkingTime"
    if(-not(Test-Path $WTFolderPath)) {
        New-Item $WTFolderPath -ItemType Directory -Force | Out-Null
    }
    $WTFilePath = Join-Path $WTFolderPath "wt_$(Get-Date -Format "yyyy-MM-dd").json"
    # Remove-Item $WTFilePath -Force -ea 0
    if (Test-Path $WTFilePath) {
        $WTInfo = Get-Content $WTFilePath | ConvertFrom-Json
        $WMI_OsLastBootUpTime = $WTInfo.LastBootUpTime
    } else {
        # $WMI_OsLastBootUpTime = Get-CimInstance win32_operatingsystem | Select-Object @{LABEL = 'LastBootUpTime'; EXPRESSION = { ($_.ConverttoDateTime($_.lastbootuptime)).DateTime } }
        $WMI_OsLastBootUpTime = Get-CimInstance win32_operatingsystem | Select-Object LastBootUpTime
        $WMI_OsLastBootUpTime | ConvertTo-Json | Out-File $WTFilePath
        $WMI_OsLastBootUpTime = $WMI_OsLastBootUpTime.LastBootUpTime
    }
    # $WMI_OsLastBootUpTime


    $OsLastBootUpTime = Get-Date $WMI_OsLastBootUpTime

    $WT["StartTijd"] = Get-Date $OsLastBootUpTime -Format "HH:mm:ss"

    $WorkingTime = (Get-Date) - $OsLastBootUpTime

    $WT["TijdGewerkt"] = $WorkingTime


    $HoeLangNog = (Get-Date -Hour 8 -Minute 30 -Second 0) - $WorkingTime

    $WT["HoeLangNog"] = Get-Date $HoeLangNog -Format "HH:mm:ss"

    $HoeLangNogHalveDag = (Get-Date -Hour 4 -Minute 0 -Second 0) - $WorkingTime

    $WT["HoeLangNogHalveDag"] = Get-Date $HoeLangNogHalveDag -Format "HH:mm:ss"

    $WT["JeMoetTot"] = Get-Date $OsLastBootUpTime.AddMinutes(510) -Format "HH:mm:ss"

    return $WT

}


function Import-MyModule {

    [CmdletBinding()]

    param (
        [Parameter()]
        [ArgumentCompleter( {
                param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
                Get-ChildItem "$(Join-Path $($env:USERPROFILE) -ChildPath "Git\")$wordToComplete*" -Directory -Name | ForEach-Object { $_ } }
        )]
        [ValidateScript( {
                $_ -in (Get-ChildItem "$(Join-Path $($env:USERPROFILE) -ChildPath "Git\")" -Directory -Name)
            } ) ]
        [string] $Module,
        [Parameter()] [switch] $All,
        [Parameter()] [switch] $ShowVersion
    )


    function Import-MyModuleInternal {

        [CmdletBinding()]
        param (
            [Parameter(Mandatory)] [string] $InternalModule
        )

        $ShowVersion = $true

        $ModuleManifest = "$(Join-Path $($env:USERPROFILE) -ChildPath "Git")\$($InternalModule)\$InternalModule.psd1"

        if (Test-Path $ModuleManifest) {
            if ($ShowVersion) {
                $ModuleVersion = Get-Module $InternalModule | Where-Object Path -Like "*\git\*" | Select-Object Name, Version
                Write-Verbose "Old version: $($ModuleVersion.Name) $($ModuleVersion.Version)" -Verbose
            }

            Import-Module $ModuleManifest -Force -Global

            if ($ShowVersion) {
                $ModuleVersion = Get-Module $InternalModule | Where-Object Path -Like "*\git\*" | Select-Object Name, Version
                Write-Verbose "New version: $($ModuleVersion.Name) $($ModuleVersion.Version)" -Verbose
            }
        } else {
            Write-Warning "Module $ModuleManifest not found"
        }

    }





    if ($All) {

        $Folders = Get-ChildItem (Join-Path $env:USERPROFILE "Git") | Where-Object Name -NE "JaapsTools"
        # Write-Verbose ($Folders | Out-String) -Verbose
        foreach ($folder in $Folders) {
            Write-Verbose $folder.Name
            Import-MyModuleInternal -InternalModule $folder.Name
        }
        Import-MyModuleInternal -InternalModule JaapsTools

    } else {

        if (!($Module)) {
            $PossibleModulename = Get-Item . | Select-Object -ExpandProperty Name
            $ModuleFile = "$PossibleModulename.psd1"
            # Write-Verbose $PossibleModulename -Verbose
            # Write-Verbose $ModuleFile -Verbose
            if (Test-Path $ModuleFile) {
                $Module = $PossibleModulename
                Write-Verbose $PossibleModulename -Verbose
            }
        }

        if ($Module) {
            Import-MyModuleInternal -InternalModule $Module
        } else {
            Write-Warning "No module file found in this folder"
        }
    }

}

function Install-ModuleFromGithub {
    <#
    .SYNOPSIS
    A short one-line action-based description, e.g. 'Tests if a function is valid'
    .DESCRIPTION
    A longer description of the function, its purpose, common use cases, etc.
    .NOTES
    Information or caveats about the function e.g. 'This function is not supported in Linux'
    .LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
    .EXAMPLE
    Install-ModuleFromGithub -Url https://github.com/Something -PSModuleName Something -Headers @{ Authorization = "token 123456789abcdefg" }
    Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][string] $Url,
        [Parameter(Mandatory)][string] $PSModuleName,
        [Parameter()][string] $Branch = "main",
        [Parameter()][PSCustomObject] $Headers,
        [Parameter()][string] $PSRepoName = "Local",
        [Parameter()][string] $PSRepoPath = "C:\Temp\PSRepo"
    )


    if (-not(Test-Path C:\temp\PSRepo)) {
        New-Item C:\temp\PSRepo -ItemType Directory -Force
    }

    $DownloadParams = @{
        Uri     = "$($Url)/archive/refs/heads/$($Branch).zip"
        Headers = $Headers
        OutFile = "c:\temp\repo.zip"
    }

    # Cleanup
    Remove-Item "$($env:TEMP)\$($PSModuleName)" -Recurse -Force
    Remove-Item $DownloadParams.OutFile -Force

    # $ProgressPreference = 'SilentlyContinue'
    Invoke-WebRequest @DownloadParams -ea stop
    # $ProgressPreference = 'Continue'


    # $PSRepoName = "Local"
    # $PSRepoPath = "C:\Temp\PSrepo"
    # $PSModuleName = "WindowsImaging"
    # $ModulePath = Join-Path -Path $PSRepoPath -ChildPath $PSModuleName


    # Remove-Item $ModulePath -Recurse -Force -ea 0
    Expand-Archive $DownloadParams.OutFile -DestinationPath $env:TEMP -Force


    Rename-Item "$($env:TEMP)\$($PSModuleName)-$($Branch)" -NewName $PSModuleName

    if (-not(Get-PSRepository $PSRepoName -ea 0)) {
        Register-PSRepository -Name $PSRepoName -SourceLocation $PSRepoPath -InstallationPolicy Trusted
    }
    try {
        Publish-Module -Path "$($env:TEMP)\$($PSModuleName)" -Repository $PSRepoName
    } catch {
        Write-Error $_
    }
    Find-Module $PSModuleName -Repository $PSRepoName

    if (Get-Module $PSModuleName -ListAvailable) {
        Update-Module $PSModuleName
    } else {
        Install-Module $PSModuleName -Repository $PSRepoName -Scope AllUsers
    }

}

function Invoke-ChocoOutdated {

    # version 1

    choco outdated --ignore-pinned

}

function Invoke-ChocoUpgradeAll {

    # version 1

    choco upgrade all -y

}

function Invoke-Launch {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ArgumentCompleter( {
                param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
                $gci1 = Get-ChildItem "$($env:ProgramData)\Microsoft\Windows\Start Menu" -Recurse -File -Filter *.lnk
                $gci2 = Get-ChildItem "$($env:APPDATA)\Microsoft\Windows\Start Menu" -Recurse -File -Filter *.lnk
                $Links = $gci1 + $gci2
                $Links | Where-Object BaseName -Like "$wordToComplete*" | ForEach-Object { "`"$($_.BaseName)`"" }
            }
        )]
        # [ValidateScript( {
        # $_ -in (Get-ChildItem "$($Path)" -Directory -Name)
        # } ) ]
        [string] $Program
    )

    Write-Verbose $Program

    $ShortCut = Get-ChildItem "$($env:ProgramData)\Microsoft\Windows\Start Menu" -Recurse -File -Filter "$($Program).lnk"
    if(!($ShortCut)) {
        $ShortCut = Get-ChildItem "$($env:APPDATA)\Microsoft\Windows\Start Menu" -Recurse -File -Filter "$($Program).lnk"
    }

    Write-Verbose $ShortCut.FullName

    Start-Process $ShortCut.FullName

}

function Invoke-ParallelRobocopy {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Source,
        [Parameter(Mandatory)] [string] $Destination,
        [Parameter()] [string] $ExcludeDirectories,
        [Parameter()] [int] $MaxParallelJobs = 2
    )

    if ($Source[-1] -eq "\") {
        $Source = $Source.Substring(0, ($Source.Length - 1))
    }
    if ($Destination[-1] -eq "\") {
        $Destination = $Destination.Substring(0, ($Destination.Length - 1))
    }
    Write-Verbose $Source
    Write-Verbose $Destination

    $dirs = Get-ChildItem $Source -Directory
    if ($ExcludeDirectories) {
        $dirs = $dirs | Where-Object Name -NotMatch $ExcludeDirectories
    }
    Write-Verbose ($dirs | Format-Table | Out-String)

    $processRc = @{}

    $dirs | ForEach-Object {

        $i++
        $subSource = $_.FullName
        $subDestination = Join-Path $Destination $_.Name
        $processRc[$i] = Start-Process -FilePath "robocopy.exe" -ArgumentList "`"$($subSource)`" `"$($subDestination)`" /MIR /NFL /NDL /W:1 /R:1 /MT:32" -PassThru -WindowStyle Minimized

        while (($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count -ge $MaxParallelJobs) {
            Write-Warning "Waiting to finish $(($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count) robocopy jobs"
            Start-Sleep 1
        }

    }

    while ($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }) {
        Write-Warning "Waiting to finish $(($processRc | ForEach-Object { $_.Values | Where-Object HasExited -EQ $false }).count) robocopy jobs before final sync"
        Start-Sleep 1
    }

    Start-Process -FilePath "robocopy.exe" -ArgumentList "`"$($Source)`" `"$($Destination)`" /MIR /NFL /NDL /W:1 /R:1 /MT:128 /XD $($ExcludeDirectories)" -NoNewWindow -Wait

}

function Invoke-Timer {

    [CmdletBinding()]


    param (
        [parameter()] [int] $Time = 3,
        [Parameter()] [string] [ValidateSet("Seconds", "Minutes", "Hours")] $HMS = "Seconds",
        [parameter()] [string] $At
    )


    switch ($HMS) {
        "Hours" {
            $Seconds = ($time * 60 * 60)
        }
        "Minutes" {
            $Seconds = ($time * 60)
        }
        "Seconds" {
            $Seconds = $time
        }
    }





    if ($At) {

        if ($At.Length -eq 3) {
            # $At

            $At = "0$($At)"
            $At

        } elseif ($At.Length -eq 4) {

        } else {
            Write-Error "`"At`" not in correct format"
        }

        $ShutdownHour = $At[0] + $At[1]
        $ShutdownHour = [int]$ShutdownHour
        # $ShutdownHour

        $ShutdownMinute = $At[2] + $At[3]
        $ShutdownMinute = [int]$ShutdownMinute
        # $ShutdownMinute

        if ($ShutdownHour -gt 23 -or $ShutdownMinute -gt 59) {
            Write-Error "Time input not correct"
            break
        }

        $EndTime = Get-Date -Hour $ShutdownHour -Minute $ShutdownMinute -Second 00

        if ($EndTime -lt (Get-Date)) {
            $EndTime = $EndTime.AddDays(1)
        }

    } else {
        $EndTime = (Get-Date).AddSeconds($Seconds)
    }

    while (($TimeRemaining = ($EndTime - (Get-Date))) -gt 0) {

        Write-Progress -Activity 'Timer running until...' -Status $EndTime -SecondsRemaining $TimeRemaining.TotalSeconds
        Start-Sleep 1

    }


}


function Invoke-UserLogoff {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $UserName,
        [Parameter()] [string] $Identity,
        [Parameter()] [string] [ValidateSet("Active", "Disc")] $State,
        [Parameter()] [int] $IdleTimeHours,
        [Parameter()] [int] $IdleTimeMinutes
    )


    $PSBoundParameters.GetEnumerator() | ForEach-Object {
        # "$($_.Key): $($_.Value)"
        $ParamSet = $true
        $ParamSet | Out-Null
    }

    # $PSBoundParameters.GetEnumerator().Key

    if (!($ParamSet)) {
        Write-Warning "No parameters supplied"
        break
    }



    $Param = @{}


    if ($State) {
        $param.Add("State", $State)
    }
    if ($Identity) {
        $param.Add("Identity", $Identity)
    }
    if ($UserName) {
        $param.Add("UserName", $UserName)
    }
    if ($IdleTimeHours) {
        $param.Add("IdleTimeHours", $IdleTimeHours)
    }
    if ($IdleTimeMinutes) {
        $param.Add("IdleTimeMinutes", $IdleTimeMinutes)
    }


    $Sessions = Get-Quser @Param


    $Sessions | ForEach-Object {
        Write-Verbose "Logoff user $($_.UserName) ($($_.ID))..."
        logoff.exe $_.ID
    }

}


function logout {
    [CmdletBinding()]
    param (

    )

    Stop-ProcessSoft msedge -Silent
    Stop-ProcessSoft -ProcessName chrome -Silent
    Stop-ProcessSoft -ProcessName EXCEL -Silent

    Start-Sleep 1

    logoff

}

function Maintenance {
    [CmdletBinding()]
    param (
        [Parameter()] [switch] $UpdateAll,
        [Parameter()] [switch] $UpdatePSModules,
        [Parameter()] [switch] $UpdateHelp
    )

    cua
    cr
    rdi

    Write-Host "`n----`n"
    Remove-DownloadFolderItems
    Show-TrayIcons

    # Backup-MySettings -All
    # Set-ChocoPinList


    if (Get-Command Get-MpComputerStatus -ea 0) {
        $MpScan_Params = @{
            AsJob = $true
        }
        if ((Get-MpComputerStatus).FullScanOverdue -eq $true) {
            $MpScan_Params.Add('ScanType', 'FullScan')
        }

        if ((Get-MpComputerStatus).QuickScanOverdue -eq $true -or (Get-MpComputerStatus).FullScanOverdue -eq $true) {
            Start-MpScan @MpScan_Params
        }
    }

    if ($UpdateAll -or $UpdatePSModules) {
        Start-Job -ScriptBlock {
            Update-InstalledModule
            Uninstall-OldInstalledModules
        }
    }
    if ($UpdateAll -or $UpdateHelp) {
        Start-Job -ScriptBlock { Update-Help -Scope AllUsers -Force -ea 0 }
    }



}


function New-ChromiumProfile {
    [CmdletBinding()]
    param (

    )

    $Params = @{
        FilePath     = "C:\Program Files\Chromium\Application\chrome.exe"
        ArgumentList = "--profile-directory=Profile$(Get-Random)"
    }
    Start-Process @Params -Verbose

}

function New-GitBranch {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $BranchName
    )


    git branch $BranchName
    git switch $BranchName
    git push --set-upstream origin $BranchName

}

function New-GitHubIssue {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Title,
        [Parameter(Mandatory)] [ValidateSet("enhancement", "bug")] [string[]] $Labels,
        [Parameter()] [string] $Body = ""
    )

    if (!(Test-Path "C:\Program Files\GitHub CLI\gh.exe")) {
        throw "Github CLI not installed. Missing `"C:\Program Files\GitHub CLI\gh.exe`""
    }

    if (!(gh api /user)) {
        throw "Not logged in to GitHub"
    }

    # if (!(Test-Path ".git")) {
    # throw "not a git repository"
    # }


    gh issue create --label $Labels --title "$($Title)" --assignee '@me' --body $Body



}

function New-GitHubPullRequest {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Title,
        [Parameter()] [int] $IssueNo,
        [Parameter()] [string] $Body,
        [Parameter()] [switch] $Merge
    )

    $BodyString = ""
    if ($Body) {
        $BodyString = "$($BodyString) $($Body)"
    }
    if ($IssueNo) {
        $BodyString = "$($BodyString) - Fixes #$($IssueNo)"
    }
    $BodyString = $BodyString.Trim()

    gh pr create -t $Title -b $BodyString

    gh pr list

    if ($Merge) {
        gh pr merge
    }

}

function New-GuitarRecord {

    $VideoRecordFolder = "$($env:OneDrive)\Guitar\VideoRecord"
    $Date = Get-Date -Format "yyyyMMdd"

    $CakewalkTemplateProject = "$VideoRecordFolder\Template\Template"
    $ShotcutTemplateProject = "$VideoRecordFolder\Template\Template.mlt"

    $Artist = Read-Host -Prompt "Artist"

    $Title = Read-Host -Prompt "Title"

    if(!($Artist) -or !($Title)) {
        break
    }

    #$NewProjectName = "Dragon Force - Through the fire and the Flames"
    $NewProjectName = "$Artist - $Title"

    $NewProjectFolder = "$VideoRecordFolder\$Date - $NewProjectName"
    $NewProjectFolderAudio = "$NewProjectFolder\A"
    $NewProjectFolderVideo = "$NewProjectFolder\V"


    ##

    #ls $VideoRecordFolder

    #$Date

    ##

    New-Item -ItemType Directory $NewProjectFolder -Force
    New-Item -ItemType Directory $NewProjectFolderVideo -Force
    New-Item -ItemType Directory $NewProjectFolderAudio -Force


    #Start-Process "Robocopy" -ArgumentList "$CakewalkTemplateProject $NewProjectFolderAudio /MIR" -Wait -PassThru
    Robocopy.exe $CakewalkTemplateProject $NewProjectFolderAudio /MIR

    Rename-Item "$NewProjectFolderAudio\Template.cwp" "$NewProjectFolderAudio\$NewProjectName.cwp"

    Copy-Item $ShotcutTemplateProject $NewProjectFolderVideo
    Rename-Item "$NewProjectFolderVideo\Template.mlt" "$NewProjectName.mlt"

    explorer $NewProjectFolder

    ##

}

function New-MyVolume {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript( {
                $_ -in ((Get-Disk).Number)
            } ) ]
        [string] $Disk,
        [Parameter(Mandatory)] [string] $DriveLetter
    )

    if ((Get-Disk -Number $Disk).AllocatedSize -ne 0) {
        Write-Warning "Unable to create partition, the disk already has data present."
    } elseif (Get-Volume $DriveLetter -ea 0) {
        Write-Warning "Unable to create partition, a volume with driveletter $DriveLetter already exists."
    } else {
        Clear-Disk $Disk -Confirm:$false -ea 0
        Initialize-Disk $Disk -PartitionStyle GPT
        New-Volume -DiskNumber $Disk -FileSystem NTFS -DriveLetter $DriveLetter -FriendlyName $DriveLetter
    }
}

function New-Password {
    [CmdletBinding()]
    param (
    )


    $words = @(
        "Appel"
        "Banaan"
        "Balpen"
        "Circus"
        "Camera"
        "Camper"
        "Koffie"
        "Kleuren"
        "Liefde"
        "Bloemen"
        "Laptop"
        "Fietsen"
        "Visser"
        "Winter"
        "Zomer"
        "Jurken"
        "Zwemmer"
        "Strand"
        "Hotel"
        "Vlinder"
        "Paddestoel"
        "Bossen"
        "Bergen"
        "Golven"
        "Vijver"
        "Tomaat"
        "Schip"
        "Kaars"
        "Toren"
        "Wolk"
        "Steen"
        "Kamer"
        "Vlam"
        "Kasteel"
        "Honing"
        "Vleugel"
        "Kabel"
        "Zeil"
        "Inktvis"
        "Puzzel"
        "Kussen"
        "Spiegel"
        "Schilder"
        "Stoelen"
        "Scharen"
        "Knopen"
        "Bellen"
        "Schroeven"
        "Schop"
        "Ketting"
        "Tapijt"
        "Horloge"
        "Stral"
        "Kist"
        "Ballon"
        "Torpedo"
        "Ladder"
        "Gordijn"
        "Baksteen"
        "Komeet"
        "Paraplu"
        "Spijker"
        "Laars"
        "Schaats"
        "Redder"
        "Raam"
        "Tafel"
        "Metaal"
        "Staaf"
        "Potlood"
        "Sleutel"
        "Zwaard"
        "Kraan"
        "Container"
        "Zaklamp"
        "Fotolijst"
        "Schilderij"
        "Hanger"
        "Schelp"
        "Beker"
        "Liniaal"
        "Bomen"
        "Struiken"
        "Poeders"
        "Radijs"
        "Olijven"
        "Meloen"
        "Avocados"
        "Ananas"
        "Pompelmoes"
        "Sinaasappel"
        "Citroen"
        "Mandarijn"
        "Abrikoos"
        "Aardbei"
        "Framboos"
        "Guave"
        "Wortels"
        "Spinazie"
        "Broccoli"
        "Tomaten"
        "Radijzen"
        "Komkommers"
        "Pompoenen"
        "Komijn"
        "Knoflook"
        "Paprika"
        "Courgette"
        "Boontjes"
        "Peterselie"
        "Rozemarijn"
        "Tijm"
        "Oregano"
        "Dille"
        "Dragon"
        "Salie"
        "Kervel"
        "Bieslook"
        "Munt"
        "Kamille"
        "Laurier"
        "Anijs"
        "Kaneel"
        "Gember"
        "Vanille"
        "Nootmuskaat"
        "Kruidnagel"
        "Kardemom"
        "Koriander"
        "Karwij"
        "Venkel"
        "Selderij"
        "Mosterd"
        "Peper"
        "Zout"
        "Suiker"
        "Siroop"
        "Karamel"
        "Marshmallow"
        "Praline"
        "Marsepein"
        "Braam"
        "Bosbes"
        "Aalbes"
        "Kers"
        "Druif"
        "Papaja"
        "Perzik"
        "Pruim"
        "Nectarine"
        "Kiwi"
        "Mango"
        "Vrucht"
        "Bonbon"
        "Chocolade"
        "Cupcake"
        "Dropveter"
        "Kauwgom"
        "Kaneelstok"
        "Kruidnoot"
        "Lolly"
        "Napoleon"
        "Noga"
        "Paasei"
        "Pepermunt"
        "Pepernoot"
        "Salmiak"
        "Schuimpje"
        "Skittles"
        "Suikerspin"
        "Toffee"
        "Toverbal"
        "Tumtum"
        "Winegum"
        "Zoethout"
        "Zuremat"
        "Zuurstok"
        "Zuurtje"
        "Zwartwit"
        "Avocado"
        "Cranberry"
        "Dadel"
        "Granaatappel"
        "Grapefruit"
        "Kokosnoot"
        "Limoen"
        "Moerbei"
        "Olijf"
        "Peer"
        "Vlierbes"
        "Watermeloen"
        # Gereedschap toevoegen
    )


    $word = Get-Random $words

    $specialChar = Get-Random @("!", "@", "#", "$", "%", "&", "*", "(", ")", "-", "=")

    $number = Get-Random -Minimum 101 -Maximum 998


    $password = "$($word)$($specialChar)$($number)"

    return $password

}




function New-RegKey {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $Path,
        [Parameter(Mandatory)] [string] $Name,
        [Parameter(Mandatory)] [ValidateSet("SEG_SZ", "SEG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", "SEG_MULTI_SZ", "REG_QWORD")] [string] $PropertyType,
        [Parameter(Mandatory)] [string] $Value
    )

    $PropertyTypes = @{
        SEG_SZ        = "String"
        SEG_EXPAND_SZ = "ExpandString"
        REG_BINARY    = "Binary"
        REG_DWORD     = "DWord"
        SEG_MULTI_SZ  = "MultiString"
        REG_QWORD     = "QWord"
    }

    $Param = @{
        Path         = $Path
        Name         = $Name
        PropertyType = $PropertyTypes[$PropertyType]
        Value        = $Value
    }


    $Param

    New-ItemProperty @Param -Force -ErrorAction 0


}

function New-Shortcut {
    [CmdletBinding()]
    param (
        [parameter(mandatory = $true)][string]$ShortcutLocation,
        [parameter(mandatory = $true)][string]$ShortcutTarget,
        [string]$Arguments,
        [string]$IconLocation
    )

    Write-Verbose "Creating shortcut with parameters:"
    Write-Verbose ($PSCmdlet.MyInvocation.BoundParameters | Out-String)

    $shortcutTargetParent = Split-Path $ShortcutTarget -Parent

    $WshShell = New-Object -ComObject WScript.Shell

    $Shortcut = $WshShell.CreateShortcut($ShortcutLocation)
    if ($Arguments) {
        $Shortcut.Arguments = $Arguments
    }
    $Shortcut.TargetPath = $ShortcutTarget
    if ($shortcutTargetParent) {
        $Shortcut.WorkingDirectory = $shortcutTargetParent
    }
    if ($IconLocation) {
        $Shortcut.IconLocation = $IconLocation
    }

    if ($Shortcut.Save()) {

        Write-Verbose "Shortcut created with the following parameters:"
        Write-Verbose ($Shortcut | Out-String)

    }
}


function New-ShortcutAdvanced {
    param (
        [parameter(Mandatory = $true)] [string] $ShortcutLocation,
        [parameter(Mandatory = $true)] [string] $ShortcutTarget,
        [parameter()] [string] $Arguments,
        [parameter()] [string] $IconLocation,
        [parameter()] [switch] $Force
    )


    function Test-URI {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $true)]
            [String]
            $URI
        )

        $URI2 = $URI -as [System.Uri]
        $validation = ($null -ne $URI2.AbsoluteURI) -and ($URI2.Scheme -match '[http|https]')
        return $validation

    }


    if ($Force) {
        New-Item $ShortcutLocation -ItemType File -Force -ErrorAction Stop | Out-Null
        Write-Verbose "File created: $($ShortcutLocation)"
    }

    $ShortcutParent = Split-Path $ShortcutLocation -Parent
    Write-Verbose "ShortcutParent: $($ShortcutParent)"
    $ShortcutLeaf = Split-Path $ShortcutLocation -Leaf
    Write-Verbose "ShortcutLeaf: $($ShortcutLeaf)"


    $ShortcutParent = Resolve-Path $ShortcutParent -ErrorAction Stop
    Write-Verbose "ShortcutParent: $($ShortcutParent)"
    if (!($Force)) {
        if (Test-URI $ShortcutTarget) {

        } else {
            $ShortcutTarget = Resolve-Path $ShortcutTarget -ErrorAction Stop
        }
    }
    Write-Verbose "ShortcutTarget: $($ShortcutTarget)"
    if ($ShortcutTarget -match '[http|https]') {
    } else {
        $shortcutTargetParent = Split-Path $ShortcutTarget -Parent
        Write-Verbose "shortcutTargetParent: $($shortcutTargetParent)"
    }

    if ($IconLocation) {
        $IconSplitString = $IconLocation -split ","
        $IconLocation = Resolve-Path $IconSplitString[0] -ErrorAction Stop
        if ($IconSplitString[1]) {
            $IconLocation = "$($IconLocation),$($IconSplitString[1])"
        }
        Write-Verbose "IconLocation: $($IconLocation)"
    }


    $ShortcutLocationDefinitive = Join-Path $ShortcutParent $ShortcutLeaf
    Write-Verbose "ShortcutLocationDefinitive: $($ShortcutLocationDefinitive)"


    $WshShell = New-Object -ComObject WScript.Shell

    $Shortcut = $WshShell.CreateShortcut($ShortcutLocationDefinitive)
    if ($Arguments) {
        $Shortcut.Arguments = $Arguments
    }
    $Shortcut.TargetPath = $ShortcutTarget
    if ($shortcutTargetParent) {
        $Shortcut.WorkingDirectory = $shortcutTargetParent
    }
    if ($IconLocation) {
        $Shortcut.IconLocation = $IconLocation
    }
    # $Shortcut
    $Shortcut.Save()



}


function Open-InExplorer {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $Path = "."
    )

    Write-Verbose $Path

    $NewPath = (Resolve-Path $Path).Path

    Write-Verbose $NewPath

    if (Test-Path $NewPath) {

        if($NewPath[$NewPath.Length - 1] -eq "\") {
            $NewPath2 = $NewPath.Substring(0, ($NewPath.Length - 1))
        } else {
            $NewPath2 = $NewPath
        }


        Write-Verbose $NewPath2

        explorer.exe $NewPath2

    } else {
        Write-Warning "$NewPath not found"
    }


}

function Open-JPSHistory {

    # version 1

    notepad++.exe (Get-PSReadlineOption).HistorySavePath

}

function Open-NewWindowsinstallScript {
    [CmdletBinding()]
    param (

    )

    $ScriptLoc = "$(Split-Path (Get-Module JaapsTools).path)\scripts\New-Windowsinstall.ps1"

    $PreferredApps = @(
        # "C:\Program Files\Microsoft VS Code\Code.exe"
        "C:\Program Files\Notepad++\notepad++.exe"
    )

    foreach ($app in $PreferredApps) {
        if (Test-Path $app) {
            Start-Process $app -ArgumentList "`"$ScriptLoc`""
            $AppFound = $true
            break
        }
    }

    if (!($AppFound)) {
        notepad.exe $ScriptLoc
    }



}

function Publish-MyModule {
    [CmdletBinding()]
    param (
        [Parameter()]
        [ArgumentCompleter( {
                param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
                Get-ChildItem "$($env:USERPROFILE)\Git\$wordToComplete*" -Directory -Name | ForEach-Object { $_ } }
        )]
        [string]
        $Module
    )



    $NugetApiKeyFile = "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell\NugetApikey.txt"
    if (Test-Path $NugetApiKeyFile) {
        $NugetApiKey = Get-Content $NugetApiKeyFile
    } else {
        Write-Warning "NugetApiKeyFile not found at: `"$($NugetApiKeyFile)`""
        break
    }

    # $Path = (Get-Item (Get-Module $Module -ListAvailable).Path).Directory.Fullname
    $Path = "$($env:USERPROFILE)\Git\$($Module)"

    Publish-Module -Path $Path -NuGetApiKey $NugetApiKey




    # Publish-Module -Path 'C:\Users\jterlouw\OneDrive - RAM Infotechnology\Documenten\WindowsPowerShell\Modules\TestmodulePSGallery\' -NuGetApiKey "oy2c75vxjgj5nm7kwzeaov7xlnbve64uhgr7yjdrz6u7gy"

    Write-Host "https://www.powershellgallery.com/packages/$($Module)/" -ForegroundColor Yellow



}


function Remove-ChromiumProfiles {
    [CmdletBinding()]
    param (

    )

    $ChromeAppdataPath = Join-Path $env:LOCALAPPDATA "Chromium\User Data"

    if (Test-Path $ChromeAppdataPath) {

        $Profiles = Get-ChildItem $ChromeAppdataPath "Profile*" -Directory

        if ($Profiles) {
            Stop-ProcessSoft Chrome
            $Profiles | ForEach-Object {
                Write-Verbose "Removing: $($_.Name)"
                Remove-Item $_.FullName -Recurse -Force
            }
            Remove-Item (Join-Path $ChromeAppdataPath "Local State") -Verbose
        } else {
            Write-Warning "Nothing to remove"
        }
    } else {
        Write-Warning "Nothing to remove"
    }

}

function Remove-DesktopIcons {

    # version 1

    Get-ChildItem $env:OneDrive\Bureaublad -ErrorAction 0 | Remove-Item -Recurse -Force
    Get-ChildItem $env:OneDrive\Desktop -ErrorAction 0 | Remove-Item -Recurse -Force
    Get-ChildItem $env:PUBLIC\Bureaublad -ErrorAction 0 | Remove-Item -Recurse -Force
    Get-ChildItem $env:PUBLIC\Desktop -ErrorAction 0 | Remove-Item -Recurse -Force
    Get-ChildItem $env:USERPROFILE\Desktop -ErrorAction 0 | Remove-Item -Recurse -Force

}

function Remove-DownloadFolderItems {
    [CmdletBinding()]
    param (
        # [Parameter()] [string] $ParameterName
    )

    $DownloadFolder = "$($env:USERPROFILE)\Downloads"

    $Items = Get-ChildItem $DownloadFolder -File

    $RemoveMatch = @()
    $RemoveMatch += "*.ica"
    $RemoveMatch += "*.rdp"

    if ($Items) {
        foreach ($rm in $RemoveMatch) {
            $Items.FullName -like $rm | ForEach-Object {
                Write-Host "Removing: $($_)" -ForegroundColor Yellow
                Remove-Item $_
            }
        }
    }
}

function Remove-GitBranchesNotExistantOnline {
    [CmdletBinding()]
    param (
    )

    $GitConfigPath = Join-Path . ".git"

    if (Test-Path $GitConfigPath) {

        $Ignorelist = @("HEAD", "main", "master")

        git fetch -a -p

        $gitBranchesLocal = ((git branch) -replace '\*', "").Trim()
        $gitBranchesRemote = (git branch -r).Trim()

        if ($gitBranchesLocal -match "main") {
            $defaultBranch = "main"
        } elseif ($gitBranchesLocal -match "master") {
            $defaultBranch = "master"
        } else {
            break
        }
        Write-Host "Switching to branch $($defaultBranch)" -ForegroundColor Green
        git switch $defaultBranch

        "-------------------------"
        Write-Host "Local branches" -ForegroundColor Yellow
        $gitBranchesLocal
        # ($gitBranchesLocal).GetType()
        "-------------------------"
        Write-Host "Remote branches" -ForegroundColor Yellow
        $gitBranchesRemote
        "-------------------------`n`n`n"
        "-------------------------"
        $gitBranchesLocal | ForEach-Object {
            # $_
            # "next`n"
            Write-Host "Checking local branch `"$($_)`"" -ForegroundColor Yellow
            if ($Ignorelist -contains $_) {
                Write-Host "Branch $($_) will be ignored" -ForegroundColor Red
            } else {
                # $branchName = ($_ -split "/")[-1]
                if ($gitBranchesRemote -match $_) {
                    Write-Host "This branch exists remote and locally" -ForegroundColor Green
                } else {
                    Write-Host "This branch exists only locally" -ForegroundColor Red
                    if ((Read-Host -Prompt "Delete branch $_? (y/N)") -eq "y") {
                        git branch -d $_
                    }
                }
            }

            "-------------------------"
        }

    } else {
        Write-Warning "This folder is not git folder"
    }
}

function Resize-MyVolume {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $DriveLetter
    )

    Get-Disk | Update-Disk

    $MaxPartitionSize = Get-Partition -DriveLetter $DriveLetter | Get-PartitionSupportedSize | Select-Object -ExpandProperty SizeMax
    # Write-Host $MaxPartitionSize
    if ((Get-Partition -DriveLetter $DriveLetter).size -eq $MaxPartitionSize) {
        Write-Warning "Partition $DriveLetter is already the maximum size"
    } else {
        Resize-Partition -DriveLetter $DriveLetter -Size $MaxPartitionSize
    }

}

function Send-WOL {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $MacAddress
    )

    Write-Verbose "$($MacAddress)"
    Start-Process "$($PSScriptRoot)\..\Resources\wakemeonlan\WakeMeOnLan.exe" -ArgumentList "/wakeup $($MacAddress)" -Wait -NoNewWindow


}


function Set-ChocoPinList {
    [CmdletBinding()]
    param (
        # [Parameter(Mandatory)] [ValidateSet("Appels", "Peren")] [string] $ParameterName
    )

    $ChocoPinListFile = "$($env:OneDrive)\Backup\ChocoPinList.json"

    if (Test-Path $ChocoPinListFile) {
        $ChocoPinListImport = Get-Content $ChocoPinListFile | ConvertFrom-Json
        # $ChocoPinListImport
    }

    $ChocoPinList = choco pin list --limit-output

    $Output = @()
    $Output += $ChocoPinListImport
    $ChocoPinList | ForEach-Object {

        $app = ($_).split('|')[0]

        if ($ChocoPinListImport -notcontains $app) {
            $Output += $app
        }

    }

    $Output | ConvertTo-Json | Out-File $ChocoPinListFile


    $Output | ForEach-Object {
        choco pin add --name=`'$_`' | Out-Null
    }
}

function Set-ClevoPowerMode {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [ValidateSet("Quiet", "Powersaving", "Performance", "Entertainment")] [string] $PowerMode
    )

    Start-Process $PSScriptRoot\..\resources\opencontrol\opencontrol.exe -ArgumentList "Power $PowerMode" -NoNewWindow -Wait

}

function Set-IPAddress {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ParameterSetName = 'IP')] [string] $IP,
        [Parameter(Mandatory, ParameterSetName = 'IP')] [string] $Prefix,
        [Parameter(Mandatory, ParameterSetName = 'IP')] [string] $Gateway,
        [Parameter(Mandatory, ParameterSetName = 'IP')] [string[]] $DNSServers,
        [Parameter(ParameterSetName = 'Dynamic')] [switch] $Dynamic,
        [Parameter()] [int] $IfIndex
    )

    if (-not($IfIndex)) {
        if ((Get-NetAdapter).count -gt 1) {
            Get-NetAdapter
            Write-Host "Choose interface index" -ForegroundColor Yellow
            $IfIndex = Read-Host -Prompt "Choose interface index"
            if (Get-NetAdapter -ifIndex $IfIndex -ea 0) {
            } else {
                Write-Host "Interface with index $IfIndex does not exist" -ForegroundColor Red
                Remove-Variable IfIndex
            }
        } else {
            $IfIndex = (Get-NetAdapter).InterfaceIndex
        }
    }


    if ($IfIndex) {
        if ($Dynamic) {
            Remove-NetIPAddress -InterfaceIndex $IfIndex -Confirm:$false
            Remove-NetRoute -ifIndex $IfIndex -Confirm:$false
            Set-DnsClientServerAddress -ResetServerAddresses -InterfaceIndex $IfIndex
            Set-NetIPInterface -ifIndex $IfIndex -Dhcp Enabled
            ipconfig.exe /renew
        } else {
            Remove-NetIPAddress -InterfaceIndex $IfIndex -Confirm:$false
            Remove-NetRoute -ifIndex $IfIndex -Confirm:$false
            New-NetIPAddress -IPAddress $IP -DefaultGateway $Gateway -PrefixLength $Prefix -InterfaceIndex $IfIndex
            Set-DnsClientServerAddress -ServerAddresses $DNSServers -InterfaceIndex $IfIndex
        }
    }




}

function Set-Mylocation {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [ValidateSet("Git", "DevOps", "OneDrive", "WindowsPowershell", "WindowsPowershellProgramFiles", "Downloads", "MyDocuments", "SharePoint", "SharePoint2", "Powershell", "PowershellProgramFiles", "CameraRoll")] [string] $Folder,
        [Parameter()] [switch] $OpenInExplorer
    )


    switch ($Folder) {
        "Git" {
            $Path = "$([Environment]::GetFolderPath("UserProfile"))\Git"
            if(-not(Test-Path $Path)) {
                New-Item $Path -ItemType Directory
            }
        }
        "DevOps" {
            $Path = "$([Environment]::GetFolderPath("UserProfile"))\DevOps"
            if (-not(Test-Path $Path)) {
                New-Item $Path -ItemType Directory
            }
        }
        "OneDrive" {
            $Path = $env:OneDrive
        }
        "WindowsPowershell" {
            $Path = "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell"
        }
        "WindowsPowershellProgramFiles" {
            $Path = "$([Environment]::GetFolderPath("ProgramFiles"))\WindowsPowerShell"
        }
        "Powershell" {
            $Path = "$([Environment]::GetFolderPath("MyDocuments"))\PowerShell"
        }
        "PowershellProgramFiles" {
            $Path = "$([Environment]::GetFolderPath("ProgramFiles"))\PowerShell"
        }
        "Downloads" {
            $Path = "$([Environment]::GetFolderPath("UserProfile"))\Downloads"
        }
        "MyDocuments" {
            $Path = "$([Environment]::GetFolderPath("MyDocuments"))"
        }
        "SharePoint" {
            $RegKey = "HKCU:\SOFTWARE\Microsoft\OneDrive\Accounts\Business1\Tenants"
            if (!(Test-Path $RegKey)) {
                break
            }
            $Regpath = Get-ChildItem $RegKey | Where-Object PSChildName -NotLike "*OneDrive*"
            if (!($Regpath)) {
                break
            }
            $Regpath2 = $Regpath[0].Property[0]
            $Regpath3 = Split-Path $Regpath2
            $Path = $Regpath3

        }
        "SharePoint2" {
            $RegKey = "HKCU:\SOFTWARE\Microsoft\OneDrive\Accounts\Business1\Tenants"
            if (!(Test-Path $RegKey)) {
                break
            }
            $Regpath = Get-ChildItem $RegKey | Where-Object PSChildName -NotLike "*OneDrive*"
            if (!($Regpath)) {
                break
            }
            $Regpath2 = $Regpath[1].Property[0]
            $Regpath3 = Split-Path $Regpath2
            $Path = $Regpath3

        }
        "CameraRoll" {
            $Date = Get-Date
            $Path = "$($env:OneDrive)\Pictures\Camera Roll\$($Date.Year)\$(Get-Date -Format "MM")"
            if(!(Test-Path $Path)) {
                $Path = "$($env:OneDrive)\Pictures\Camera Roll\$($Date.Year)"
                if(!(Test-Path $Path)) {
                    $Path = "$($env:OneDrive)\Pictures\Camera Roll"
                }
            }
        }
    }

    # return $Path
    if (Test-Path $Path) {
        Set-Location $Path
        if ($OpenInExplorer) {
            explorer.exe $Path
        }
    } else {
        Write-Error "$Path not found"
    }

}

function Set-OnedriveItemAvailability {
    [CmdletBinding()]
    param (
        [Parameter()] [ValidateSet("Online", "Offline", "FreeUpSpace")] [string] $Setting = "Offline",
        [Parameter()] [string] $Path = ((Get-Location).Path),
        [Parameter()] [string] $Filter
    )


    $loc = Get-Location

    Set-Location $Path
    if ($Filter) {
        switch ($Setting) {
            "Offline" {
                attrib +P /S /D $Filter
            }
            "Online" {
                attrib -P /S /D $Filter
            }
            "FreeUpSpace" {
                attrib -P +U /S /D $Filter
            }
        }
    } else {
        switch ($Setting) {
            "Offline" {
                attrib +P /S /D
            }
            "Online" {
                attrib -P /S /D
            }
            "FreeUpSpace" {
                attrib -P +U /S /D
            }
        }
    }

    Set-Location $loc

}

function Set-PSWindowsTitle {
    param (
        [Parameter(Mandatory = $true)] [ArgumentCompleter( {
                param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
                $possibleValues = @('HyperV', 'WI')
                $possibleValues | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { $_ }
            })] [string] $Title
    )

    $host.ui.RawUI.WindowTitle = $Title

}

function Set-Scaling {
    # Posted by IanXue-MSFT on
    # https://learn.microsoft.com/en-us/answers/questions/197944/batch-file-or-tool-like-powertoy-to-change-the-res.html
    # $scaling = 0 : 100% (default)
    # $scaling = 1 : 125%
    # $scaling = 2 : 150%
    # $scaling = 3 : 175%
    param($scaling)
    $source = @'
    [DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
    public static extern bool SystemParametersInfo(
                      uint uiAction,
                      int uiParam,
                      uint pvParam,
                      uint fWinIni);
'@

    $apicall = Add-Type -MemberDefinition $source -Name WinAPICall -Namespace SystemParamInfo -PassThru
    $apicall::SystemParametersInfo(0x009F, $scaling, $null, 1) | Out-Null
}



function Show-TrayIcons {
    [CmdletBinding()]
    param (
        [Parameter()] [switch] $Hide
    )

    $regLocation = "HKCU:\Control Panel\NotifyIconSettings\"

    if (Test-Path $regLocation) {
        $TrayIcons = Get-ChildItem $regLocation

        # $TrayIcons

        foreach ($t in $TrayIcons) {

            Write-Verbose $t.Name
            # $IsPromoted = Get-ItemProperty "registry::$($t)" -Name IsPromoted | Select-Object -ExpandProperty IsPromoted

            if ($Hide) {
                $value = 0
            } else {
                $value = 1
            }
            # if ($IsPromoted -ne 1) {
                New-ItemProperty "registry::$($t)" -Name IsPromoted -Value $value -Force | Out-Null
            # }
            # Get-ItemProperty "$($reglocation)\$($t)" -Name IsPromoted

        }
    } else {
        Write-Warning "Reg key `"$($regLocation)`" not found"
    }



}


function ShutdownTimer {

    [CmdletBinding()]


    param (
        [parameter()] [int] $Time = 2,
        [Parameter()] [string] [ValidateSet("Seconds", "Minutes", "Hours")] $HMS = "Seconds",
        [parameter()] [string] $At,
        [parameter()] [switch] $Restart,
        [parameter()] [switch] $Lock
    )


    switch ($HMS) {
        "Hours" {
            $Seconds = ($time * 60 * 60)
        }
        "Minutes" {
            $Seconds = ($time * 60)
        }
        "Seconds" {
            $Seconds = $time
        }
    }





    if ($At) {

        if ($At.Length -eq 3) {
            # $At

            $At = "0$($At)"
            $At

        } elseif ($At.Length -eq 4) {

        } else {
            Write-Error "`"At`" not in correct format"
        }

        $ShutdownHour = $At[0] + $At[1]
        $ShutdownHour = [int]$ShutdownHour
        # $ShutdownHour

        $ShutdownMinute = $At[2] + $At[3]
        $ShutdownMinute = [int]$ShutdownMinute
        # $ShutdownMinute

        if ($ShutdownHour -gt 23 -or $ShutdownMinute -gt 59) {
            Write-Error "Time input not correct"
            break
        }

        $EndTime = Get-Date -Hour $ShutdownHour -Minute $ShutdownMinute -Second 00

        if ($EndTime -lt (Get-Date)) {
            $EndTime = $EndTime.AddDays(1)
        }

    } else {
        $EndTime = (Get-Date).AddSeconds($Seconds)
    }


    $i = 0

    while (($TimeRemaining = ($EndTime - (Get-Date))) -gt 0) {

        if (($EndTime - (Get-Date)).TotalSeconds -lt 5) {
            Stop-ProcessSoft msedge -Silent
            Stop-ProcessSoft -ProcessName chrome -Silent
            Stop-ProcessSoft -ProcessName EXCEL -Silent
        }

        if ($Restart) {
            $ActivityText = "Restarting in..."
        } else {
            $ActivityText = "Shutting down in..."
        }
        Write-Progress -Activity $ActivityText -Status $EndTime -SecondsRemaining $TimeRemaining.TotalSeconds
        Start-Sleep 1

        $i++
        if ($Lock -and ($i -eq 2)) {
            $Users = (quser) -ireplace '\s{2,}', ',' | ConvertFrom-Csv
            if (($Users | Where-Object Username -Match ">").Sessionname -eq "console") {
                Rundll32.exe user32.dll, LockWorkStation
            } else {
                tsdiscon.exe
            }
        }


    }

    if ($Restart) {
        Restart-Computer -Force
    } else {
        Stop-Computer -Force
    }

}


function Start-PlayingSilence {

    # version 1

    #Add-Type -AssemblyName presentationCore
    #$MediaPlayer = New-Object system.windows.media.MediaPlayer
    #$MediaPlayer.open("C:\Users\Jaap\OneDrive - JaapTerlouw\Documents\Silent.wav")
    $MediaPlayer = New-Object System.Media.SoundPlayer
    $MediaPlayer.SoundLocation = "C:\Users\Jaap\OneDrive - JaapTerlouw\Documents\Silent.wav"
    $MediaPlayer.PlayLooping()

}

function Start-RDP {
    [CmdletBinding()]
    param (
        [Parameter()] [string] $ComputerName,
        [Parameter()] [int] $Port = 3389,
        [Parameter()] [ValidateSet("1080", "1440", "Fullscreen")] [string] $Resolution = "1440",
        [Parameter()] [ValidateSet("AudioDontPlay", "AudioRemote", "AudioRedirect")] [string] $AudioMode = "AudioRemote",
        [Parameter()] [switch] $NoCredSSP
    )



    $WindowSizes = [PSCustomObject]@{
        1080       = @"
screen mode id:i:1
desktopwidth:i:1917
desktopheight:i:1006

"@


        1440       = @"
screen mode id:i:1
desktopwidth:i:2558
desktopheight:i:1366

"@


        Fullscreen = @"
screen mode id:i:2

"@


    }

    $AudioModes = [PSCustomObject]@{
        AudioDontPlay = @"
audiomode:i:2

"@

        AudioRemote   = @"
audiomode:i:1

"@

        AudioRedirect = @"
audiomode:i:0

"@

    }


    if ($NoCredSSP) {
        $CredSSPContent = @"
enablecredsspsupport:i:0

"@

    } else {
        $CredSSPContent = ""
    }


    if ($ComputerName) {
        $ContentFullAddress = @"
full address:s:$($ComputerName):$($Port)

"@

    } else {
        $ContentFullAddress = @"
full address:s:

"@

    }


    $DefaultRdpFileContent = @"
use multimon:i:0
session bpp:i:32
winposstr:s:0, 3, 0, 0, 800, 600
compression:i:1
keyboardhook:i:1
audiocapturemode:i:0
videoplaybackmode:i:1
connection type:i:7
networkautodetect:i:1
bandwidthautodetect:i:1
displayconnectionbar:i:1
enableworkspacereconnect:i:0
disable wallpaper:i:0
allow font smoothing:i:1
allow desktop composition:i:1
disable full window drag:i:0
disable menu anims:i:0
disable themes:i:0
disable cursor setting:i:0
bitmapcachepersistenable:i:1
redirectprinters:i:0
redirectcomports:i:0
redirectsmartcards:i:1
redirectclipboard:i:1
redirectposdevices:i:0
drivestoredirect:s:
autoreconnection enabled:i:1
authentication level:i:2
prompt for credentials:i:0
negotiate security layer:i:1
remoteapplicationmode:i:0
alternate shell:s:
shell working directory:s:
gatewayhostname:s:
gatewayusagemethod:i:4
gatewaycredentialssource:i:4
gatewayprofileusagemethod:i:0
promptcredentialonce:i:0
gatewaybrokeringtype:i:0
use redirection server name:i:0
rdgiskdcproxy:i:0
kdcproxyname:s:

"@








    $RdpFileContent = $DefaultRdpFileContent + $ContentFullAddress + $WindowSizes.$Resolution + $AudioModes.$AudioMode + $CredSSPContent
    if ($ComputerName) {
        $Outfile = "$($env:TEMP)\$($Computername).rdp"
    } else {
        $Outfile = "$($env:TEMP)\RDP.rdp"
    }

    $RdpFileContent | Out-File $Outfile

    Start-Process $Outfile

    Get-Content $Outfile
    # Remove-Item $Outfile -Force

}


function Startup {

    [CmdletBinding()]
    param (

    )

    Set-Location "\"

    # $GitFolder = "$($env:USERPROFILE)\git"

    # $ImportedModules = @()

    # Get-ChildItem $GitFolder -Depth 1 -File *.psd1 | ForEach-Object {
    # $ImportedModules += Import-Module $_.FullName -Force -Global
    # }
    # $Modules = Get-ChildItem $GitFolder -Directory

    # $Modules = @(
    # # "JaapsTools"
    # # "OfficeAndAzureTools"
    # # "WindowsSandbox"
    # )


    # foreach ($d in ($Modules)) {
    # $psd1file = "$($d.FullName)\$($d.Name).psd1"
    # if (Test-Path $psd1file) {
    # $ImportedModules += Import-Module $psd1file -Force -Global
    # }
    # }


}


function Stop-ProcessSoft {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [string] $ProcessName,
        [Parameter()] [switch] $Silent
    )

    if (!(Get-Process $ProcessName -ErrorAction 0)) {
        if (-not($Silent)) {
            Write-Warning "Process $($ProcessName) not found"
        }
    } else {
        1..100 | ForEach-Object {
            $Process = (Get-Process $ProcessName -ErrorAction 0)
            if ($Process) {
                $Process.CloseMainWindow() | Out-Null
            }
        }
    }

}


$scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $ps = Get-Process "$wordToComplete*"
    $ps.Name | ForEach-Object {
        if ($_ -match " ") {
            "'$_'"
        } else {
            $_
        }
    }
}
Register-ArgumentCompleter -CommandName Stop-ProcessSoft -ParameterName ProcessName -ScriptBlock $scriptblock



function Switch-HyperV {

    [CmdletBinding()]
    param (
        [Parameter()][switch]$Enable
        ,[Parameter()][switch]$Disable
    )

    if($Enable -and $Disable) {
        Write-Error "Mag niet allebei"
        break
    }
    if(!($Enable) -and (!($Disable))) {
        Write-Error "Geef parameter Enable of Disable op"
        break
    }

    if($Enable) {
        bcdedit /set hypervisorlaunchtype auto
    }

    if($Disable) {
        bcdedit /set hypervisorlaunchtype off
    }
}

function Sync-GitRepos {
    [CmdletBinding()]
    param (
        [Parameter()] [switch] $Pull,
        [Parameter()] [switch] $RunMaintenance,
        [Parameter()] [switch] $CommitAndPush,
        [Parameter()] [string[]] $GitFolder = @("$($env:USERPROFILE)\Git", "$($env:USERPROFILE)\DevOps")
    )

    $currentLocation = $PWD.Path

    foreach ($gf in $GitFolder) {

        Write-Host "$($gf)" -ForegroundColor Yellow

        if (Test-Path $gf) {

            "---------------------------------"

            Get-ChildItem $gf -Directory | ForEach-Object {

                $GitConfigPath = Join-Path $_.FullName ".git"
                if (Test-Path $GitConfigPath) {
                    Set-Location $_.FullName
                    Write-Host $_.Name -ForegroundColor Green
                    "`n"
                    Write-Host "get all branches and switch to main/master" -ForegroundColor Yellow
                    $Branches = git branch -a
                    if ($Branches -match "main") {
                        git switch main
                    } elseif ($Branches -match "master") {
                        git switch master
                    } else {
                        Write-Warning "Master or main branch not found, leaving branch unchanged"
                    }

                    Write-Host "Fetch all and prune (git fetch --all --prune)" -ForegroundColor Yellow
                    git fetch --all --prune
                    Write-Host "Run git status (git status)" -ForegroundColor Yellow
                    git status
                    Write-Host "Get git branches (git branch -a)" -ForegroundColor Yellow
                    git branch -a

                    if ($Pull) {
                        Write-Host "Git pull" -ForegroundColor Yellow
                        git pull
                    }
                    if ($RunMaintenance) {
                        Write-Host "Run git lfs prune" -ForegroundColor Yellow
                        git lfs prune
                        Write-Host "Run git gc" -ForegroundColor Yellow
                        git gc
                        Write-Host "Run git fsck" -ForegroundColor Yellow
                        git fsck
                    }
                    if ($CommitAndPush) {
                        git add .
                        git commit -a -m 'Commit All'
                        git push
                    }
                    "---------------------------------------------------------------`n"

                }

            }

            Set-Location $gf

        }
    }
    Set-Location $currentLocation

}

function Uninstall-OldInstalledModules {
    [CmdletBinding()]
    param (

    )
    Start-Job -ScriptBlock {
        foreach ($Module in Get-InstalledModule) {
            Get-InstalledModule $Module.Name -AllVersions | Where-Object { $_.Version -ne $Module.Version } | ForEach-Object {
                Write-Output "Uninstalling module $($_.Name) version $($_.Version)"
                Uninstall-Module -Name $_.Name -MaximumVersion $_.Version -Force
            }
        } -ArgumentList $m

    }
}

function Update-InstalledModule {
    [CmdletBinding()]
    param (
    )

    $excludeList = "Az.|Microsoft.Graph.Beta."

    Get-InstalledModule | Where-Object Name -NotMatch $excludeList | ForEach-Object {

        Write-Output $_.Name
        Update-Module $_.Name -AcceptLicense -Scope AllUsers

    }

}

function Update-MyModuleManifest {
    [CmdletBinding()]
    param (
        [Parameter()]
        [ArgumentCompleter( {
                param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
                Get-ChildItem "$($env:USERPROFILE)\Git\$wordToComplete*" -Directory -Name | ForEach-Object { $_ } }
        )]
        [string]
        $Module,
        [Parameter()] [ValidateSet("Minor", "Major", "Build")] [string] $VersionChange = "Build"
    )

    if (!($Module)) {
        $PossibleModulename = Get-Item . | Select-Object -ExpandProperty Name
        $ModuleFile = "$PossibleModulename.psd1"
        Write-Verbose "ModuleFile: $ModuleFile"
        if (Test-Path $ModuleFile) {
            $Module = $PossibleModulename
            Write-Verbose "Module: $Module"
        }
    }

    Write-Verbose "Module: $Module"

    $FullPath = Join-Path "$($env:USERPROFILE)\Git" $Module
    Write-Verbose $FullPath
    Write-Verbose "Test-Path $FullPath"
    if (Test-Path $FullPath) {
        $ModuleFolders = Get-Item $FullPath
    } else {
        throw "No module file found"
        # #Set-Location $MyModulePath
        # $MyModulePath = "$($env:USERPROFILE)\Git"

        # if ($Module) {
        # $ModuleFolders = Get-ChildItem $MyModulePath -Directory -Exclude ".git" | Where-Object Name -EQ $Module
        # } else {
        # $ModuleFolders = Get-ChildItem $MyModulePath -Directory -Exclude ".git", "PackageManagement"
        # }
    }


    foreach ($m in $ModuleFolders) {

        Write-Output "Updating module: $($m.BaseName)"
        Write-Verbose $m.FullName

        $FunctionsToExport = (Get-ChildItem (Join-Path $m.FullName -ChildPath "Functions") -File).BaseName

        Get-ChildItem (Join-Path $m.FullName -ChildPath "Functions") -File | Get-Content | Out-File (Join-Path $m.FullName -ChildPath "Functions.ps1") -Force

        $VariablesToExport = @('-')
        if (Test-Path (Join-Path $m.FullName -ChildPath "Variables")) {
            $VariablesToExport = (Get-ChildItem (Join-Path $m.FullName -ChildPath "Variables")).BaseName
        }

        if (Test-Path (Join-Path $m.FullName -ChildPath "Other\RequiredModules.csv")) {
            $RequiredModules = Import-Csv (Join-Path $m.FullName -ChildPath "Other\RequiredModules.csv") | Select-Object -ExpandProperty Modulename
        } else {
            $RequiredModules = @()
        }

        if (Test-Path (Join-Path $m.FullName -ChildPath "Other\ModuleDescription.txt")) {
            $ModuleDescription = Get-Content (Join-Path $m.FullName -ChildPath "Other\ModuleDescription.txt")
        } else {
            $ModuleDescription = $m.BaseName
        }

        if (Test-Path (Join-Path $m.FullName -ChildPath ($m.BaseName + ".psd1"))) {
            $ModuleInfo = Get-Module $m.FullName -ListAvailable
            if ($VersionChange -eq "Major") {
                $CurrentVersion = $ModuleInfo.Version.Major
                $NewVersion = $CurrentVersion + 1
                $NewVersion = "$($NewVersion).0.0"
            } elseif ($VersionChange -eq "Minor") {
                $CurrentVersion = $ModuleInfo.Version.Minor
                $NewVersion = $CurrentVersion + 1
                $NewVersion = "$($ModuleInfo.Version.Major).$($NewVersion).0"
            } elseif ($VersionChange -eq "Build") {
                $CurrentVersion = $ModuleInfo.Version.Build
                $NewVersion = $CurrentVersion + 1
                $NewVersion = "$($ModuleInfo.Version.Major).$($ModuleInfo.Version.Minor).$NewVersion"
            }
        } else {

            $NewVersion = "0.0.1"

        }

        $ModuleManifestParameters = @{
            Path              = (Join-Path $m.FullName -ChildPath ($m.BaseName + ".psd1"))
            RootModule        = $m.BaseName + ".psm1"
            ModuleVersion     = $NewVersion
            FunctionsToExport = $FunctionsToExport
            VariablesToExport = $VariablesToExport
            Copyright         = "(c) $((Get-Date).Year) JT. All rights reserved."
        }
        if ($RequiredModules) {
            $ModuleManifestParameters.Add("RequiredModules", $RequiredModules)
        }
        if ($ModuleDescription) {
            $ModuleManifestParameters.Add("Description", $ModuleDescription)
        }

        #$ModuleManifestParameters


        if (Test-Path $ModuleManifestParameters.Path) {
            Update-ModuleManifest @ModuleManifestParameters
        } else {

            $psmContent = @"
(Get-ChildItem "`$PSScriptRoot\Functions") | ForEach-Object {
    . `$_.FullName
}
"@

            $psmContent | Out-File (Join-Path $m.FullName -ChildPath ($m.BaseName + ".psm1"))

            New-ModuleManifest @ModuleManifestParameters

        }


        if (Test-Path (Join-Path $m.FullName ".git")) {
            $CurrentLocation = Get-Location

            Set-Location $m.FullName
            # $m.FullName
            Write-Host "$($m.BaseName).psd1"
            git add ".\$($m.BaseName).psd1"
            git commit -i ".\$($m.BaseName).psd1" -m "Version update"

            Set-Location $CurrentLocation

        }

    }

    Test-ModuleManifest $ModuleManifestParameters.Path | Format-List
    $ModuleManifestParameters | Format-Table

}