; Universal Printer Installer for Network Printers
; Installs HP/Xerox network printers from ShopDB via TCP/IP

[Setup]
AppId={{7B9C3E41-5F2A-4D8B-9E1C-8A6D4F3E2B1A}}
AppName=WJDT GE Aerospace Printer Installer
AppVersion=1.0
AppPublisher=WJDT
AppPublisherURL=http://tsgwp00524.logon.ds.ge.com
AppSupportURL=http://tsgwp00524.logon.ds.ge.com
AppUpdatesURL=http://tsgwp00524.logon.ds.ge.com
CreateAppDir=no
ChangesAssociations=no
PrivilegesRequired=admin
; Force 64-bit installation mode on 64-bit compatible systems
ArchitecturesInstallIn64BitMode=x64compatible
OutputDir=C:\Users\570005354\Downloads\Output
OutputBaseFilename=PrinterInstaller
SolidCompression=yes
WizardStyle=modern
SetupIconFile=gea-logo.ico
WizardImageFile=patrick.bmp
WizardSmallImageFile=patrick-sm.bmp
CreateUninstallRegKey=no
DisableWelcomePage=no

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Messages]
WelcomeLabel2=This utility will install network printers.%n%nFeatures:%n- Query HP/Xerox printers from ShopDB%n- Install via TCP/IP using FQDN or IP address%n- Automatic driver installation (HP Universal PCL6, Xerox Global Print Driver)%n%nClick Next to continue.

[Files]
; HP Universal Print Driver x64
Source: "drivers\hp_x64\*"; DestDir: "{tmp}\hp_drivers_x64"; Flags: ignoreversion recursesubdirs; Check: Is64BitInstallMode

; HP Universal Print Driver x32
Source: "drivers\hp_x32\*"; DestDir: "{tmp}\hp_drivers_x32"; Flags: ignoreversion recursesubdirs; Check: not Is64BitInstallMode

; Xerox Universal Print Driver x64
Source: "drivers\xerox_x64\*"; DestDir: "{tmp}\xerox_drivers_x64"; Flags: ignoreversion recursesubdirs; Check: Is64BitInstallMode

; Xerox Universal Print Driver x32
Source: "drivers\xerox_x32\*"; DestDir: "{tmp}\xerox_drivers_x32"; Flags: ignoreversion recursesubdirs; Check: not Is64BitInstallMode

[Code]
var
  PrinterSelectionPage: TInputOptionWizardPage;
  PrinterDataArray: array of record
    PrinterName: String;
    FQDN: String;
    IPAddress: String;
    Vendor: String;
    Model: String;
    IsInstalled: Boolean;
  end;

// Query database for printers via web API
function QueryPrinters(): Boolean;
var
  ResultCode: Integer;
  TempScriptPath, TempOutputPath: String;
  PSScript: String;
  Output: AnsiString;
  Lines: TStringList;
  I: Integer;
  Fields: TStringList;
