////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Datacard Corporation.  All Rights Reserved.
//
// XPS Printer SDK: csharp 'printer_control' sample
////////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using dxp01sdk;

public enum Commands {
    undefined,
    cancelAllJobs,
    restart,
    resetCardCounts,
    adjustColors,
    defaultColors,
    powerDownPrinter,
    cleanLaminator,
    cleanPrinter,
    setColorMode,
    wakePrinter,
    sleepPrinter
}

public class CommandLineOptions {

    public CommandLineOptions() {
        command = Commands.undefined;
    }

    public string printerName;
    public Commands command;
    public string colorMode;
}

internal class control {
    private static List<string> SigmaColormodeValues = new List<string> {
        "TrueColor (ICC)", "Vivid", "SD/CD", "Custom" };
    private static List<string> CR805ColormodeValues = new List<string> {
        "TrueColor (ICC)", "Vivid", "Legacy SR/CR", "Legacy SR wLUT", "Adobe RGB" };

    private static void Usage() {
        string thisExeName = Path.GetFileName(Environment.GetCommandLineArgs()[0]);
        Console.WriteLine();
        Console.WriteLine(thisExeName + " controls the printer through the printer driver.");
        Console.WriteLine();
        Console.WriteLine(thisExeName + " -n <printername> <command>");
        Console.WriteLine("  -n <printername>. Required. Try -n \"XPS Card Printer\".");
        Console.WriteLine("  -c cancel all jobs: Send a command to the printer to clear any print job");
        Console.WriteLine("                    from the printer. This does not delete spooled Windows");
        Console.WriteLine("                    print jobs.");
        Console.WriteLine("  -p power down the printer.");
        Console.WriteLine("  -q clean the printer.");
        Console.WriteLine("  -r restart the printer.");
        Console.WriteLine("  -w wake the printer.");
        Console.WriteLine("  -x put the printer to sleep.");
        Console.WriteLine("  -e reset all card counts.");
        Console.WriteLine("  -a set colorAdjust settings. This sample changes only the Red settings. Blue and Green are unchanged");
        Console.WriteLine("  -d set default colorAdjust settings. This sample only resets Red and Green.  Blue is unchanged.");
        Console.WriteLine("  -s set colorMode <colorMode>");
        Console.WriteLine();
        Environment.Exit(-1);
    }

    private static CommandLineOptions GetCommandlineOptions(string[] args) {
        CommandLineOptions commandLineOptions = new CommandLineOptions();
        CommandLine.Utility.Arguments arguments = new CommandLine.Utility.Arguments(args);

        if (string.IsNullOrEmpty(arguments["n"])) {
            Usage();
        }

        // check for -n with no printer name:
        bool boolVal = false;
        if (Boolean.TryParse(arguments["n"], out boolVal)) {
            Usage();
        }
        commandLineOptions.printerName = arguments["n"];

        if (!string.IsNullOrEmpty(arguments["c"])) {
            commandLineOptions.command = Commands.cancelAllJobs;
        }

        if (!string.IsNullOrEmpty(arguments["p"])) {
            commandLineOptions.command = Commands.powerDownPrinter;
        }

        if (!string.IsNullOrEmpty(arguments["q"])) {
            commandLineOptions.command = Commands.cleanPrinter;
        }

        if (!string.IsNullOrEmpty(arguments["r"])) {
            commandLineOptions.command = Commands.restart;
        }

        if (!string.IsNullOrEmpty(arguments["e"])) {
            commandLineOptions.command = Commands.resetCardCounts;
        }

        if (!string.IsNullOrEmpty(arguments["a"])) {
            commandLineOptions.command = Commands.adjustColors;
        }

        if (!string.IsNullOrEmpty(arguments["d"])) {
            commandLineOptions.command = Commands.defaultColors;
        }

        if (!string.IsNullOrEmpty(arguments["s"])) {
            commandLineOptions.command = Commands.setColorMode;
            commandLineOptions.colorMode = arguments["s"];
        }

        if (!string.IsNullOrEmpty(arguments["w"])) {
            commandLineOptions.command = Commands.wakePrinter;
        }

        if (!string.IsNullOrEmpty(arguments["x"])) {
            commandLineOptions.command = Commands.sleepPrinter;
        }

        if (Commands.undefined == commandLineOptions.command) {
            Usage();
        }

        return commandLineOptions;
    }




