class Context {
    [string]$EntryPoint;
    [string]$RootDir;
    [string]$BackupName;
    [string]$UserName;
    [string]$AdminName = "Admin";
    [string]$RunOnceName = "PortValhalla";
    [string] BackupRoot() {
        if (-not $this.RootDir)
        {
            return Join-Path $PSScriptRoot ".." ".." ".." "backup" $this.BackupName;
        }
        else
        {
            return $this.RootDir;
        }
    }

    [string] ArchivePath($name) {
        return Join-Path $this.BackupRoot() "$name.7z";
    }

    [string] FileArchivePath($name) {
        return $this.ArchivePath($(Join-Path "Files" $name));
    }

    [string] SoftwareArchive([string]$softwareName) {
        return $this.ArchivePath($softwareName);
    }

    [void] Backup([string]$sourcePath, [string]$archivePath, [string[]]$arguments) {
        if (Test-Path $archivePath) {
            Remove-Item -Recurse $archivePath;
        }

        Start-Process -WorkingDirectory "$sourcePath" `
            -FilePath "7z" `
            -ArgumentList (
                @(
                    "a",
                    "-xr!desktop.ini",
                    "-xr!thumbs.db",
                    "-xr!Thumbs.db",
                    $archivePath) + $arguments) `
            -Wait `
            -NoNewWindow;
    }

    [void] Restore([string]$archivePath, [string]$destinationPath) {
        if (-not (Test-Path -PathType Container $destinationPath)) {
            New-Item -ItemType Directory "$destinationPath";
        }

        Start-Process -WorkingDirectory "$destinationPath" `
            -FilePath "7z"
            -ArgumentList "x" `
            -Wait `
            -NoNewWindow;
    }

    [string] GetTempDirectory() {
        $tempDir = Join-Path $([System.IO.Path]::GetTempPath()) $([System.IO.Path]::GetRandomFileName());
        $null = New-Item -ItemType Directory $tempDir;
        return $tempDir;
    }

    [void] ProcessDefaultUserKey([System.Action[Microsoft.Win32.RegistryKey]] $action) {
        $root = "HKLM:\DefaultUser";
        $regRoot = $root.Replace(":", "");
        $hivePath = "$env:SystemDrive\Users\Default\NTUSER.dat"
        $null = & reg load $regRoot $hivePath;
        $action.Invoke((Get-Item $root));
        reg unload $regRoot;
    }

    [void] ProcessLogonKey([System.Action[Microsoft.Win32.RegistryKey]] $action) {
        $key = Get-Item "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon";
        $action.Invoke($key);
    }

    [Microsoft.Win32.RegistryKey] GetRunOnceKey() {
        return $this.GetRunOnceKey($null);
    }

    [Microsoft.Win32.RegistryKey] GetRunOnceKey([Microsoft.Win32.RegistryKey] $userKey) {
        if (-not $userKey) {
            $userKey = Get-Item "HKCU:\";
        }

        Push-Location $userKey.PSPath;
        $runOncePath = "Software\Microsoft\Windows\CurrentVersion\RunOnce";

        if (-not (Test-Path $runOncePath)) {
            New-Item $runOncePath;
        }

        $result = Get-Item $runOncePath;
        Pop-Location;
        return $result;
    }

    [void] RegisterReboot() {
        $this.RegisterReboot($null);
    }

    [void] RegisterReboot([Microsoft.Win32.RegistryKey] $userKey) {
        $null = Set-ItemProperty -Path $this.GetRunOnceKey($userKey).PSPath -Name $this.RunOnceName -Value "pwsh `"$($this.EntryPoint)`"" -Type "ExpandString";
    }

    [void] RegisterNewUserReboot() {
        $this.ProcessDefaultUserKey({ param ($root) $this.RegisterReboot($root); });
    }

    [void] DeregisterNewUserReboot() {
        $this.ProcessDefaultUserKey({ param ($root) Remove-Item -Path $this.GetRunOnceKey($root).PSPath });
    }

    [void] SetAutologin($user, $pw) {
        $this.ProcessLogonKey(
            {
                param ($logon)
                $path = $logon.PSPath;
                Set-ItemProperty $path -Name "AutoAdminLogon" 1;
                Set-ItemProperty $path -Name "DefaultUserName" $user;

                if ($password) {
                    Set-ItemProperty $path -Name "DefaultPassword" $password;
                } else {
                    Remove-ItemProperty $path -Name "DefaultPassword";
                }
            });
    }

    [void] RemoveAutologin() {
        $this.ProcessLogonKey(
            {
                param ($logon)
                $path = $logon.PSPath;
                Set-ItemProperty $path -Name "AutoAdminLogon" 0;
                Remove-ItemProperty $path -Name "DefaultDomainName";
                Remove-ItemProperty $path -Name "DefaultUserName";
                Remove-ItemProperty $path -Name "DefaultPassword";
            });
    }

    [void] Reboot() {
        Write-Host "Restarting Computer...";
        $this.RegisterReboot();
        Restart-Computer;
        exit;
    }
}