////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Datacard Corporation.  All Rights Reserved.
//
// XPS Driver SDK c++ 'Print' sample application.
////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <prntvpt.h>
#include <vector>
#include <atlimage.h>
#include <atlpath.h>
#include "XGetopt.h"
#include "util.h"
#include "printTicketXml.h"
#include "DXP01SDK.H"
#include "CommandLineOptions.h"

using namespace std;

void usage()
{
    const string shortExeName = CT2A(util::GetShortExeName());
    cout << endl << shortExeName << " demonstrates print functionality of the printer and driver." << endl << endl;
    cout << shortExeName << " -n <printername> [-i <input hopper>] [-f <side>]" << endl << endl;
    cout << "options: " << endl;
    cout << "  -n <printername>. Required. Try -n \"XPS Card Printer\"." << endl;
    cout << "  -i <input hopper>. Defaults to input hopper #1" << endl;
    cout << "  -f <Front | Back>. Specify the front or back side the card will eject from." << endl;
    cout << "  -k Card will be ejected to the front output tray." << endl; 
    cout << "       If -k is not specified, the card will be dispensed to the rear of the printer" << endl;
    cout << "  -c Poll for job completion." << endl << endl;
    cout << "  -e Emboss and Indent." << endl << endl;

    cout << "  " << shortExeName << " -n \"XPS Card Printer\"" << endl;
    cout << "  Pick card from input hopper 1, print a one-sided landscape card and eject card to rear of the printer" << endl;

    cout << "  " << shortExeName << " -n \"XPS Card Printer\" -i r1 -c" << endl;
    cout << "  Pick card from rear input, print a one-sided landscape card, eject card to rear of the printer and poll for job status." << endl;

    cout << "  " << shortExeName << " -n \"XPS Card Printer\" -i 2 -k -e" << endl;
    cout << "  Pick card from from input hopper 2, print and emboss a one-sided landscape card , eject to the front output tray." << endl;
    ::exit(-1);
}


CommandLineOptions GetCommandlineOptions(const int argc, _TCHAR* argv[])
{
    CommandLineOptions commandLineOptions;

    // Initialize with the defaults
    commandLineOptions.hopperID = "1";
    commandLineOptions.cardEjectSide = "Default";
    commandLineOptions.outputHopperLocation = "";

    int c(0);
    while ((c = getopt(argc, argv, _T("n:ci:f:ke"))) != EOF) {

        switch (c) {
        case L'n': commandLineOptions.printerName = optarg; break;
        case L'c': commandLineOptions.jobCompletion = true; break;
        case L'e': commandLineOptions.emboss = true; break;
        case L'i': commandLineOptions.hopperID = CString(optarg).MakeLower(); break;
        case L'f': commandLineOptions.cardEjectSide = CString(optarg).MakeLower(); break;
        case L'k': commandLineOptions.outputHopperLocation = "FrontOutputTray"; break;
        default: usage();
        }
    }

    return commandLineOptions;
}

////////////////////////////////////////////////////////////////////////////////
// main()
////////////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
    CommandLineOptions commandLineOptions = GetCommandlineOptions(argc, argv);
    commandLineOptions.Validate();

    CComPtr <IBidiSpl> bidiSpl;
    int printerJobID(0);

    try {
        stringstream exceptionText;
        HRESULT hr = ::CoInitialize(NULL);
        if (FAILED(hr)) {
            exceptionText << "::CoInitialize:" << " 0x" << hex << hr << " " << CT2A(util::Win32ErrorString(hr));
            throw runtime_error(exceptionText.str());
        }

        // create an IBidiSpl COM smartpointer instance, and bind it to the
        // printer name. We use it for all subsequent call using the IBidi interface.
        hr = bidiSpl.CoCreateInstance(CLSID_BidiSpl);
        if (FAILED(hr)) {
            exceptionText << "bidiSpl.CoCreateInstance:" << " 0x" << hex << hr << " " << CT2A(util::Win32ErrorString(hr));
            throw runtime_error(exceptionText.str());
        }

        hr = bidiSpl->BindDevice(commandLineOptions.printerName, BIDI_ACCESS_USER);
        if (FAILED(hr)) {
            exceptionText << "BindDevice() error " << " 0x" << hex << hr << " '" << CT2A(commandLineOptions.printerName) << "' " << CT2A(util::Win32ErrorString(hr));
            throw runtime_error(exceptionText.str());
        }

        CString driverVersionXml = util::GetDriverVersionXML(bidiSpl);
        CString driverVersionString = util::ParseDriverVersionXML(driverVersionXml);
        cout << endl << "driver version: " << CW2A(driverVersionString) << endl << endl;

        CString printerOptionsXML = util::GetPrinterOptionsXML(bidiSpl);
        const util::PrinterOptionsValues printerOptionsValues = util::ParsePrinterOptionsXML(printerOptionsXML);
        if ("Ready" == printerOptionsValues._printerStatus ||
            "Busy" == printerOptionsValues._printerStatus) {
            // the printer can accept new jobs.
        }
        else {
            exceptionText << "printer " << CT2A(commandLineOptions.printerName) << " is not ready.";
            throw runtime_error(exceptionText.str());
        }

        if ("Installed" != printerOptionsValues._kioskOption) {
            exceptionText << "printer " << CT2A(commandLineOptions.printerName) << " does not have a kiosk installed.";
            throw runtime_error(exceptionText.str());
        }

        if ("Installed" != printerOptionsValues._printHead && "Version1" != printerOptionsValues._connectionProtocol) {
			exceptionText << "printer " << CT2A(commandLineOptions.printerName) << " does not have a print head installed.";
			throw std::runtime_error(exceptionText.str());
        }

        if (commandLineOptions.emboss && "Installed" != printerOptionsValues._moduleEmbosser) {
            exceptionText << "printer " << CT2A(commandLineOptions.printerName) << " does not have a emboss module installed.";
            throw runtime_error(exceptionText.str());
        }

        if (commandLineOptions.hopperID == "r1" && "Installed" != printerOptionsValues._rearPickOption) {
            exceptionText << "printer " << CT2A(commandLineOptions.printerName) << " does not have a rear pick option installed.";
            throw runtime_error(exceptionText.str());
        }

        printerJobID = util::StartJob(
            bidiSpl, 
            commandLineOptions.hopperID, 
            commandLineOptions.cardEjectSide, 
            commandLineOptions.outputHopperLocation);

        util::PrintText(commandLineOptions.printerName, false, 200, commandLineOptions.emboss);

        // notify the printer that all the data has been sent for the card:
        util::EndJob(bidiSpl);

        if (commandLineOptions.jobCompletion) {
            util::PollForJobCompletion(bidiSpl, printerJobID);
        }
    }
    catch (util::BidiException& e) {
        cerr << e.what() << endl;
        util::CancelJob(bidiSpl, e.printerJobID, e.errorCode);
    }
    catch (runtime_error& e) {
        cerr << e.what() << endl;
        if (0 != printerJobID) {
            util::CancelJob(bidiSpl, printerJobID, 0);
        }
    }

    if (bidiSpl) {
        bidiSpl->UnbindDevice();
        bidiSpl = NULL;
    }

    ::CoUninitialize();
    return 0;
}