begin
  Result := False;

  // Build PowerShell script to query database API
  PSScript :=
    'try {' + #13#10 +
    '    $url = "https://tsgwp00525.rd.ds.ge.com/shopdb/api_printers.asp"' + #13#10 +
    '    # Allow TLS 1.2 for HTTPS connections' + #13#10 +
    '    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12' + #13#10 +
    '    $response = Invoke-WebRequest -Uri $url -Method GET -UseBasicParsing -UseDefaultCredentials -ErrorAction Stop' + #13#10 +
    '    $printers = $response.Content | ConvertFrom-Json' + #13#10 +
    '    foreach ($printer in $printers) {' + #13#10 +
    '        if (($printer.isactive -eq 1 -or $printer.isactive -eq $true) -and ($printer.vendor -eq "HP" -or $printer.vendor -eq "Xerox" -or $printer.vendor -eq "HID")) {' + #13#10 +
    '            $name = $printer.printerwindowsname' + #13#10 +
    '            $vendor = $printer.vendor' + #13#10 +
    '            $model = $printer.modelnumber' + #13#10 +
    '            # Use FQDN or IP if available, otherwise default to USB for card printers' + #13#10 +
    '            if ($printer.fqdn -and $printer.fqdn -ne "" -and $printer.fqdn -ne "USB") {' + #13#10 +
    '                $address = $printer.fqdn' + #13#10 +
    '            } elseif ($printer.ipaddress -and $printer.ipaddress -ne "") {' + #13#10 +
    '                $address = $printer.ipaddress' + #13#10 +
    '            } else {' + #13#10 +
    '                $address = "USB"' + #13#10 +
    '            }' + #13#10 +
    '            if ($address -and $address -ne "") {' + #13#10 +
    '                Write-Output "$name|$address|$vendor|$model"' + #13#10 +
    '            }' + #13#10 +
    '        }' + #13#10 +
    '    }' + #13#10 +
    '} catch {' + #13#10 +
    '    Write-Error "Failed to query printers: $_"' + #13#10 +
    '    exit 1' + #13#10 +
    '}';

  TempScriptPath := ExpandConstant('{tmp}\query_printers.ps1');
  TempOutputPath := ExpandConstant('{tmp}\printers_output.txt');

  SaveStringToFile(TempScriptPath, PSScript, False);

  if not Exec('powershell.exe',
       '-NoProfile -ExecutionPolicy Bypass -Command "& ''' + TempScriptPath + ''' | Out-File -FilePath ''' + TempOutputPath + ''' -Encoding ASCII"',
       '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
  begin
    DeleteFile(TempScriptPath);
    Exit;
  end;

  DeleteFile(TempScriptPath);

  if not FileExists(TempOutputPath) then
    Exit;

  if not LoadStringFromFile(TempOutputPath, Output) then
  begin
    DeleteFile(TempOutputPath);
    Exit;
  end;

  DeleteFile(TempOutputPath);

  Lines := TStringList.Create;
  try
    Lines.Text := Output;

    // Count valid lines
    I := 0;
    while I < Lines.Count do
    begin
      if (Trim(Lines[I]) = '') or (Pos('|', Lines[I]) = 0) then
        Lines.Delete(I)
      else
        I := I + 1;
    end;

    if Lines.Count = 0 then
      Exit;

    SetArrayLength(PrinterDataArray, Lines.Count);

    for I := 0 to Lines.Count - 1 do
    begin
      Fields := TStringList.Create;
      try
        Fields.Delimiter := '|';
        Fields.StrictDelimiter := True;
        Fields.DelimitedText := Trim(Lines[I]);

        if Fields.Count >= 4 then
        begin
          PrinterDataArray[I].PrinterName := Trim(Fields[0]);
          PrinterDataArray[I].FQDN := Trim(Fields[1]);
          PrinterDataArray[I].IPAddress := Trim(Fields[1]);
          PrinterDataArray[I].Vendor := Trim(Fields[2]);
          PrinterDataArray[I].Model := Trim(Fields[3]);
          Result := True;
        end;
      finally
        Fields.Free;
      end;
    end;
  finally
    Lines.Free;
  end;
end;

// Check which printers are already installed
function CheckInstalledPrinters(): Boolean;
var
  ResultCode: Integer;
  TempScriptPath, TempOutputPath: String;
  PSScript: String;
  Output: AnsiString;
  Lines: TStringList;
  I, J: Integer;
begin
  Result := False;

  // Build PowerShell script to get installed printers
  PSScript :=
    'try {' + #13#10 +
    '    Get-Printer | Select-Object -ExpandProperty Name | ForEach-Object { Write-Output $_ }' + #13#10 +
    '} catch { exit 1 }';

  TempScriptPath := ExpandConstant('{tmp}\check_installed.ps1');
  TempOutputPath := ExpandConstant('{tmp}\installed_output.txt');

  SaveStringToFile(TempScriptPath, PSScript, False);

  if not Exec('powershell.exe',
       '-NoProfile -ExecutionPolicy Bypass -Command "& ''' + TempScriptPath + ''' | Out-File -FilePath ''' + TempOutputPath + ''' -Encoding ASCII"',
       '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
  begin
    DeleteFile(TempScriptPath);
    Exit;
  end;

  DeleteFile(TempScriptPath);

  if not FileExists(TempOutputPath) then
    Exit;

  if not LoadStringFromFile(TempOutputPath, Output) then
  begin
    DeleteFile(TempOutputPath);
    Exit;
  end;

  DeleteFile(TempOutputPath);

  Lines := TStringList.Create;
  try
    Lines.Text := Output;

    // Check each printer in database against installed printers
    for I := 0 to GetArrayLength(PrinterDataArray) - 1 do
    begin
      PrinterDataArray[I].IsInstalled := False;

      for J := 0 to Lines.Count - 1 do
      begin
        if Trim(Lines[J]) = PrinterDataArray[I].PrinterName then
        begin
          PrinterDataArray[I].IsInstalled := True;
          Break;
        end;
      end;
    end;

    Result := True;
  finally
    Lines.Free;
  end;
end;

// Initialize wizard pages
procedure InitializeWizard();
var
  I: Integer;
  DisplayText: String;
  AutoSelectPrinter: String;
  FoundMatch: Boolean;
begin
  // Query printers from database
  if not QueryPrinters() then
  begin
    MsgBox('Failed to query printers from ShopDB database.' + #13#10#13#10 +
           'Please ensure:' + #13#10 +
           '- You have network connectivity' + #13#10 +
           '- The ShopDB server (tsgwp00525.rd.ds.ge.com) is accessible' + #13#10 +
           '- You are connected to the GE network' + #13#10 +
           '- The API endpoint is running',
           mbError, MB_OK);
    WizardForm.Close;
    Exit;
  end;

  if GetArrayLength(PrinterDataArray) = 0 then
  begin
    MsgBox('No printers found in the database.' + #13#10#13#10 +
           'This installer supports:' + #13#10 +
           '- HP and Xerox network printers (TCP/IP)' + #13#10 +
           '- HID card printers (USB)',
           mbInformation, MB_OK);
    WizardForm.Close;
    Exit;
  end;

  // Check which printers are already installed
  CheckInstalledPrinters();

  // Check for command-line parameter to auto-select printer(s)
  // Usage: PrinterInstaller.exe /PRINTER="PrinterName"
  // Or multiple: /PRINTER="Printer1,Printer2,Printer3"
  AutoSelectPrinter := ExpandConstant('{param:PRINTER|}');
  FoundMatch := False;

  // Create printer selection page
  PrinterSelectionPage := CreateInputOptionPage(wpWelcome,
    'Manage Network Printers',
    'Select printers to install or uncheck to remove',
    'Checked printers will be installed. Unchecked printers that are currently installed will be removed. ' +
    'Already-installed printers are pre-checked.',
    False,
    False);

  // Add printers to selection page
  for I := 0 to GetArrayLength(PrinterDataArray) - 1 do
  begin
    if PrinterDataArray[I].PrinterName <> '' then
    begin
      if PrinterDataArray[I].IsInstalled then
        DisplayText := '[INSTALLED] '
      else
        DisplayText := '';

      DisplayText := DisplayText + PrinterDataArray[I].PrinterName + ' - ' +
                     PrinterDataArray[I].Vendor + ' ' +
                     PrinterDataArray[I].Model + ' (' +
                     PrinterDataArray[I].FQDN + ')';
      PrinterSelectionPage.Add(DisplayText);

      // Check if this printer should be auto-selected via command line
      if AutoSelectPrinter <> '' then
      begin
        // First try exact match against each comma-separated printer name
        if Pos(',', AutoSelectPrinter) > 0 then
        begin
          // Multiple printers separated by comma - check for exact matches
          if Pos(',' + PrinterDataArray[I].PrinterName + ',', ',' + AutoSelectPrinter + ',') > 0 then
          begin
            PrinterSelectionPage.Values[I] := True;
            FoundMatch := True;
          end
          else if PrinterDataArray[I].IsInstalled then
            PrinterSelectionPage.Values[I] := True
          else
            PrinterSelectionPage.Values[I] := False;
        end
        else
        begin
          // Single printer - use partial match (original behavior)
          if Pos(AutoSelectPrinter, PrinterDataArray[I].PrinterName) > 0 then
          begin
            PrinterSelectionPage.Values[I] := True;
            FoundMatch := True;
          end
          else if PrinterDataArray[I].IsInstalled then
            PrinterSelectionPage.Values[I] := True
          else
            PrinterSelectionPage.Values[I] := False;
        end;
      end
      // No auto-select parameter - pre-check if already installed
      else if PrinterDataArray[I].IsInstalled then
      begin
        PrinterSelectionPage.Values[I] := True;
      end
      else
      begin
        PrinterSelectionPage.Values[I] := False;
      end;
    end;
  end;

  // If command-line parameter was provided but no match found, show warning
  if (AutoSelectPrinter <> '') and (not FoundMatch) then
  begin
    MsgBox('Printer "' + AutoSelectPrinter + '" not found in the database.' + #13#10#13#10 +
           'Please select a printer from the list manually.',
           mbInformation, MB_OK);
  end;
end;

// Validate selection before continuing
function NextButtonClick(CurPageID: Integer): Boolean;
var
  I: Integer;
  HasChange: Boolean;
begin
  Result := True;

  if CurPageID = PrinterSelectionPage.ID then
  begin
    HasChange := False;

    // Check if any printer state has changed
    for I := 0 to GetArrayLength(PrinterDataArray) - 1 do
    begin
      // If checkbox state differs from installation state, we have a change
      if PrinterSelectionPage.Values[I] <> PrinterDataArray[I].IsInstalled then
      begin
        HasChange := True;
        Break;
      end;
    end;

    if not HasChange then
    begin
      MsgBox('No changes detected. Select printers to install or uncheck installed printers to remove them.',
             mbInformation, MB_OK);
      Result := False;
    end;
  end;
end;

// Install printers during installation phase (after files are extracted)
procedure CurStepChanged(CurStep: TSetupStep);
var
  I: Integer;
  PSScript: String;
  TempScriptPath: String;
  ResultCode: Integer;
  TotalCount: Integer;
  IsX64: Boolean;
  HPDriverPath, XeroxDriverPath: String;
begin
  // Run after files are extracted but before finishing
  if CurStep = ssPostInstall then
  begin
    // Check if there are any changes to process (installs or removals)
    TotalCount := 0;
    for I := 0 to GetArrayLength(PrinterDataArray) - 1 do
    begin
      // Count changes: either installing (not installed but checked) or removing (installed but unchecked)
      if (not PrinterDataArray[I].IsInstalled and PrinterSelectionPage.Values[I]) or
         (PrinterDataArray[I].IsInstalled and not PrinterSelectionPage.Values[I]) then
        TotalCount := TotalCount + 1;
    end;

    if TotalCount = 0 then
      Exit;

    // Determine architecture and driver paths
    IsX64 := Is64BitInstallMode;

    if IsX64 then
    begin
      HPDriverPath := ExpandConstant('{tmp}\hp_drivers_x64');
      XeroxDriverPath := ExpandConstant('{tmp}\xerox_drivers_x64\UNIV_5.1055.3.0_PCL6_x64_Driver.inf\x3UNIVX.inf');
    end
    else
    begin
      HPDriverPath := ExpandConstant('{tmp}\hp_drivers_x32');
      XeroxDriverPath := ExpandConstant('{tmp}\xerox_drivers_x32');
    end;

    // Build PowerShell installation script
    PSScript :=
      '$ErrorActionPreference = "Continue"' + #13#10 +
      '# Installer architecture mode (matches file copy behavior)' + #13#10;

    if IsX64 then
      PSScript := PSScript + '$Is64BitInstallMode = $true' + #13#10
    else
      PSScript := PSScript + '$Is64BitInstallMode = $false' + #13#10;

    PSScript := PSScript +
      'Write-Host ""' + #13#10 +
      'Write-Host "========================================"  -ForegroundColor Cyan' + #13#10 +
      'Write-Host "  WJDT Printer Installer" -ForegroundColor Cyan' + #13#10 +
      'Write-Host "========================================"  -ForegroundColor Cyan' + #13#10 +
      'Write-Host ""' + #13#10 +
      '' + #13#10 +
      '# Function to install printer driver' + #13#10 +
      'function Install-PrinterDriver {' + #13#10 +
      '    param(' + #13#10 +
      '        [string]$Vendor,' + #13#10 +
      '        [string]$DriverPath' + #13#10 +
      '    )' + #13#10 +
      '' + #13#10 +
      '    try {' + #13#10 +
      '        if ($Vendor -eq "HP") {' + #13#10 +
      '            $driverName = "HP Universal Printing PCL 6"' + #13#10 +
      '            # Use different INF file for x32 vs x64' + #13#10 +
      '            if ($DriverPath -like "*x64*") {' + #13#10 +
      '                $infFile = Join-Path $DriverPath "hpcu345u.inf"' + #13#10 +
      '            } else {' + #13#10 +
      '                $infFile = Join-Path $DriverPath "hpcu345c.inf"' + #13#10 +
      '            }' + #13#10 +
      '        } elseif ($Vendor -eq "Xerox") {' + #13#10 +
      '            $driverName = "Xerox Global Print Driver PCL6"' + #13#10 +
      '            # Check if DriverPath is already an INF file (x64) or directory (x32)' + #13#10 +
      '            if ($DriverPath -like "*.inf") {' + #13#10 +
      '                $infFile = $DriverPath' + #13#10 +
      '            } else {' + #13#10 +
      '                $infFile = Join-Path $DriverPath "x3UNIVX.inf"' + #13#10 +
      '            }' + #13#10 +
      '        } else {' + #13#10 +
      '            return $false' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Check if driver already installed' + #13#10 +
      '        $existingDriver = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue' + #13#10 +
      '        if ($existingDriver) {' + #13#10 +
      '            Write-Host "  $driverName already installed" -ForegroundColor Gray' + #13#10 +
      '            return $true' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        Write-Host "  Installing $driverName driver..." -ForegroundColor Yellow' + #13#10 +
      '' + #13#10 +
      '        # Locate pnputil.exe - check all possible locations' + #13#10 +
      '        $pnputil = $null' + #13#10 +
      '        $pnputilLocations = @(' + #13#10 +
      '            (Join-Path $env:SystemRoot "System32\pnputil.exe"),' + #13#10 +
      '            (Join-Path $env:SystemRoot "SysNative\pnputil.exe"),' + #13#10 +
      '            (Join-Path $env:SystemRoot "Syswow64\pnputil.exe"),' + #13#10 +
      '            "C:\Windows\System32\pnputil.exe",' + #13#10 +
      '            "C:\Windows\SysNative\pnputil.exe",' + #13#10 +
      '            "C:\Windows\Syswow64\pnputil.exe"' + #13#10 +
      '        )' + #13#10 +
      '' + #13#10 +
      '        foreach ($location in $pnputilLocations) {' + #13#10 +
      '            if (Test-Path $location) {' + #13#10 +
      '                $pnputil = $location' + #13#10 +
      '                Write-Host "  Found pnputil at: $pnputil" -ForegroundColor Gray' + #13#10 +
      '                break' + #13#10 +
      '            }' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Try PATH as last resort' + #13#10 +
      '        if (-not $pnputil) {' + #13#10 +
      '            $pnputil = (Get-Command pnputil.exe -ErrorAction SilentlyContinue).Path' + #13#10 +
      '            if ($pnputil) {' + #13#10 +
      '                Write-Host "  Found pnputil in PATH: $pnputil" -ForegroundColor Gray' + #13#10 +
      '            }' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # If still not found, try without full path' + #13#10 +
      '        if (-not $pnputil) {' + #13#10 +
      '            $pnputil = "pnputil.exe"' + #13#10 +
      '            Write-Host "  Using pnputil.exe from system PATH (no full path)" -ForegroundColor Gray' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Use pnputil to add driver package' + #13#10 +
      '        try {' + #13#10 +
      '            # Debug: Verify file exists' + #13#10 +
      '            Write-Host "  INF file path: $infFile" -ForegroundColor Gray' + #13#10 +
      '            if (Test-Path $infFile) {' + #13#10 +
      '                Write-Host "  INF file exists: YES" -ForegroundColor Green' + #13#10 +
      '            } else {' + #13#10 +
      '                Write-Host "  INF file exists: NO - FILE NOT FOUND!" -ForegroundColor Red' + #13#10 +
      '                Write-Host "  ERROR: Cannot proceed without INF file" -ForegroundColor Red' + #13#10 +
      '                return $false' + #13#10 +
      '            }' + #13#10 +
      '            Write-Host "  Running: & $pnputil /add-driver $infFile /install" -ForegroundColor Gray' + #13#10 +
      '            $pnpResult = & $pnputil /add-driver "$infFile" /install 2>&1' + #13#10 +
      '            $pnpExitCode = $LASTEXITCODE' + #13#10 +
      '            Write-Host "  pnputil exit code: $pnpExitCode" -ForegroundColor Gray' + #13#10 +
      '            if ($pnpResult) {' + #13#10 +
      '                Write-Host "  pnputil output: $pnpResult" -ForegroundColor Gray' + #13#10 +
      '            }' + #13#10 +
      '' + #13#10 +
      '            # Exit code 0 = success, 259 = driver already installed' + #13#10 +
      '            if ($pnpExitCode -ne 0 -and $pnpExitCode -ne 259) {' + #13#10 +
      '                Write-Host "  Warning: pnputil returned code $pnpExitCode" -ForegroundColor Yellow' + #13#10 +
      '            }' + #13#10 +
      '        } catch {' + #13#10 +
      '            Write-Host "  Warning: pnputil execution failed: $_" -ForegroundColor Yellow' + #13#10 +
      '            Write-Host "  Attempting to continue with Add-PrinterDriver..." -ForegroundColor Yellow' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        Start-Sleep -Seconds 5' + #13#10 +
      '' + #13#10 +
      '        # Try to add printer driver using PowerShell cmdlet' + #13#10 +
      '        try {' + #13#10 +
      '            Add-PrinterDriver -Name $driverName -ErrorAction Stop' + #13#10 +
      '            Write-Host "  $driverName installed successfully" -ForegroundColor Green' + #13#10 +
      '            return $true' + #13#10 +
      '        } catch {' + #13#10 +
      '            # If Add-PrinterDriver fails, try legacy rundll32 method' + #13#10 +
      '            Write-Host "  Add-PrinterDriver failed, trying legacy method..." -ForegroundColor Yellow' + #13#10 +
      '            try {' + #13#10 +
      '                $null = rundll32 printui.dll,PrintUIEntry /ia /m "$driverName" /f "$infFile"' + #13#10 +
      '                Start-Sleep -Seconds 3' + #13#10 +
      '                # Verify driver was installed' + #13#10 +
      '                $verifyDriver = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue' + #13#10 +
      '                if ($verifyDriver) {' + #13#10 +
      '                    Write-Host "  $driverName installed via legacy method" -ForegroundColor Green' + #13#10 +
      '                    return $true' + #13#10 +
      '                } else {' + #13#10 +
      '                    Write-Host "  Warning: Driver installation could not be verified" -ForegroundColor Yellow' + #13#10 +
      '                    return $true  # Continue anyway' + #13#10 +
      '                }' + #13#10 +
      '            } catch {' + #13#10 +
      '                Write-Host "  Warning: Legacy installation also failed" -ForegroundColor Yellow' + #13#10 +
      '                return $true  # Continue anyway, printer creation might still work' + #13#10 +
      '            }' + #13#10 +
      '        }' + #13#10 +
      '    } catch {' + #13#10 +
      '        Write-Host "  Error installing driver: $_" -ForegroundColor Red' + #13#10 +
      '        return $false' + #13#10 +
      '    }' + #13#10 +
      '}' + #13#10 +
      '' + #13#10 +
      '# Function to remove printer and its port' + #13#10 +
      'function Remove-NetworkPrinter {' + #13#10 +
      '    param(' + #13#10 +
      '        [string]$PrinterName,' + #13#10 +
      '        [string]$PortAddress' + #13#10 +
      '    )' + #13#10 +
      '' + #13#10 +
      '    try {' + #13#10 +
      '        Write-Host ""' + #13#10 +
      '        Write-Host "Removing: $PrinterName" -ForegroundColor Magenta' + #13#10 +
      '' + #13#10 +
      '        # Check if printer exists' + #13#10 +
      '        $existingPrinter = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue' + #13#10 +
      '        if (-not $existingPrinter) {' + #13#10 +
      '            Write-Host "  SKIPPED: Printer not found" -ForegroundColor Yellow' + #13#10 +
      '            return $true' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Remove printer' + #13#10 +
      '        Write-Host "  Removing printer..." -ForegroundColor Yellow' + #13#10 +
      '        Remove-Printer -Name $PrinterName -ErrorAction Stop' + #13#10 +
      '        Write-Host "  Printer removed" -ForegroundColor Green' + #13#10 +
      '' + #13#10 +
      '        # Remove port if exists and not used by other printers' + #13#10 +
      '        $portName = "IP_$PortAddress"' + #13#10 +
      '        $existingPort = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue' + #13#10 +
      '        if ($existingPort) {' + #13#10 +
      '            $printersUsingPort = Get-Printer | Where-Object { $_.PortName -eq $portName }' + #13#10 +
      '            if ($printersUsingPort.Count -eq 0) {' + #13#10 +
      '                Write-Host "  Removing unused port..." -ForegroundColor Yellow' + #13#10 +
      '                Remove-PrinterPort -Name $portName -ErrorAction Stop' + #13#10 +
      '                Write-Host "  Port removed: $portName" -ForegroundColor Green' + #13#10 +
      '            } else {' + #13#10 +
      '                Write-Host "  Port still in use by other printers" -ForegroundColor Gray' + #13#10 +
      '            }' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        Write-Host "  SUCCESS: $PrinterName removed" -ForegroundColor Green' + #13#10 +
      '        return $true' + #13#10 +
      '    } catch {' + #13#10 +
      '        Write-Host "  ERROR: $_" -ForegroundColor Red' + #13#10 +
      '        return $false' + #13#10 +
      '    }' + #13#10 +
      '}' + #13#10 +
      '' + #13#10 +
      '# Function to install individual printer' + #13#10 +
      'function Install-NetworkPrinter {' + #13#10 +
      '    param(' + #13#10 +
      '        [string]$PrinterName,' + #13#10 +
      '        [string]$PortAddress,' + #13#10 +
      '        [string]$Vendor,' + #13#10 +
      '        [string]$Model' + #13#10 +
      '    )' + #13#10 +
      '' + #13#10 +
      '    try {' + #13#10 +
      '        Write-Host ""' + #13#10 +
      '        Write-Host "Installing: $PrinterName" -ForegroundColor Cyan' + #13#10 +
      '        Write-Host "  Address: $PortAddress" -ForegroundColor Gray' + #13#10 +
      '        Write-Host "  Model: $Vendor $Model" -ForegroundColor Gray' + #13#10 +
      '' + #13#10 +
      '        # Determine driver name' + #13#10 +
      '        if ($Vendor -eq "HP") {' + #13#10 +
      '            $driverName = "HP Universal Printing PCL 6"' + #13#10 +
      '        } elseif ($Vendor -eq "Xerox") {' + #13#10 +
      '            $driverName = "Xerox Global Print Driver PCL6"' + #13#10 +
      '        } elseif ($Vendor -eq "HID") {' + #13#10 +
      '            $driverName = "DTC4500e Card Printer"' + #13#10 +
      '        } else {' + #13#10 +
      '            Write-Host "  Unknown vendor: $Vendor" -ForegroundColor Red' + #13#10 +
      '            return $false' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # For USB card printers, driver installation is enough' + #13#10 +
      '        # User plugs in USB device and Windows auto-configures it' + #13#10 +
      '        if ($PortAddress -eq "USB") {' + #13#10 +
      '            Write-Host "  USB card printer - driver installed" -ForegroundColor Green' + #13#10 +
      '            Write-Host "  Connect printer via USB and Windows will detect it" -ForegroundColor Gray' + #13#10 +
      '            Write-Host "  SUCCESS: $driverName ready for USB device" -ForegroundColor Green' + #13#10 +
      '            return $true' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # For network printers, check if printer already exists' + #13#10 +
      '        $existingPrinter = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue' + #13#10 +
      '        if ($existingPrinter) {' + #13#10 +
      '            Write-Host "  SKIPPED: Printer already exists" -ForegroundColor Yellow' + #13#10 +
      '            return $true' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Create port name for network printers' + #13#10 +
      '        $portName = "IP_$PortAddress"' + #13#10 +
      '' + #13#10 +
      '        # Check if port exists' + #13#10 +
      '        $existingPort = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue' + #13#10 +
      '        if (-not $existingPort) {' + #13#10 +
      '            Write-Host "  Creating printer port..." -ForegroundColor Yellow' + #13#10 +
      '            Add-PrinterPort -Name $portName -PrinterHostAddress $PortAddress -ErrorAction Stop' + #13#10 +
      '            Write-Host "  Port created: $portName" -ForegroundColor Green' + #13#10 +
      '        } else {' + #13#10 +
      '            Write-Host "  Using existing port: $portName" -ForegroundColor Gray' + #13#10 +
      '        }' + #13#10 +
      '' + #13#10 +
      '        # Add printer' + #13#10 +
      '        Write-Host "  Adding printer..." -ForegroundColor Yellow' + #13#10 +
      '        Add-Printer -Name $PrinterName -DriverName $driverName -PortName $portName -ErrorAction Stop' + #13#10 +
      '        Write-Host "  SUCCESS: $PrinterName installed" -ForegroundColor Green' + #13#10 +
      '        return $true' + #13#10 +
      '    } catch {' + #13#10 +
      '        Write-Host "  ERROR: $_" -ForegroundColor Red' + #13#10 +
      '        return $false' + #13#10 +
      '    }' + #13#10 +
      '}' + #13#10 +
      '' + #13#10 +
      'Write-Host "Step 1: Installing printer drivers..." -ForegroundColor Cyan' + #13#10 +
      'Write-Host ""' + #13#10 +
      '' + #13#10;

    // Install HP driver if needed
    PSScript := PSScript +
      'if (Install-PrinterDriver -Vendor "HP" -DriverPath "' + HPDriverPath + '") {' + #13#10 +
      '    Write-Host ""' + #13#10 +
      '} else {' + #13#10 +
      '    Write-Host "Failed to install HP driver. Continuing anyway..." -ForegroundColor Yellow' + #13#10 +
      '    Write-Host ""' + #13#10 +
      '}' + #13#10;

    // Install Xerox driver (both x64 and x32 now supported)
    PSScript := PSScript +
      'if (Install-PrinterDriver -Vendor "Xerox" -DriverPath "' + XeroxDriverPath + '") {' + #13#10 +
      '    Write-Host ""' + #13#10 +
      '} else {' + #13#10 +
      '    Write-Host "Failed to install Xerox driver. Continuing anyway..." -ForegroundColor Yellow' + #13#10 +
      '    Write-Host ""' + #13#10 +
      '}' + #13#10 +
      '' + #13#10;

    PSScript := PSScript +
      'Write-Host "Step 2: Processing printer changes..." -ForegroundColor Cyan' + #13#10 +
      '' + #13#10 +
      '$installedCount = 0' + #13#10 +
      '$removedCount = 0' + #13#10 +
      '$failCount = 0' + #13#10 +
      '' + #13#10;

    // Process each printer: install, remove, or skip
    for I := 0 to GetArrayLength(PrinterDataArray) - 1 do
    begin
      // If was installed but now unchecked - REMOVE
      if PrinterDataArray[I].IsInstalled and (not PrinterSelectionPage.Values[I]) then
      begin
        PSScript := PSScript +
          'if (Remove-NetworkPrinter -PrinterName "' + PrinterDataArray[I].PrinterName + '" ' +
          '-PortAddress "' + PrinterDataArray[I].FQDN + '") {' + #13#10 +
          '    $removedCount++' + #13#10 +
          '} else {' + #13#10 +
          '    $failCount++' + #13#10 +
          '}' + #13#10;
      end
      // If not installed and now checked - INSTALL
      else if (not PrinterDataArray[I].IsInstalled) and PrinterSelectionPage.Values[I] then
      begin
        PSScript := PSScript +
          'if (Install-NetworkPrinter -PrinterName "' + PrinterDataArray[I].PrinterName + '" ' +
          '-PortAddress "' + PrinterDataArray[I].FQDN + '" ' +
          '-Vendor "' + PrinterDataArray[I].Vendor + '" ' +
          '-Model "' + PrinterDataArray[I].Model + '") {' + #13#10 +
          '    $installedCount++' + #13#10 +
          '} else {' + #13#10 +
          '    $failCount++' + #13#10 +
          '}' + #13#10;
      end;
      // If state hasn't changed - SKIP (no code generated)
    end;

    // Add summary
    PSScript := PSScript +
      '' + #13#10 +
      'Write-Host ""' + #13#10 +
      'Write-Host "========================================"  -ForegroundColor Cyan' + #13#10 +
      'Write-Host "  Operation Complete" -ForegroundColor Cyan' + #13#10 +
      'Write-Host "========================================"  -ForegroundColor Cyan' + #13#10 +
      'Write-Host ""' + #13#10 +
      'if ($installedCount -gt 0) {' + #13#10 +
      '    Write-Host "  Printers installed: $installedCount" -ForegroundColor Green' + #13#10 +
      '}' + #13#10 +
      'if ($removedCount -gt 0) {' + #13#10 +
      '    Write-Host "  Printers removed: $removedCount" -ForegroundColor Magenta' + #13#10 +
      '}' + #13#10 +
      'if ($failCount -gt 0) {' + #13#10 +
      '    Write-Host "  Failed operations: $failCount" -ForegroundColor Red' + #13#10 +
      '}' + #13#10 +
      'if ($installedCount -eq 0 -and $removedCount -eq 0 -and $failCount -eq 0) {' + #13#10 +
      '    Write-Host "  No changes were made" -ForegroundColor Gray' + #13#10 +
      '}' + #13#10 +
      'Write-Host ""' + #13#10 +
      'Write-Host "Press any key to close..." -ForegroundColor Gray' + #13#10 +
      '$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")';

    // Save and execute script
    TempScriptPath := ExpandConstant('{tmp}\install_printers.ps1');
    SaveStringToFile(TempScriptPath, PSScript, False);

    Exec('powershell.exe',
         '-NoProfile -ExecutionPolicy Bypass -File "' + TempScriptPath + '"',
         '', SW_SHOW, ewWaitUntilTerminated, ResultCode);

    DeleteFile(TempScriptPath);
  end;
end;

// Skip directory selection
function ShouldSkipPage(PageID: Integer): Boolean;
begin
  Result := False;
  if PageID = wpSelectDir then
    Result := True;
end;