    public static void adjustColors(BidiSplWrap bidiSpl) {
        // create the XML and convert to bytes for the upcoming SetInputData() call.

        String formattedColorForRChannel = string.Format(strings.COLOR_CHANNEL,
            "1", "2", "-1", "0", "25", "6", "-19", "12", "13", "14", "15");

        String adjustColorXML = string.Format( strings.ADJUST_COLOR_XML, formattedColorForRChannel, "", "");

        string printerStatusXML = bidiSpl.SetPrinterData(strings.ADJUST_COLOR, adjustColorXML);
        PrinterStatusValues printerStatusValues = Util.ParsePrinterStatusXML(printerStatusXML);

        if (0 != printerStatusValues._errorCode) {
            Console.WriteLine("Request Not supported - error={0}", printerStatusValues._errorCode );
            throw new BidiException(printerStatusValues._errorString, printerStatusValues._printerJobID, printerStatusValues._errorCode);
        }
    } 


    public static void SetDefaultColors(BidiSplWrap bidiSpl) {

        // create the XML and convert to bytes for the upcoming SetInputData() call.
        // Setting red and green color channel values back to defaults and keeping blue unchanged
        String defaultColorXML = string.Format(strings.DEFAULT_COLOR_XML, "true", "true", "false");

        string printerStatusXML = bidiSpl.SetPrinterData(strings.SET_DEFAULT_COLOR, defaultColorXML);
        PrinterStatusValues printerStatusValues = Util.ParsePrinterStatusXML(printerStatusXML);

        if (0 != printerStatusValues._errorCode) {
            Console.WriteLine("Request Not supported - error={0}", printerStatusValues._errorCode);
            throw new BidiException(printerStatusValues._errorString, printerStatusValues._printerJobID, printerStatusValues._errorCode);
        }
    }


