// rawprint.cs — PS4 POS raw printer helper
// Usage: rawprint.exe "<PrinterName>" "<FilePath>"
// Compiled: csc rawprint.cs -out:rawprint.exe
// Suppresses ALL Windows UI dialogs — safe for service/non-interactive use
using System;
using System.IO;
using System.Runtime.InteropServices;

class RawPrint {

    // ── Suppress Windows error/crash dialogs ──────────────────────────────────
    [DllImport("kernel32.dll")]
    static extern uint SetErrorMode(uint uMode);
    const uint SEM_FAILCRITICALERRORS  = 0x0001;
    const uint SEM_NOGPFAULTERRORBOX   = 0x0002;
    const uint SEM_NOOPENFILEERRORBOX  = 0x8000;

    // ── WinSpool API ──────────────────────────────────────────────────────────
    [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
    static extern bool OpenPrinter(string name, out IntPtr handle, IntPtr defaults);

    [DllImport("winspool.drv", SetLastError=true)]
    static extern bool ClosePrinter(IntPtr handle);

    [DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
    static extern int StartDocPrinter(IntPtr handle, int level, ref DocInfo1 di);

    [DllImport("winspool.drv", SetLastError=true)]
    static extern bool EndDocPrinter(IntPtr handle);

    [DllImport("winspool.drv", SetLastError=true)]
    static extern bool StartPagePrinter(IntPtr handle);

    [DllImport("winspool.drv", SetLastError=true)]
    static extern bool EndPagePrinter(IntPtr handle);

    [DllImport("winspool.drv", SetLastError=true)]
    static extern bool WritePrinter(IntPtr handle, byte[] buf, int count, out int written);

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
    struct DocInfo1 {
        public string pDocName;
        public string pOutputFile;
        public string pDataType;
    }

    static int Main(string[] args) {
        // Suppress ALL Windows dialogs — must be the very first call
        SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);

        if (args.Length < 2) {
            Console.Error.WriteLine("Usage: rawprint.exe <PrinterName> <FilePath>");
            return 1;
        }

        string printerName = args[0];
        string filePath    = args[1];

        // ── Read ESC/POS bytes ────────────────────────────────────────────────
        byte[] data;
        try {
            data = File.ReadAllBytes(filePath);
        } catch (Exception ex) {
            Console.Error.WriteLine("ERR_READ:" + ex.Message);
            return 2;
        }

        // ── Open printer handle ───────────────────────────────────────────────
        IntPtr hPrinter;
        if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero)) {
            int err = Marshal.GetLastWin32Error();
            Console.Error.WriteLine("ERR_OPEN:Win32Error=" + err + " Printer=" + printerName);
            return 3;
        }

        // ── Send raw bytes ────────────────────────────────────────────────────
        try {
            var di = new DocInfo1 {
                pDocName    = "PS4-POS",
                pDataType   = "RAW",
                pOutputFile = null
            };

            int jobId = StartDocPrinter(hPrinter, 1, ref di);
            if (jobId <= 0) {
                Console.Error.WriteLine("ERR_STARTDOC:Win32Error=" + Marshal.GetLastWin32Error());
                return 4;
            }

            if (!StartPagePrinter(hPrinter)) {
                EndDocPrinter(hPrinter);
                Console.Error.WriteLine("ERR_STARTPAGE:Win32Error=" + Marshal.GetLastWin32Error());
                return 5;
            }

            int written;
            bool ok = WritePrinter(hPrinter, data, data.Length, out written);

            EndPagePrinter(hPrinter);
            EndDocPrinter(hPrinter);

            if (!ok || written == 0) {
                Console.Error.WriteLine("ERR_WRITE:written=" + written);
                return 6;
            }

            Console.WriteLine("OK:" + written);
            return 0;

        } finally {
            ClosePrinter(hPrinter);
        }
    }
}