private string sendRemoteMessage(RemoteMessage remoteMsg, int version = 1, byte[] attachmentData = null, string attachmentUrl = null, string attachmentEncoding = null)
        {
            remoteMsg.packageName         = this.packageName;
            remoteMsg.remoteApplicationID = remoteApplicationID;
            remoteMsg.remoteSourceSDK     = remoteSourceSDK;
            remoteMsg.version             = version;

            if (remoteMsg.version > 1)
            {
                bool hasAttachmentURI  = attachmentUrl != null;
                bool hasAttachmentData = attachmentData != null;
                int  payloadHelper     = 0;
                if (remoteMsg.attachment != null)
                {
                    payloadHelper = (remoteMsg.attachment.Length != 0 ? remoteMsg.attachment.Length : 0);
                }

                if (remoteMsg.payload != null)
                {
                    payloadHelper += (remoteMsg.payload.Length != 0 ? remoteMsg.payload.Length : 0);
                }

                // maxMessageSizeInChars is controlled by user, make sure it is a positive number or use a reasonable default to avoid infinite loop etc. problems
                int  maxSize         = maxMessageSizeInChars <= 0 ? 1000 : maxMessageSizeInChars;
                bool payloadTooLarge = payloadHelper > maxSize;
                bool shouldFrag      = hasAttachmentURI || payloadTooLarge || hasAttachmentData;
                if (shouldFrag)
                {
                    if ((remoteMsg.attachment != null && remoteMsg.attachment.Length > MAX_PAYLOAD_SIZE))
                    {
                        // Console.WriteLine("Error sending message - payload size is greater than the maximum allowed");
                        return(null);
                    }

                    int    fragmentIndex = 0;
                    string payloadStr    = remoteMsg.payload ?? "";

                    int startIndex = 0;
                    while (startIndex < payloadStr.Length)
                    {
                        int    length   = (maxSize < payloadStr.Length ? maxSize : payloadStr.Length);
                        string fPayload = payloadStr.Substring(startIndex, length);
                        startIndex += length;
                        bool noAttachmentAvailable    = string.IsNullOrEmpty(remoteMsg.attachment);
                        bool noAttachmentUriAvailable = string.IsNullOrEmpty(remoteMsg.attachmentUri);
                        bool lastFragment             = payloadStr.Length == 0 && (noAttachmentAvailable && noAttachmentUriAvailable);
                        sendMessageFragment(remoteMsg, fPayload, null, fragmentIndex++, lastFragment);
                    }

                    // now let's fragment the attachment or attachmentData
                    string attach = remoteMsg.attachment;
                    if (attach != null)
                    {
                        if (remoteMsg.attachmentEncoding == "BASE64")
                        {
                            remoteMsg.attachmentEncoding = "BASE64.ATTACHMENT";
                            int start = 0;
                            while (attach.Length > 0)
                            {
                                string aPayload = attach.Substring(start, maxSize < attach.Length ? maxSize : attach.Length);
                                start += maxSize < attach.Length ? maxSize : attach.Length;
                                sendMessageFragment(remoteMsg, null, aPayload, fragmentIndex++, attach.Length == 0);
                            }
                        }
                        else
                        {
                            // TODO: chunk as-is
                        }
                    }
                    else if (attachmentData != null)
                    {
                        int start = 0;
                        int count = attachmentData.Length;
                        remoteMsg.attachmentEncoding = "BASE64.FRAGMENT";
                        while (start < count)
                        {
                            int    length    = Math.Min(maxSize, count - start);
                            byte[] chunkData = new byte[length];
                            Array.Copy(attachmentData, start, chunkData, 0, length);
                            start = start + maxSize;

                            //FRAGMENT Payload
                            string fAttachment = Convert.ToBase64String(chunkData);
                            sendMessageFragment(remoteMsg: remoteMsg, fPayload: null, fAttachment: fAttachment, fragmentIndex: fragmentIndex++, lastFragment: start > count);
                        }
                    }
                }
                else //we DON'T need to fragment
                {
                    // note: attachmentData is always null here because we take earlier if branch "if shouldFrag" when it's not
                    if (attachmentData != null)
                    {
                        string base64string = Convert.ToBase64String(attachmentData);
                        doPrintImage(base64string);
                    }
                }
            }

            return(remoteMsg.id);
        }
 public static RemoteMessage createMessage(Methods meth, MessageTypes msgType, Message payload, string packageName, string remoteSourceSDK, string remoteApplicationID)
 {
     RemoteMessage msg = new RemoteMessage();
     msg.method = meth;
     msg.type = msgType;
     if (null == payload)
     {
         payload = new Message(meth);
     }
     msg.payload = JsonUtils.serialize(payload);
     msg.packageName = packageName;
     msg.remoteSourceSDK = remoteSourceSDK;
     msg.remoteApplicationID = remoteApplicationID;
     return msg;
 }
        /// <summary>
        /// Parse the generic message and figure out which handler should be used for processing
        /// </summary>
        /// <param name="message">The message.</param>
        public void onMessage(string message)
        {
#if DEBUG
            Console.WriteLine("Received raw message: " + message);
#endif
            RemoteMessage rMessage = null;
            try
            {
                // Note: This handling was changed after the 1.4.2 release to properly suppress uknown messages.
                //       Old versions of the WinSDK will crash with unknown messages, and old versions are still expected to be in customer's hands.
                //       When testing releases, make sure any suppressed errors here are paid proper backwards-compatible attention and tested

                // Deserialize the message object to a real object
                rMessage             = JsonUtils.DeserializeSdk <RemoteMessage>(message);
                remoteMessageVersion = Math.Max(remoteMessageVersion, rMessage.version);
            }
            catch (Newtonsoft.Json.JsonSerializationException)
            {
                // if a remote message can't be parsed, ignore this unknown message; log as appropriate
                // - and verify backwards compatiblility story since old WinSDK releases will crash

                // TODO: Log message and exception in new logging
            }

            try
            {
                // Handle and route known messages appropriately
                if (rMessage != null)
                {
                    switch (rMessage.method)
                    {
                    case Methods.BREAK:
                        break;

                    case Methods.ACK:
                        AcknowledgementMessage ackMessage = JsonUtils.DeserializeSdk <AcknowledgementMessage>(rMessage.payload);
                        notifyObserverAck(ackMessage);
                        break;

                    case Methods.CASHBACK_SELECTED:
                        CashbackSelectedMessage cbsMessage = JsonUtils.DeserializeSdk <CashbackSelectedMessage>(rMessage.payload);
                        notifyObserversCashbackSelected(cbsMessage);
                        break;

                    case Methods.DISCOVERY_RESPONSE:
                        DiscoveryResponseMessage drMessage = JsonUtils.DeserializeSdk <DiscoveryResponseMessage>(rMessage.payload);
                        deviceInfo.name   = drMessage.name;
                        deviceInfo.serial = drMessage.serial;
                        deviceInfo.model  = drMessage.model;
                        notifyObserversDiscoveryResponse(drMessage);
                        break;

                    case Methods.FINISH_CANCEL:
                        FinishCancelMessage finishCancelMessage = JsonUtils.DeserializeSdk <FinishCancelMessage>(rMessage.payload);
                        notifyObserversFinishCancel(finishCancelMessage.requestInfo);
                        break;

                    case Methods.FINISH_OK:
                        FinishOkMessage fokmsg = JsonUtils.DeserializeSdk <FinishOkMessage>(rMessage.payload);
                        notifyObserversFinishOk(fokmsg);
                        break;

                    case Methods.KEY_PRESS:
                        KeyPressMessage kpm = JsonUtils.DeserializeSdk <KeyPressMessage>(rMessage.payload);
                        notifyObserversKeyPressed(kpm);
                        break;

                    case Methods.PARTIAL_AUTH:
                        PartialAuthMessage partialAuth = JsonUtils.DeserializeSdk <PartialAuthMessage>(rMessage.payload);
                        notifyObserversPartialAuth(partialAuth);
                        break;

                    case Methods.CONFIRM_PAYMENT_MESSAGE:
                        setPaymentConfirmationIdle(false);
                        ConfirmPaymentMessage confirmPaymentMessage = JsonUtils.DeserializeSdk <ConfirmPaymentMessage>(rMessage.payload);
                        notifyObserversConfirmPayment(confirmPaymentMessage);
                        break;

                    case Methods.TIP_ADDED:
                        TipAddedMessage tipMessage = JsonUtils.DeserializeSdk <TipAddedMessage>(rMessage.payload);
                        notifyObserversTipAdded(tipMessage);
                        break;

                    case Methods.TX_START_RESPONSE:
                        TxStartResponseMessage txsrm = JsonUtils.DeserializeSdk <TxStartResponseMessage>(rMessage.payload);
                        notifyObserversTxStartResponse(txsrm);
                        break;

                    case Methods.TX_STATE:
                        TxStateMessage txStateMsg = JsonUtils.DeserializeSdk <TxStateMessage>(rMessage.payload);
                        notifyObserversTxState(txStateMsg);
                        break;

                    case Methods.UI_STATE:
                        UiStateMessage uiStateMsg = JsonUtils.DeserializeSdk <UiStateMessage>(rMessage.payload);
                        notifyObserversUiState(uiStateMsg);
                        break;

                    case Methods.VERIFY_SIGNATURE:
                        paymentRejected = false;
                        VerifySignatureMessage vsigMsg = JsonUtils.DeserializeSdk <VerifySignatureMessage>(rMessage.payload);
                        notifyObserversVerifySignature(vsigMsg);
                        break;

                    case Methods.REFUND_RESPONSE:
                        RefundResponseMessage refRespMsg = JsonUtils.DeserializeSdk <RefundResponseMessage>(rMessage.payload);
                        notifyObserversRefundPaymentResponse(refRespMsg);
                        break;

                    case Methods.TIP_ADJUST_RESPONSE:
                        TipAdjustResponseMessage tipAdjustMsg = JsonUtils.DeserializeSdk <TipAdjustResponseMessage>(rMessage.payload);
                        notifyObserversTipAdjusted(tipAdjustMsg);
                        break;

                    case Methods.VAULT_CARD_RESPONSE:
                        VaultCardResponseMessage vcrMsg = JsonUtils.DeserializeSdk <VaultCardResponseMessage>(rMessage.payload);
                        notifyObserversVaultCardResponse(vcrMsg);
                        break;

                    case Methods.CARD_DATA_RESPONSE:
                        ReadCardDataResponseMessage rcdrMsg = JsonUtils.DeserializeSdk <ReadCardDataResponseMessage>(rMessage.payload);
                        notifyObserversReadCardDataResponse(rcdrMsg);
                        break;

                    case Methods.CAPTURE_PREAUTH_RESPONSE:
                        CapturePreAuthResponseMessage carMsg = JsonUtils.DeserializeSdk <CapturePreAuthResponseMessage>(rMessage.payload);
                        notifyObserversCapturePreAuthResponse(carMsg);
                        break;

                    case Methods.CLOSEOUT_RESPONSE:
                        CloseoutResponseMessage crMsg = JsonUtils.DeserializeSdk <CloseoutResponseMessage>(rMessage.payload);
                        notifyObserversCloseoutResponse(crMsg);
                        break;

                    case Methods.RETRIEVE_PENDING_PAYMENTS_RESPONSE:
                        RetrievePendingPaymentsResponseMessage rpprMsg = JsonUtils.DeserializeSdk <RetrievePendingPaymentsResponseMessage>(rMessage.payload);
                        notifyObserversPendingPaymentsResponse(rpprMsg);
                        break;

                    case Methods.ACTIVITY_RESPONSE:
                        ActivityResponseMessage arm = JsonUtils.DeserializeSdk <ActivityResponseMessage>(rMessage.payload);
                        notifyObserversActivityResponse(arm);
                        break;

                    case Methods.ACTIVITY_MESSAGE_FROM_ACTIVITY:
                        ActivityMessageFromActivity amfa = JsonUtils.DeserializeSdk <ActivityMessageFromActivity>(rMessage.payload);
                        notifyObserversActivityMessage(amfa);
                        break;

                    case Methods.RESET_DEVICE_RESPONSE:
                        ResetDeviceResponseMessage rdrm = JsonUtils.DeserializeSdk <ResetDeviceResponseMessage>(rMessage.payload);
                        notifyObserversDeviceReset(rdrm);
                        break;

                    case Methods.RETRIEVE_DEVICE_STATUS_RESPONSE:
                        RetrieveDeviceStatusResponseMessage rdsrm = JsonUtils.DeserializeSdk <RetrieveDeviceStatusResponseMessage>(rMessage.payload);
                        notifyObserversRetrieveDeviceStatusResponse(rdsrm);
                        break;

                    case Methods.PRINT_CREDIT:
                        CreditPrintMessage cpm = JsonUtils.DeserializeSdk <CreditPrintMessage>(rMessage.payload);
                        notifyObserversPrintCredit(cpm);
                        break;

                    case Methods.PRINT_CREDIT_DECLINE:
                        DeclineCreditPrintMessage dcpm = JsonUtils.DeserializeSdk <DeclineCreditPrintMessage>(rMessage.payload);
                        notifyObserversPrintCreditDecline(dcpm);
                        break;

                    case Methods.PRINT_PAYMENT:
                        PaymentPrintMessage ppm = JsonUtils.DeserializeSdk <PaymentPrintMessage>(rMessage.payload);
                        notifyObserversPrintPayment(ppm);
                        break;

                    case Methods.PRINT_PAYMENT_DECLINE:
                        DeclinePaymentPrintMessage dppm = JsonUtils.DeserializeSdk <DeclinePaymentPrintMessage>(rMessage.payload);
                        notifyObserversPrintPaymentDecline(dppm);
                        break;

                    case Methods.PRINT_PAYMENT_MERCHANT_COPY:
                        PaymentPrintMerchantCopyMessage ppmcm = JsonUtils.DeserializeSdk <PaymentPrintMerchantCopyMessage>(rMessage.payload);
                        notifyObserversPrintMerchantCopy(ppmcm);
                        break;

                    case Methods.REFUND_PRINT_PAYMENT:
                        RefundPaymentPrintMessage rppm = JsonUtils.DeserializeSdk <RefundPaymentPrintMessage>(rMessage.payload);
                        notifyObserversPrintRefund(rppm);
                        break;

                    case Methods.RETRIEVE_PAYMENT_RESPONSE:
                        RetrievePaymentResponseMessage rprm = JsonUtils.DeserializeSdk <RetrievePaymentResponseMessage>(rMessage.payload);
                        notifyObserversRetrievePaymentResponse(rprm);
                        break;

                    case Methods.GET_PRINTERS_RESPONSE:
                        RetrievePrintersResponseMessage rtrm = JsonUtils.DeserializeSdk <RetrievePrintersResponseMessage>(rMessage.payload);
                        notifyObserversRetrievePrinterResponse(rtrm);
                        break;

                    case Methods.PRINT_JOB_STATUS_RESPONSE:
                        PrintJobStatusResponseMessage pjsrm = JsonUtils.DeserializeSdk <PrintJobStatusResponseMessage>(rMessage.payload);
                        notifyObserversRetrievePrintJobStatus(pjsrm);
                        break;

                    case Methods.SHOW_RECEIPT_OPTIONS_RESPONSE:
                        ShowReceiptOptionsResponseMessage srorm = JsonUtils.DeserializeSdk <ShowReceiptOptionsResponseMessage>(rMessage.payload);
                        notifyObserverDisplayReceiptOptionsResponse(srorm);
                        break;

                    case Methods.CUSTOMER_PROVIDED_DATA_MESSAGE:
                        notifyObserversCustomerProvidedData(JsonUtils.DeserializeSdk <CustomerProvidedDataResponseMessage>(rMessage.payload));
                        break;

                    case Methods.VOID_PAYMENT_RESPONSE:
                        VoidPaymentResponseMessage vprm = JsonUtils.DeserializeSdk <VoidPaymentResponseMessage>(rMessage.payload);
                        notifyObserversPaymentVoided(vprm);
                        break;

                    case Methods.REFUND_REQUEST:
                    case Methods.PAYMENT_VOIDED:
                    case Methods.ORDER_ACTION_RESPONSE:
                    case Methods.DISCOVERY_REQUEST:
                    case Methods.ORDER_ACTION_ADD_DISCOUNT:
                    case Methods.ORDER_ACTION_ADD_LINE_ITEM:
                    case Methods.ORDER_ACTION_REMOVE_LINE_ITEM:
                    case Methods.ORDER_ACTION_REMOVE_DISCOUNT:
                    case Methods.PRINT_IMAGE:
                    case Methods.PRINT_TEXT:
                    case Methods.SHOW_ORDER_SCREEN:
                    case Methods.SHOW_PAYMENT_RECEIPT_OPTIONS:
                    case Methods.SHOW_REFUND_RECEIPT_OPTIONS:
                    case Methods.SHOW_CREDIT_RECEIPT_OPTIONS:
                    case Methods.SHOW_THANK_YOU_SCREEN:
                    case Methods.SHOW_WELCOME_SCREEN:
                    case Methods.SIGNATURE_VERIFIED:
                    case Methods.TERMINAL_MESSAGE:
                    case Methods.TX_START:
                    case Methods.VOID_PAYMENT:
                    case Methods.CLOSEOUT_REQUEST:
                    case Methods.VAULT_CARD:
                    case Methods.CARD_DATA:
                    case Methods.CLOVER_DEVICE_LOG_REQUEST:
                        //Outbound no-op
                        break;

                    default:
                        // Messsage Method not recognized or null: usually rMessage.type == MessageTypes.PING instead of normal Command message
                        if (rMessage.type == MessageTypes.PING)
                        {
                            onPing();
                        }
                        break;
                    }
                }
            }
            catch (Newtonsoft.Json.JsonSerializationException exception)
            {
                // if a remote message's details can't be parsed, ignore this misformed/unrecognized message; log as appropriate
                // - and verify backwards compatiblility story since old WinSDK releases will crash

                // TODO: Log message and exception in new logging

                // TODO: Send a warning to the point of sale that a message arrived but failed to parse. Perhaps leverage DeviceError or create a DeviceWarning
            }
        }