    ////////////////////////////////////////////////////////////////////////////////
    // isPrinterReady()
    //
    // Returns true if printer is in READY state,  otherwise returns false.
    //
    ////////////////////////////////////////////////////////////////////////////////
    public static bool isPrinterReady(BidiSplWrap bidiSpl) {

        string printerOptionsXML = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2);
        PrinterOptionsValues printerOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXML);

        Console.WriteLine("printer state: {0}", printerOptionsValues._printerStatus);

        // verify that the printer is in the READY state
        if ("Ready" != printerOptionsValues._printerStatus) {
            throw new Exception("ERROR:  printer is not in state 'READY'.");
        }

        return true;
    }


    ////////////////////////////////////////////////////////////////////////////////
    // isColorAdjustCapable()
    //
    // Returns true if printer is in READY state and firmware is >= D3.17.4
    // otherwise returns false.
    //
    ////////////////////////////////////////////////////////////////////////////////
    public static bool isColorAdjustCapable(BidiSplWrap bidiSpl) {

        string printerOptionsXML = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2);
        PrinterOptionsValues printerOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXML);

        Console.WriteLine("printer state: {0}", printerOptionsValues._printerStatus);

        // verify that the printer is in the READY state
        if ("Ready" != printerOptionsValues._printerStatus) {
            throw new Exception("ERROR:  printer is not in state 'READY'.");
        }

        string strPrinterVersion = printerOptionsValues._printerVersion;
        FirmwareVersion actualFirmware = Util.ParseFirmwareRev(strPrinterVersion);
        Console.WriteLine("Printer Version = {0}", strPrinterVersion);

        if ((actualFirmware._printerBase == "D3") && (actualFirmware._majorVersion > 16)) {
            if (actualFirmware._majorVersion > 17)
                return true;
            else if ((actualFirmware._majorVersion == 17) && (actualFirmware._minorVersion >= 4))
                return true;
        }
        else if (actualFirmware._printerBase == "D4") {
            return true;
        }

        throw new Exception("ERROR - Requires printer version: D3.17.4 (or greater)");
    }



    ////////////////////////////////////////////////////////////////////////////////
    // isLaminatorPresent()
    //
    // Returns true if printer is in READY state and laminator != "none"
    // otherwise returns false.
    //
    ////////////////////////////////////////////////////////////////////////////////
    public static bool isLaminatorPresent(BidiSplWrap bidiSpl) {

        string printerOptionsXML = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2);
        PrinterOptionsValues printerOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXML);

        Console.WriteLine("printer state: {0}", printerOptionsValues._printerStatus);

        // verify that the printer is in the READY state
        if ("Ready" != printerOptionsValues._printerStatus) {
            throw new Exception("ERROR:  printer is not in state 'READY'.");
        }

        // verify that the printer is in the READY state
        if ("None" == printerOptionsValues._laminator) {
            throw new Exception("ERROR:  printer does not have a laminator.");
        }

        return true;
    }


    ////////////////////////////////////////////////////////////////////////////
    // getPrinterMessage()
    ////////////////////////////////////////////////////////////////////////////
    public static int getPrinterMessage(BidiSplWrap bidiSpl, out string errorString) { 
        int errorCode = 0;
        errorString = "";   // need to assign a value to an "out" parameter

        // loop until error code is returned.
        // error code 172 = "insert cleaning card" is expected.
        // any other code is an error
        int count = 5;
        for (int i = 0; i < count; i++) {
            //string printerStatusXML = bidiSpl.GetPrinterData(strings.STATUS_ELEMENT);
            string printerStatusXML = bidiSpl.GetPrinterData(strings.PRINTER_MESSAGES);
            PrinterStatusValues printerStatusValues = Util.ParsePrinterStatusXML(printerStatusXML);

            errorCode = printerStatusValues._errorCode;
            errorString = printerStatusValues._errorString;

            if (errorCode != 0) {
                break;
            }

            if (i < count) {
                Thread.Sleep(1000);
            }
        }

        return errorCode;
    }

    ////////////////////////////////////////////////////////////////////////////
    // processCleaningCardErrorCode()
    ////////////////////////////////////////////////////////////////////////////
    public static void processCleaningCardErrorCode(BidiSplWrap bidiSpl, int errorCode, string errorString) {
        switch (errorCode) {
            case 0:
                // HMM - did not get error code
                Console.WriteLine(" - Did not get proper return code from driver");
                Util.CancelJob(bidiSpl, 0, 0);
                break;
            case 172:
                Console.WriteLine(" - Insert Cleaning card.");
                break;
            default:
                Console.WriteLine("CleanPrinter: {0}", errorString);
                Util.CancelJob(bidiSpl, 0, 0);
                break;
        }
    }



    ////////////////////////////////////////////////////////////////////////////
    // cleanPrinter()
    ////////////////////////////////////////////////////////////////////////////
    public static void cleanPrinter(BidiSplWrap bidiSpl) {
        try {
            if (isPrinterReady(bidiSpl)) {
                bidiSpl.SetPrinterData(strings.CLEAN_PRINTER_ACTION);
                Console.WriteLine("command issued to clean printer.");

                string errorString = "";
                int errorCode = getPrinterMessage(bidiSpl, out errorString);

                processCleaningCardErrorCode(bidiSpl, errorCode, errorString);
            }
        }
        catch (Exception e) {
            Console.WriteLine(e.Message);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // cleanLaminator()
    ////////////////////////////////////////////////////////////////////////////
    public static void cleanLaminator(BidiSplWrap bidiSpl) {
        try {
            if (isLaminatorPresent(bidiSpl)) {
                bidiSpl.SetPrinterData(strings.CLEAN_LAMINATOR_ACTION);
                Console.WriteLine("command issued to clean laminator.");

                string errorString = "";
                int errorCode = getPrinterMessage(bidiSpl, out errorString);

                processCleaningCardErrorCode(bidiSpl, errorCode, errorString);
            }
        }
        catch (Exception e) {
            Console.WriteLine(e.Message);
        }
    }


    ////////////////////////////////////////////////////////////////////////////////
    // isColorModeCapable()
    //
    // Returns true if printer is in READY state and appropriate colormode has
    // been provided
    ////////////////////////////////////////////////////////////////////////////////
    public static bool isColorModeCapable(BidiSplWrap bidiSpl, string colorMode)
    {
        string printerOptionsXML = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2);
        PrinterOptionsValues printerOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXML);

        Console.WriteLine("printer state: {0}", printerOptionsValues._printerStatus);

        // verify that the printer is in the READY state
        if ("Ready" != printerOptionsValues._printerStatus)
        {
            throw new Exception("ERROR:  printer is not in state 'READY'.");
        }

        string strPrinterVersion = printerOptionsValues._printerVersion;
        FirmwareVersion actualFirmware = Util.ParseFirmwareRev(strPrinterVersion);
        Console.WriteLine("Printer Version = {0}", strPrinterVersion);

        bool bFound = false;
        string exceptionText = "";
        if (actualFirmware._printerBase == "D4")
        {
            for (int i = 0; i < control.SigmaColormodeValues.Count; i++)
            {
                if (colorMode == SigmaColormodeValues[i])
                {
                    bFound = true;
                    break;
                }
            }
            if (!bFound)
            {
                exceptionText = "Error - '" + colorMode + "' is not a valid Sigma colormode value";
                throw new Exception(exceptionText);
            }
        }
        else if (actualFirmware._printerBase == "R4")
        {
            for (int i = 0; i < control.CR805ColormodeValues.Count; i++)
            {
                if (colorMode == CR805ColormodeValues[i])
                {
                    bFound = true;
                    break;
                }
            }
            if (!bFound)
            {
                exceptionText = "Error - '" + colorMode + "' is not a valid Sigma colormode value";
                throw new Exception(exceptionText);
            }
        }
        else
        {
            exceptionText = "Error - printer type '" + actualFirmware._printerBase + "' does not support colormode";
            throw new Exception(exceptionText);
        }

        return true;
    }


    ////////////////////////////////////////////////////////////////////////////////
    // setColorMode()
    //
    // using the IBidiSpl interface, issue a 'setColorMode' command.
    ////////////////////////////////////////////////////////////////////////////////
    public static void  setColorMode(BidiSplWrap bidiSpl, string colorMode) {

        try {
            // this method throws an exception if a problem was incurred
            // no need to check return code
            isColorModeCapable(bidiSpl, colorMode);

            // create the XML and convert to bytes for the upcoming SetInputData() call.
            string settingSubsystem = "Print";
            string settingName = "ColorMode";

            Console.WriteLine("command issued to set color mode.");
            Console.WriteLine("    settingSubsystem = {0}.", settingSubsystem);
            Console.WriteLine("    settingName = {0}.", settingName);
            Console.WriteLine("    value = {0}.", colorMode);

            string submitSettingsXML = string.Format(strings.SUBMIT_SETTINGS_XML, settingSubsystem, settingName, colorMode);

            string printerStatusXML = bidiSpl.SetPrinterData(strings.SUBMIT_SETTINGS, submitSettingsXML);

        }
        catch (Exception e) {
            Console.WriteLine(e.Message);
        }
    }


    ////////////////////////////////////////////////////////////////////////////
    // Main()
    ////////////////////////////////////////////////////////////////////////////
    private static void Main(string[] args) {

        try {
            CommandLineOptions commandLineOptions = GetCommandlineOptions(args);

            BidiSplWrap bidiSpl = new BidiSplWrap();
            bidiSpl.BindDevice(commandLineOptions.printerName);

            // There is a bug in SendRecv interface. Driver does not get the correct printer name
            // if user renames the printer. We mimic sending data to driver and that makes sure that
            // driver updates the printer name.
            //
            Util.RawDataToXpsPrinter(commandLineOptions.printerName);

            switch (commandLineOptions.command) {
                case Commands.cancelAllJobs:
                    // using zero for the printerJobID and the errorCode causes all
                    // printerJobs to be canceled:
                    Util.CancelJob(bidiSpl, 0, 0);
                    Console.WriteLine("all jobs canceled for '{0}'", commandLineOptions.printerName);
                    break;

                case Commands.restart:
                    bidiSpl.SetPrinterData(strings.RESTART_PRINTER);
                    Console.WriteLine("command issued to restart the printer.");
                    break;

                case Commands.powerDownPrinter:
                    bidiSpl.SetPrinterData(strings.POWER_DOWN_PRINTER);
                    Console.WriteLine("command issued to power down the printer.");
                    break;

                case Commands.cleanPrinter:
                    cleanPrinter(bidiSpl);
                    break;

                case Commands.cleanLaminator:
                    cleanLaminator(bidiSpl);
                    break;

                case Commands.resetCardCounts:
                    bidiSpl.SetPrinterData(strings.RESET_CARD_COUNTS);
                    Console.WriteLine("command issued to reset the printer's card counts.");
                    break;

                case Commands.adjustColors:
                    Console.WriteLine("AdjustColors - begin.");
                    if (isColorAdjustCapable(bidiSpl)) {
                        adjustColors(bidiSpl);
                    }
                    Console.WriteLine("AdjustColors - end.");
                    break;

                case Commands.defaultColors:
                    Console.WriteLine("SetDefaultColors - begin.");
                    if (isColorAdjustCapable(bidiSpl)) {
                        SetDefaultColors(bidiSpl);
                    }
                    Console.WriteLine("SetDefaultColors - end.");
                    break;
                    
                case Commands.setColorMode:
                    setColorMode(bidiSpl, commandLineOptions.colorMode);
                    break;

                case Commands.wakePrinter:
                    bidiSpl.SetPrinterData(strings.WAKE_PRINTER);
                    Console.WriteLine("command issued to wake the printer.");
                    break;

                case Commands.sleepPrinter:
                    bidiSpl.SetPrinterData(strings.SLEEP_PRINTER);
                    Console.WriteLine("command issued to put the printer to sleep.");
                    break;

                default:
                    Console.WriteLine("unexpected commandline option.");
                    break;
            }

            bidiSpl.UnbindDevice();
        }
        catch (Exception e) {
            Console.WriteLine(e.Message);
        }

        // press any key to continue
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }
}