# JUSTSALE Background Print Service (Instant Mode)
# This script uses FileSystemWatcher for sub-second response times.

$watchDir = "c:\xampp\htdocs\justsale\receipts"
$archivedDir = "c:\xampp\htdocs\justsale\receipts\archived"
$printerName = "Xlife D82"
$configPath = "c:\xampp\htdocs\justsale\printer_config.txt"

if (!(Test-Path $archivedDir)) {
    New-Item -ItemType Directory -Path $archivedDir | Out-Null
}

# Add Raw Printing Support via Win32 API
$RawPrinterCode = @"
using System;
using System.Runtime.InteropServices;
using System.IO;

public class RawPrinterHelper {
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class DOCINFOA {
        [MarshalAs(UnmanagedType.LPStr)] public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)] public string pDataType;
    }
    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr hPrinter);
    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

    public static bool SendBytesToPrinter(string szPrinterName, byte[] pBytes, Int32 dwCount, string szDocName) {
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false;
        di.pDocName = szDocName;
        di.pDataType = "RAW";
        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) {
            if (StartDocPrinter(hPrinter, 1, di)) {
                if (StartPagePrinter(hPrinter)) {
                    IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(dwCount);
                    Marshal.Copy(pBytes, 0, pUnmanagedBytes, dwCount);
                    Int32 dwWritten = 0;
                    bSuccess = WritePrinter(hPrinter, pUnmanagedBytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                    Marshal.FreeCoTaskMem(pUnmanagedBytes);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        return bSuccess;
    }
}
"@

Add-Type -TypeDefinition $RawPrinterCode -ErrorAction SilentlyContinue

Write-Host "--------------------------------------------------"
Write-Host "     JUSTSALE INSTANT PRINT SERVICE IS ON"
Write-Host "--------------------------------------------------"
Write-Host "Watching: $watchDir"
Write-Host "Printer (Default): $printerName"
Write-Host "System status: Listening for sales..."

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $watchDir
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true

$action = {
    $filePath = $Event.SourceEventArgs.FullPath
    $fileName = $Event.SourceEventArgs.Name
    $extension = [System.IO.Path]::GetExtension($filePath)
    $cfgFile = "c:\xampp\htdocs\justsale\printer_config.txt"
    $archDir = "c:\xampp\htdocs\justsale\receipts\archived"

    if ($extension -match "pdf|txt") {
        # --- MULTI-TERMINAL SUPPORT ---
        # If running on a remote PC, check if this receipt belongs to this PC's IP.
        # Format: Receipt_#ID_DateTime_IP_192_168_1_10.txt
        $currentIP = (Test-Connection $env:COMPUTERNAME -Count 1).IPv4Address.IPAddressToString -replace '[\.:]', '_'
        $isLocal = ($watchDir -match "^[A-Z]:\\") # If watching a local drive, it's the main server
        
        if (-not $isLocal -and $fileName -match "_IP_") {
             if ($fileName -notmatch "_IP_$currentIP\.") {
                 # Not our receipt, skip but don't delete (the correct PC will handle it)
                 return 
             }
        }
        # ------------------------------

        try {
            # Reload printer name from config file
            $finalPrinter = "Xlife D82" # Default fallback
            if (Test-Path $cfgFile) {
                $finalPrinter = (Get-Content $cfgFile -Raw).Trim()
            }

            # Fast wait for file release (0.3s)
            Start-Sleep -Milliseconds 300
            
            Write-Host "Printing: $fileName to $finalPrinter..." -ForegroundColor Green
            
            if ($extension -eq ".txt") {
                $rawContent = [System.IO.File]::ReadAllBytes($filePath)
                $res = [RawPrinterHelper]::SendBytesToPrinter($finalPrinter, $rawContent, $rawContent.Length, "Sale Receipt")
                if (-not $res) { Write-Host "Raw Printing failed for $fileName" -ForegroundColor Red }
            } else {
                Start-Process -FilePath $filePath -Verb Print -WindowStyle Hidden -ErrorAction Stop
            }
            
            # Wait 2s and move to archive
            Start-Sleep -Seconds 2
            if (Test-Path $filePath) {
                Move-Item -Path $filePath -Destination $archDir -Force -ErrorAction SilentlyContinue
                Write-Host "Done: $fileName archived."
            }
        } catch {
            Write-Host "Error processing $($fileName): $($_.Exception.Message)"
        }
    }
}

Register-ObjectEvent $watcher "Created" -Action $action | Out-Null

while($true) { Start-Sleep -Seconds 1 }
