package demo.mapi.ccv.eu.mapi_demo.activities.unattended;


import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import demo.mapi.ccv.eu.mapi_demo.AndroidLogger;
import demo.mapi.ccv.eu.mapi_demo.MapiDemoState;
import demo.mapi.ccv.eu.mapi_demo.R;
import demo.mapi.ccv.eu.mapi_demo.formatters.OutputFormatter;
import eu.ccvlab.mapi.api.PaymentService;
import eu.ccvlab.mapi.api.TerminalService;
import eu.ccvlab.mapi.api.TokenService;
import eu.ccvlab.mapi.core.DeliveryBoxCallback;
import eu.ccvlab.mapi.core.api.PaymentApi;
import eu.ccvlab.mapi.core.api.TerminalApi;
import eu.ccvlab.mapi.core.api.TokenApi;
import eu.ccvlab.mapi.core.api.request.CardDetectionRequest;
import eu.ccvlab.mapi.core.api.request.FinancialAdviceCancellationReasonCode;
import eu.ccvlab.mapi.core.api.request.FinancialAdviceRequest;
import eu.ccvlab.mapi.core.api.request.OnlineAgentRequest;
import eu.ccvlab.mapi.core.api.request.PreAuthorisationRequest;
import eu.ccvlab.mapi.core.api.request.TokenPurpose;
import eu.ccvlab.mapi.core.api.request.TunnelRequest;
import eu.ccvlab.mapi.core.api.response.CardSlotNo;
import eu.ccvlab.mapi.core.api.response.delegate.PaymentDelegate;
import eu.ccvlab.mapi.core.api.response.delegate.TerminalDelegate;
import eu.ccvlab.mapi.core.api.response.delegate.TokenDelegate;
import eu.ccvlab.mapi.core.api.response.result.Error;
import eu.ccvlab.mapi.core.api.response.result.PrinterStatusType;
import eu.ccvlab.mapi.core.api.response.result.TokenResult;
import eu.ccvlab.mapi.core.logging.MPALogger;
import eu.ccvlab.mapi.core.logging.MPALogging;
import eu.ccvlab.mapi.core.payment.DisplayTextRequest;
import eu.ccvlab.mapi.core.payment.EReceiptRequest;
import eu.ccvlab.mapi.core.payment.GermanEichrecht;
import eu.ccvlab.mapi.core.payment.MainTextRequest;
import eu.ccvlab.mapi.core.payment.Money;
import eu.ccvlab.mapi.core.payment.Payment;
import eu.ccvlab.mapi.core.payment.PaymentAdministrationResult;
import eu.ccvlab.mapi.core.payment.PaymentReceipt;
import eu.ccvlab.mapi.core.payment.PaymentResult;
import eu.ccvlab.mapi.core.payment.eReceipt.EReceiptAdditionalTextRequest;
import eu.ccvlab.mapi.core.payment.eReceipt.EReceiptTextPlacement;
import eu.ccvlab.mapi.core.payment.preAuthorisation.Environment;
import eu.ccvlab.mapi.core.payment.preAuthorisation.PreAuthReferenceRequest;
import eu.ccvlab.mapi.core.requests.ResultState;
import eu.ccvlab.mapi.core.terminal.ExternalTerminal;
import eu.ccvlab.mapi.core.terminal.TerminalDisplayText;
import eu.ccvlab.mapi.core.terminal.TerminalDisplayTextWindow;
import eu.ccvlab.mapi.core.util.Id;
import eu.ccvlab.pi_api.domain.DisplayMessage;
import eu.ccvlab.pi_api.domain.EReceipt;
import eu.ccvlab.pi_api.domain.PaymentRequest;
import eu.ccvlab.pi_api.domain.PaymentResponse;
import eu.ccvlab.pi_api.domain.Terminal;
import eu.ccvlab.pi_api.domain.TerminalRequest;
import eu.ccvlab.pi_api.domain.TerminalResponse;
import eu.ccvlab.pi_api.domain.TokenRequest;
import eu.ccvlab.pi_api.domain.TokenResponse;
import eu.ccvlab.pi_api.domain.TokenType;
import eu.ccvlab.pi_api.domain.UserAction;
import eu.ccvlab.pi_api.RestClientProvider;
import eu.ccvlab.pi_api.retrofit.PaymentRestService;
import eu.ccvlab.pi_api.retrofit.TerminalRestService;
import eu.ccvlab.pi_api.retrofit.TokenRestService;
import eu.ccvlab.pi_api.service.PaymentServiceImp;
import eu.ccvlab.pi_api.service.TerminalServiceImp;
import eu.ccvlab.pi_api.service.TokenServiceImp;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.stream.Stream;

public class UnAttendedTerminalActivity extends AppCompatActivity {
    private TokenApi tokenService;
    private TokenDelegate tokenDelegate;
    private MapiDemoState appState = new MapiDemoState();
    private PaymentAdministrationResult paymentAdministrationResult;
    private TokenResult tokenResult;
    private OutputFormatter resultFieldFormatter;
    private String terminalIp;
    private BigDecimal amount;
    private BigDecimal minimumAmount;
    private BigDecimal totalAmount;
    private PaymentApi paymentService;
    private TerminalApi terminalService;
    private boolean restApi = false;
    private Handler handler;
    private Runnable runnable;
    private String previousPaymentId;
    private Integer cardDetectionReferenceNumber;
    private FinancialAdviceCancellationReasonCode financialAdviceCancellationReasonCode;
    private boolean cardDetectionFollowupActionInitiated = false;
    eu.ccvlab.pi_api.service.PaymentService paymentRestService;
    eu.ccvlab.pi_api.service.TokenService tokenRestService;
    eu.ccvlab.pi_api.service.TerminalService terminalRestService;

    Button paymentDeliveryboxSuccess;
    Button paymentDeliveryboxFailed;
    public TextView resultTextField;
    public ExternalTerminal sdkTerminal;
    public Terminal restTerminal;
    private AndroidLogger androidLogger = new AndroidLogger("UnAttendedTerminalActivity");

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.unattended_terminal);
        paymentDeliveryboxSuccess = findViewById(R.id.payment_deliverybox_success);
        paymentDeliveryboxFailed = findViewById(R.id.payment_deliverybox_failed);
        resultTextField = findViewById(R.id.unattended_output);
        findViewById(R.id.retrieve_last_ticket).setOnClickListener((View view) -> onClickRetrieveLastTicket());
        findViewById(R.id.retrieve_open_pre_authorisations).setOnClickListener((View view) -> onClickRetrieveOpenPreAuthorisations());
        findViewById(R.id.card_detection).setOnClickListener((View view) -> onClickCardDetection());
        findViewById(R.id.payment_recovery).setOnClickListener((View view) -> onClickPaymentRecovery());
        findViewById(R.id.card_validation).setOnClickListener((View view) -> onClickCardValidation());
        findViewById(R.id.silent_abort).setOnClickListener((View view) -> onClickSilentAbort());
        findViewById(R.id.silent_abort_on_new_connection).setOnClickListener((View view) -> onClickSilentAbortOnNewConnection());
        findViewById(R.id.normal_abort).setOnClickListener((View view) -> onClickNormalAbort());
        findViewById(R.id.pre_authorisation).setOnClickListener((View view) -> onClickPreAuthorisation());
        findViewById(R.id.pre_authorisation_mifare_cards).setOnClickListener((View view) -> onClickPreAuthorisationMifareCards());
        findViewById(R.id.cancel_pre_authorisation).setOnClickListener((View view) -> onClickCancelPreAuthorisation());
        findViewById(R.id.financial_advice).setOnClickListener((View view) -> onClickFinancialAdvice());
        findViewById(R.id.financial_advice_german_eichrecht).setOnClickListener((View view) -> onClickFinancialAdviceGermanEichrecht());
        findViewById(R.id.payment_deliverybox_success).setOnClickListener((View view) -> onClickPaymentDeliveryBoxSuccess());
        findViewById(R.id.payment_deliverybox_success_with_e_receipt).setOnClickListener((View view) -> onClickPaymentDeliveryBoxSuccessWithEReceipt());
        findViewById(R.id.payment_deliverybox_success_german_eichrecht).setOnClickListener((View view) -> onClickPaymentDeliveryBoxSuccessWithGermanEichrecht());
        findViewById(R.id.payment_deliverybox_failed).setOnClickListener((View view) -> onClickPaymentDeliveryBoxFailed());
        findViewById(R.id.read_card_uid_flexo).setOnClickListener((View view) -> onClickReadCardUIDUsingFlexo());
        findViewById(R.id.read_card_uid_card_tunnel).setOnClickListener((View view) -> onClickReadCardUIDUsingCardTunnel());
        findViewById(R.id.get_transaction_overview).setOnClickListener((View view) -> onClickGetTransactionOverview());
        findViewById(R.id.perform_period_closing).setOnClickListener((View view) -> onClickPerformPeriodClosing());
        findViewById(R.id.perform_partial_period_closing).setOnClickListener((View view) -> onClickPerformPartialPeriodClosing());
        findViewById(R.id.card_circuits).setOnClickListener((View view) -> onClickCardCircuits());
        findViewById(R.id.status).setOnClickListener((View view) -> onClickStatus());
        findViewById(R.id.apdu_card_tunnel).setOnClickListener((View view) -> onClickApduCardTunnel());

        MPALogging.addLogger(androidLogger);
        resultTextField.setMovementMethod(new ScrollingMovementMethod());
        loadLastAppState();
        updateRestClientServices();
    }

    @Override
    protected void onStart() {
        super.onStart();
        loadLastAppState();
        refreshDisplayedSettings();

        if (restApi) {
            createRestTerminal();
        } else {
            createSdkTerminal(ExternalTerminal.TerminalType.OPI_NL);
        }

        resultFieldFormatter = new OutputFormatter(line -> runOnUiThread(() -> resultTextField.append(line)));
    }

    @Override
    protected void onDestroy() {
        MPALogging.removeLogger(androidLogger);
        super.onDestroy();
    }

    public void onClickRetrieveLastTicket() {
        if (restApi) {
            Call<JsonObject> call = terminalRestService.retrieveLastTicket(new TerminalRequest.Builder().terminal(restTerminal).build());
            terminalActionRestCall(call);
        } else {
            initTerminalService();
            terminalService.retrieveLastTicket(sdkTerminal, terminalDelegate());
        }
    }

    public void onClickRetrieveOpenPreAuthorisations() {
        if (!restApi) {
            initTerminalService();
            terminalService.retrieveOpenPreAuthorisations(sdkTerminal, terminalDelegate());
        }
    }

    public void onClickCardDetection() {
        if (restApi) {
            restCardDetection();
        } else {
            cardDetection();
        }
    }

    public void onClickPaymentRecovery() {
        if (StringUtils.isEmpty(previousPaymentId)) {
            Toast.makeText(this, "There was no successful previous payment recorded", Toast.LENGTH_LONG).show();
        } else {
            if (restApi) {
                terminalActionRestCall(terminalRestService.paymentRecovery(new TerminalRequest.Builder().paymentRequestId(previousPaymentId).terminal(restTerminal).build()));
            } else {
                paymentRecovery();
            }
        }
    }

    public void onClickCardValidation() {
        if (restApi) {
            Toast.makeText(this, "This feature is currently not yet in the rest interface", Toast.LENGTH_LONG).show();
        } else {
            cardValidation();
        }
    }

    public void onClickSilentAbort() {
        if (restApi) {
            paymentRestService.abort(new TerminalRequest.Builder().terminal(restTerminal).build());
        } else {
            abort(true);
        }
    }

    public void onClickSilentAbortOnNewConnection() {
        if (restApi) {
            paymentRestService.abort(new TerminalRequest.Builder().terminal(restTerminal).build());
        } else {
            abortOnNewConnection(true);
        }
    }

    public void onClickNormalAbort() {
        if (restApi) {
            Toast.makeText(this, "Normal abort not possible via Rest API", Toast.LENGTH_LONG).show();
        } else {
            abortOnNewConnection(true);
        }
    }

    public void onClickPreAuthorisation() {
        if (restApi) {
            restPreAuthorisation(false);
        } else {
            preAuthorisation(false);
        }
    }

    public void onClickPreAuthorisationMifareCards() {
        if (restApi) {
            restPreAuthorisation(true);
        } else {
            preAuthorisation(true);
        }
    }

    public void onClickCancelPreAuthorisation() {
        if (restApi) {
            askReasonCode();
        } else {
            setupTokenDelegate();
            askReasonCode();
        }
    }

    private void askReasonCode() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        String[] reasonCodes = Stream.of(FinancialAdviceCancellationReasonCode.values()).map(reasonCode -> reasonCode.name().replace("_", " ").toLowerCase(Locale.ROOT)).toArray(String[]::new);
        builder.setTitle("Cancel reason");
        builder.setSingleChoiceItems(reasonCodes, -1, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                financialAdviceCancellationReasonCode = FinancialAdviceCancellationReasonCode.valueOf(reasonCodes[which].replace(" ", "_").toUpperCase(Locale.ROOT));
            }
        });

        // add OK and Cancel buttons
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (restApi) {
                    restCancelPreAuthorisation();
                } else {
                    financialAdvice(financialAdviceCancellationReasonCode);
                }
                dialog.dismiss();
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        runOnUiThread(builder::show);
    }

    public void onClickFinancialAdvice() {
        if (restApi) {
            restFinancialAdvice();
        } else {
            setupTokenDelegate();
            financialAdvice(null);
        }

    }

    public void onClickFinancialAdviceGermanEichrecht() {
        if (!restApi) {
            setupTokenDelegate();
            financialAdvice(null, true);
        }

    }

    public void onClickPaymentDeliveryBoxSuccess() {
        if (restApi) {
            restPaymentDeliveryBox(true, false);
        } else {
            paymentDeliveryBox(true);
        }
    }

    public void onClickPaymentDeliveryBoxSuccessWithEReceipt() {
        if (restApi) {
            restPaymentDeliveryBox(true, true);
        } else {
            paymentDeliveryBox(true, true);
        }
    }

    public void onClickPaymentDeliveryBoxSuccessWithGermanEichrecht() {
        if (!restApi) {
            paymentDeliveryBox(true, false, true);
        }
    }

    private PaymentDelegate paymentDelegate(boolean deliverySuccessful) {
        return new PaymentDelegate() {
            @Override
            public void eReceipt(EReceiptRequest eReceiptRequest) {
                log(eReceiptRequest.toString());
                resultFieldFormatter.print(eReceiptRequest.toString());
            }

            @Override
            public void showOnCustomerDisplay(MainTextRequest mainText, DisplayTextRequest subText) {
                logCustomerDisplayMessages(mainText, subText);
            }

            @Override
            public void onDeliverGoodsOrServices(DeliveryBoxCallback deliveryBoxCallback) {
                deliveryBoxCallback.proceed(deliverySuccessful);
                String goodsDeliveredMessage = deliverySuccessful ? "Goods Delivered" : "Goods Not Delivered";
                log("Deliver Goods Or Services: " + goodsDeliveredMessage);
            }

            @Override
            public void onPaymentSuccess(PaymentResult result) {
                log("PaymentResult: " + result.toString());
                resultFieldFormatter.printResult(result);
                cardDetectionReferenceNumber = null;
            }

            @Override
            public void onError(Error error) {
                log("onError: " + error.toString());
                resultFieldFormatter.printError(error.mapiError());
                cardDetectionReferenceNumber = null;
            }

            @Override
            public void cardUID(String cardUID) {
                resultFieldFormatter.print("CardUUID: " + cardUID);
            }

            @Override
            public void printJournalReceipt(PaymentReceipt paymentReceipt) {
                log("[ PaymentService ]: Print journal receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printMerchantReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ PaymentService ]: Print Merchant receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printCustomerReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ PaymentService ]: Print Customer receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }
        };
    }

    private void setupTokenDelegate() {
        tokenDelegate = new TokenDelegate() {
            @Override
            public void onTokenSuccess(TokenResult tokenResult) {
                resultFieldFormatter.print(tokenResult.toString());
            }

            @Override
            public void onError(Error error) {
                log("[ TokenService ]: CardFinancialAdvice error");
                if (error.result() != null && ResultState.DEVICE_UNAVAILABLE.equals(error.result().resultState())) {
                    Timer timer = new Timer();
                    timer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            financialAdvice(null);
                        }
                    }, 10000);
                }
                resultFieldFormatter.printError(error.mapiError());
            }

            @Override
            public void printJournalReceipt(PaymentReceipt paymentReceipt) {
                log("[ Tokenservice ]: Print journal receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printMerchantReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ Tokenservice ]: Print Merchant receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printCustomerReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ Tokenservice ]: Print Customer receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void eReceipt(EReceiptRequest eReceiptRequest) {
                resultFieldFormatter.print(eReceiptRequest.toString());
            }
        };
    }

    private TerminalDelegate terminalDelegate() {
        return new TerminalDelegate() {
            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                resultFieldFormatter.print("PaymentAdministrationService: " + result.toString());
            }

            @Override
            public void onError(Error error) {
                resultFieldFormatter.printError(error.mapiError());
                if (error.result() != null) {
                    resultFieldFormatter.printResult(error.result());
                }
            }

            @Override
            public void printCustomerReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ PaymentAdministrationService ]: Print Customer receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printMerchantReceiptAndSignature(PaymentReceipt paymentReceipt) {
                log("[ PaymentAdministrationService ]: Print Merchant receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void printJournalReceipt(PaymentReceipt paymentReceipt) {
                log("[ PaymentAdministrationService ]: Print Journal receipt ");
                resultFieldFormatter.printTicket(paymentReceipt.plainTextLines());
            }

            @Override
            public void eReceipt(EReceiptRequest eReceiptRequest) {
                resultFieldFormatter.print(eReceiptRequest.toString());
            }
        };
    }

    private List<EReceiptAdditionalTextRequest> eReceiptAdditionalTextRequest() {
        String headerLine = "Welcome To My shop";
        String footerLine = "Thank You For Coming";

        List<String> headerLines = new ArrayList<>();
        List<String> footerLines = new ArrayList<>();

        headerLines.add(headerLine);
        footerLines.add(footerLine);

        EReceiptAdditionalTextRequest request = new EReceiptAdditionalTextRequest(EReceiptTextPlacement.HEADER, headerLines);
        EReceiptAdditionalTextRequest footer = new EReceiptAdditionalTextRequest(EReceiptTextPlacement.FOOTER, footerLines);

        List<EReceiptAdditionalTextRequest> requestList = new ArrayList<>();
        requestList.add(request);
        requestList.add(footer);
        return requestList;
    }

    public void onClickPaymentDeliveryBoxFailed() {
        if (restApi) {
            restPaymentDeliveryBox(false, false);
        } else {
            paymentDeliveryBox(false);
        }
    }

    public void onClickReadCardUIDUsingFlexo() {
        if (restApi) {
            terminalActionRestCall(terminalRestService.readMifareUID(new TerminalRequest.Builder().terminal(restTerminal).build()));
        } else {
            readCardUID(true);
        }
    }

    public void onClickReadCardUIDUsingCardTunnel() {
        if (!restApi) {
            readCardUID(false);
        }
    }

    public void onClickApduCardTunnel() {
        if (!restApi) {
            apduCommandViaCardTunnel();
        }
    }

    private void apduCommandViaCardTunnel() {
        initTerminalService();

        TerminalDelegate delegate = new TerminalDelegate() {
            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                log("[TerminalService] OnPaymentAdministrationSuccess: " + result.toString());
                resultFieldFormatter.print(result.toString());
            }

            @Override
            public void onError(Error error) {
                log("[TerminalService] onError " + error.toString());
            }
        };

        terminalService.onlineAgent(sdkTerminal, OnlineAgentRequest.builder().tunnelRequest(TunnelRequest.builder().cardSlotNo(CardSlotNo.CUSTOMER_CONTACTLESS_READER).command("BCA40000022901").build()).build(),delegate);
    }

    private void readCardUID(boolean useFlexo) {
        initTerminalService();

        TerminalDelegate delegate = new TerminalDelegate() {
            @Override
            public void cardUID(String cardUID) {
                log("[TerminalService] CardUID: " + cardUID);
                resultFieldFormatter.print("CardUUID: " + cardUID);
            }

            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                log("[TerminalService] OnPaymentAdministrationSuccess: " + result.toString());
                resultFieldFormatter.print(result.toString());
            }

            @Override
            public void onError(Error error) {
                log("[TerminalService] onError " + error.toString());
            }
        };

        if(useFlexo) {
            terminalService.cardDetection(sdkTerminal, CardDetectionRequest.builder().includeFlexo(true).includeCardTunnel(false).build(),delegate);
        } else {
            terminalService.cardDetection(sdkTerminal, CardDetectionRequest.builder().includeCardTunnel(true).includeFlexo(false).build(),delegate);
        }

    }

    public void onClickGetTransactionOverview() {
        if (restApi) {
            terminalActionRestCall(terminalRestService.transactionOverview(new TerminalRequest.Builder().terminal(restTerminal).build()));
        } else {
            initTerminalService();
            terminalService.transactionOverview(sdkTerminal.shiftNumber(1), terminalDelegate());
        }
    }

    public void onClickPerformPeriodClosing() {
        if (restApi) {
            terminalActionRestCall(terminalRestService.periodClosing(new TerminalRequest.Builder().terminal(restTerminal).build()));
        } else {
            initTerminalService();
            terminalService.periodClosing(sdkTerminal, terminalDelegate());
        }
    }

    public void onClickPerformPartialPeriodClosing() {
        if (!restApi) {
            initTerminalService();
            terminalService.partialPeriodClosing(sdkTerminal, terminalDelegate());
        }
    }

    public void onClickCardCircuits() {
        if (restApi) {
            terminalActionRestCall(terminalRestService.brands(new TerminalRequest.Builder().terminal(restTerminal).build()));
        } else {
            initTerminalService();
            terminalService.cardCircuits(sdkTerminal, terminalDelegate());
        }
    }

    public void onClickStatus() {
        if (restApi) {
            terminalActionRestCall(terminalRestService.status(new TerminalRequest.Builder().terminal(restTerminal).build()));
        } else {
            initTerminalService();
            terminalService.status(sdkTerminal, terminalDelegate());
        }
    }

    private void paymentDeliveryBox(boolean deliverySuccessful) {
        paymentDeliveryBox(deliverySuccessful, false);
    }

    private void paymentDeliveryBox(boolean deliverySuccessful, boolean eReceipt) {
        paymentDeliveryBox(deliverySuccessful, eReceipt, false);
    }

    private void paymentDeliveryBox(boolean deliverySuccessful, boolean eReceipt, boolean germanEichrecht) {
        cardDetectionFollowupActionInitiated = true;
        initialisePaymentService();

        previousPaymentId = Id.generate();

        Payment.PaymentBuilder paymentBuilder = Payment
                .builder()
                .type(Payment.Type.SALE)
                .requestId(previousPaymentId)
                .amount(new Money(amount, Currency.getInstance("EUR")))
                .lastReceiptNumber("1");

        if (cardDetectionReferenceNumber != null) {
            paymentBuilder.referenceNumber(cardDetectionReferenceNumber);
        }

        if (eReceipt) {
            paymentBuilder.eReceiptAdditionalTextRequestList(eReceiptAdditionalTextRequest());
        }

        if (germanEichrecht) {
            paymentBuilder.germanEichrecht(new GermanEichrecht("3031323334", "3031323334"));
        }

        paymentService.payment(sdkTerminal.shiftNumber(1).supportMifareCards(true), paymentBuilder.build(), paymentDelegate(deliverySuccessful));
    }

    private void restPaymentDeliveryBox(boolean deliverySuccessful, boolean eReceipt) {
        previousPaymentId = Id.generate();
        eu.ccvlab.pi_api.domain.PaymentRequest.Builder paymentBuilder = new eu.ccvlab.pi_api.domain.PaymentRequest.Builder()
                .amount(amount)
                .currency("EUR")
                .requestId(previousPaymentId)
                .paymentType(Payment.Type.SALE)
                .terminal(restTerminal);

        if (eReceipt) {
            paymentBuilder.eReceipt(new EReceipt(
                    Collections.singletonList("Welcome To My shop"),
                    null,
                    Collections.singletonList("Thank You For Coming")
            ));
        }

        Call<JsonObject> call = paymentRestService.payment(paymentBuilder.build());
        call.enqueue(new Callback<JsonObject>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.code() == 201 && response.body() != null) {
                    if (UserAction.valueOf(response.body().get("userActionRequired").getAsString()).equals(UserAction.DELIVERYBOX)) {
                        deliveryBox(deliverySuccessful);
                    }
                } else if (response.code() == 200) {
                    System.out.println(response.body());
                    if (response.body() != null) {
                        runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                    }

                    if (response.errorBody() != null) {
                        PaymentResponse paymentResponse = new Gson().fromJson(response.errorBody().string(), PaymentResponse.class);
                        runOnUiThread(() -> resultFieldFormatter.print(paymentResponse.toString()));
                    }
                    runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    handler.removeCallbacks(runnable);
                    runnable = null;
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                runOnUiThread(() -> resultFieldFormatter.print("Call failed: " + t.getMessage()));
                System.out.println("Failed");
            }
        });

        displayMessages();
    }

    private void displayMessages() {
        handler = new Handler();
        runnable = new Runnable() {
            @Override
            public void run() {
                Call<DisplayMessage> displayMessageCall = paymentRestService.displayMessage();
                displayMessageCall.enqueue(new Callback<DisplayMessage>() {
                    @Override
                    public void onResponse(Call<DisplayMessage> call, Response<DisplayMessage> response) {
                        if (response.body() != null && StringUtils.isNotEmpty(response.body().toString())) {
                            runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                        }
                    }

                    @Override
                    public void onFailure(Call<DisplayMessage> call, Throwable t) {

                    }
                });
                handler.postDelayed(this, 10000);
            }
        };
        handler.post(runnable);

        new Thread(runnable).start();
    }

    private void deliveryBox(boolean deliverySuccessful) {
        Call<PaymentResponse> call = paymentRestService.deliveryBox(new eu.ccvlab.pi_api.domain.DeliveryBoxCallback(deliverySuccessful));

        call.enqueue(new Callback<PaymentResponse>() {
            @Override
            public void onResponse(Call<PaymentResponse> call, Response<PaymentResponse> response) {
                if (response.body() != null) {
                    runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                }
                handler.removeCallbacks(runnable);
            }

            @Override
            public void onFailure(Call<PaymentResponse> call, Throwable t) {
                System.out.println(t);
            }
        });
    }

    private void restCardDetection() {
        Call<TokenResponse> call = tokenRestService.token(new TokenRequest.Builder().tokenType(TokenType.CARD).amount(totalAmount).currency("EUR").terminal(restTerminal).build());
        call.enqueue(new Callback<TokenResponse>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<TokenResponse> call, Response<TokenResponse> response) {
                if (response.code() == 200) {

                    if (response.body() != null) {
                        runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                    }

                    if (response.errorBody() != null) {
                        PaymentResponse paymentResponse = new Gson().fromJson(response.errorBody().string(), PaymentResponse.class);
                        runOnUiThread(() -> resultFieldFormatter.print(paymentResponse.toString()));
                    }
                    runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    handler.removeCallbacks(runnable);
                    runnable = null;
                }
            }

            @Override
            public void onFailure(Call<TokenResponse> call, Throwable t) {
                System.out.println("Failed");
            }
        });

        displayMessages();
    }

    private void logCustomerDisplayMessages(MainTextRequest mainText, DisplayTextRequest subText) {
        if (mainText != null) {
            log(String.format("[ TokenService ]: Customer Display MainText: %s", mainText.text()));
        }

        if (subText != null) {
            log(String.format("[ TokenService ]: Customer Display SubText: %s", subText.text()));
        }
    }


    private void cardDetection() {
        TerminalDelegate terminalDelegate = new TerminalDelegate() {
            @Override
            public void showOnCustomerDisplay(MainTextRequest mainText, DisplayTextRequest subText) {
                logCustomerDisplayMessages(mainText, subText);
            }

            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                log("PaymentAdministrationResult: " + result.toString());
                setPaymentAdministrationResult(result);
                resultFieldFormatter.printResult(result);
                cardDetectionReferenceNumber = Integer.valueOf(result.requestId());
                cardDetectionFollowupActionInitiated = false;
                startFollowupTimer();
                showFollowupDialog();
            }

            @Override
            public void onError(Error error) {
                log("[ TokenService ]: CardDetection error");
                resultFieldFormatter.printError(error.mapiError());
            }

            @Override
            public void cardUID(String cardUID) {
                resultFieldFormatter.print("CardUUID: " + cardUID);
            }
        };

        initTerminalService();
        sdkTerminal.supportMifareCards(true);
        terminalService.cardDetection(sdkTerminal, CardDetectionRequest.builder().includeCardTunnel(false).includeFlexo(false).tokenPurpose(TokenPurpose.TRACKING).build(), terminalDelegate());
    }

    private void startFollowupTimer() {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if (!cardDetectionFollowupActionInitiated) {
                    abort(true);
                }
            }
        }, 175000);
    }

    private void showFollowupDialog() {
        runOnUiThread(() -> {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            //Setting message manually and performing action on button click
            builder.setMessage("CardDetection followup action required. When no followup action is initiated within 180 seconds, the CardDetection will automatically be aborted. \n\n Possible followup actions: \n - CardPayment \n - Abort").setTitle("Followup Action Required")
                    .setCancelable(true)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
            //Creating dialog box
            AlertDialog alert = builder.create();
            //Setting the title manually
            alert.setTitle("Followup Action Required");
            alert.show();
        });
    }

    private void paymentRecovery() {
        initTerminalService();
        terminalService.recoverPayment(sdkTerminal, previousPaymentId, terminalDelegate());
    }

    private void cardValidation() {
        initialiseTokenService();
        setupTokenDelegate();
        tokenService.validateCard(sdkTerminal, tokenDelegate);
    }

    public void createSdkTerminal(ExternalTerminal.TerminalType terminalType) {
        this.sdkTerminal = ExternalTerminal.builder()
                .ipAddress(terminalIp)
                .port(4100)
                .compatibilityPort(4102)
                .printerStatus(PrinterStatusType.AVAILABLE)
                .terminalType(terminalType)
                .socketMode(ExternalTerminal.SocketMode.DUAL_SOCKET)
                .terminalDisplayText(Arrays.asList(
                        new TerminalDisplayText(TerminalDisplayTextWindow.SCREEN_WAIT_FOR_CARD, Arrays.asList("We will use your", "card to retrieve", "the moment of", "parking entry")),
                        new TerminalDisplayText(TerminalDisplayTextWindow.SCREEN_AT_WAIT_FOR_FOLLOW_UP, Arrays.asList("Your parking fee", "is being", "calculated...")))
                )
                .requestTransactionInformation(true)
                .build();
    }

    public void createRestTerminal() {
        this.restTerminal = new Terminal.Builder()
                .ipAddress("127.0.0.1")
                .languageCode("NLD")
                .terminalOperatingEnvironment("UNATTENDED")
                .terminalAccessProtocol("OPI_NL")
                .socketMode("DUAL_SOCKET")
                .shiftNumber("1")
                .build();
    }

    private void abort(Boolean isSilent) {
        cardDetectionFollowupActionInitiated = true;
        TerminalDelegate terminalDelegate = new TerminalDelegate() {
            @Override
            public void showOnCustomerDisplay(MainTextRequest mainText, DisplayTextRequest subText) {
                logCustomerDisplayMessages(mainText, subText);
            }

            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                log("PaymentAdministrationResult: " + result.toString());
                setPaymentAdministrationResult(result);
                resultFieldFormatter.printResult(result);
            }

            @Override
            public void onError(Error error) {
                log(String.format("[ TokenService ]: %s", error.toString()));
                resultFieldFormatter.printError(error.mapiError());
            }
        };
        initTerminalService();
        terminalService.abort(isSilent, sdkTerminal, terminalDelegate);
    }

    private void abortOnNewConnection(Boolean isSilent) {
        cardDetectionFollowupActionInitiated = true;
        TerminalDelegate terminalDelegate = new TerminalDelegate() {
            @Override
            public void showOnCustomerDisplay(MainTextRequest mainText, DisplayTextRequest subText) {
                logCustomerDisplayMessages(mainText, subText);
            }

            @Override
            public void onPaymentAdministrationSuccess(PaymentAdministrationResult result) {
                log("PaymentAdministrationResult: " + result.toString());
                setPaymentAdministrationResult(result);
                resultFieldFormatter.printResult(result);
            }

            @Override
            public void onError(Error error) {
                log(String.format("[ AbortOnNewConnection OnError ]: %s", error.toString()));
                resultFieldFormatter.printError(error.mapiError());
            }

        };
        initTerminalService();
        terminalService.abortOnNewConnection(isSilent, sdkTerminal, terminalDelegate);
    }

    private void preAuthorisation(boolean supportMifareCards) {

        initialisePaymentService();
        tokenDelegate = new TokenDelegate() {
            @Override
            public void onTokenSuccess(TokenResult tokenResult) {
                log("PreAuthorisation TokenResult: " + tokenResult.toString());
                setTokenResult(tokenResult);
                resultFieldFormatter.printResult(tokenResult);
            }

            @Override
            public void onError(Error error) {
                log(String.format("[ PreAuthorisation OnError ]: %s", error.toString()));
                resultFieldFormatter.printError(error.mapiError());
            }

            @Override
            public void cardUID(String cardUID) {
                resultFieldFormatter.print("CardUUID: " + cardUID);
            }
        };

        PreAuthorisationRequest.PreAuthorisationRequestBuilder preAuthorisationRequest = PreAuthorisationRequest.builder()
                .minimumAmount(Money.EUR(minimumAmount))
                .totalAmount(Money.EUR(totalAmount))
                .environment(Environment.EVCHARGING)
                .tokenPurpose(TokenPurpose.TRACKING);

        sdkTerminal.supportMifareCards(true);

        //If result is not null then pass it's requestId as a reference
        if (paymentAdministrationResult != null) {
            paymentService.preAuthorisation(sdkTerminal, preAuthorisationRequest.referenceNumber(Integer.valueOf(paymentAdministrationResult.requestId())).build(), tokenDelegate);
        } else {
            paymentService.preAuthorisation(sdkTerminal, preAuthorisationRequest.build(), tokenDelegate);
        }
    }

    private void restPreAuthorisation(boolean supportMifareCards) {
        previousPaymentId = Id.generate();

        Call<JsonObject> call = paymentRestService.payment(new PaymentRequest.Builder()
                .paymentType(Payment.Type.AUTHORISE)
                .requestId(previousPaymentId)
                .terminalEnvironment(Environment.EVCHARGING.toString())
                .currency("EUR")
                .amount(totalAmount)
                .minimumCaptureAmount(minimumAmount)
                .tokenType(TokenType.CARD)
                .terminal(restTerminal)
                .supportMifareCards(supportMifareCards)
                .build()
        );

        call.enqueue(new Callback<JsonObject>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.code() == 200) {
                    System.out.println(response.body());
                    if (response.body() != null) {
                        runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                        tokenResult = TokenResult.builder()
                                .preAuthReference(PreAuthReferenceRequest.builder()
                                        .authReference(response.body().get("authReference").getAsString())
                                        .version(response.body().get("authReferenceVersion").getAsString())
                                        .build())
                                .build();
                    }

                    if (response.errorBody() != null) {
                        PaymentResponse paymentResponse = new Gson().fromJson(response.errorBody().string(), PaymentResponse.class);
                        runOnUiThread(() -> resultFieldFormatter.print(paymentResponse.toString()));
                    }
                    runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    handler.removeCallbacks(runnable);
                    runnable = null;
                } else {
                    String errorMessage = response.errorBody() != null ? response.errorBody().string() : "No body";
                    runOnUiThread(() -> resultFieldFormatter.print("Reservation failed with response: \n" + errorMessage));
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                System.out.println("Failed");
            }
        });

        displayMessages();
    }

    private void restFinancialAdvice() {
        Call<JsonObject> call = paymentRestService.payment(
                new PaymentRequest.Builder()
                        .terminalEnvironment(Environment.EVCHARGING.toString())
                        .currency("EUR")
                        .requestId(previousPaymentId)
                        .paymentType(Payment.Type.CAPTURE)
                        .amount(totalAmount)
                        .terminal(restTerminal)
                        .authReference(tokenResult != null ? tokenResult.preAuthReference().authReference() : "")
                        .authReferenceVersion(tokenResult != null ? tokenResult.preAuthReference().version() : "")
                        .eReceipt(new EReceipt(
                                Collections.singletonList("Welcome To My shop"),
                                null,
                                Collections.singletonList("Thank You For Coming")
                        )).build()
        );

        call.enqueue(new Callback<JsonObject>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.code() == 200) {
                    System.out.println(response.body());
                    if (response.body() != null) {
                        runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                    }

                    if (response.errorBody() != null) {
                        PaymentResponse paymentResponse = new Gson().fromJson(response.errorBody().string(), PaymentResponse.class);
                        runOnUiThread(() -> resultFieldFormatter.print(paymentResponse.toString()));
                    }
                    runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    handler.removeCallbacks(runnable);
                    runnable = null;
                } else {
                    String errorMessage = response.errorBody() != null ? response.errorBody().string() : "No body";
                    runOnUiThread(() -> resultFieldFormatter.print("Reservation finalization failed with response: \n" + errorMessage));
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                runOnUiThread(() -> resultFieldFormatter.print("Reservation finalization failed to call"));
            }
        });

        displayMessages();
    }

    private void restCancelPreAuthorisation() {
        Call<JsonObject> call = paymentRestService.payment(
                new PaymentRequest.Builder()
                        .terminalEnvironment(Environment.EVCHARGING.toString())
                        .captureCancellationReasonCode(financialAdviceCancellationReasonCode.name())
                        .currency("EUR")
                        .requestId(previousPaymentId)
                        .paymentType(Payment.Type.CAPTURE)
                        .amount(BigDecimal.ZERO)
                        .terminal(restTerminal)
                        .authReference(tokenResult != null ? tokenResult.preAuthReference().authReference() : "")
                        .authReferenceVersion(tokenResult != null ? tokenResult.preAuthReference().version() : "")
                        .eReceipt(new EReceipt(
                                Collections.singletonList("Welcome To My shop"),
                                null,
                                Collections.singletonList("Thank You For Coming")
                        )).build()
        );

        call.enqueue(new Callback<JsonObject>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.code() == 200) {
                    System.out.println(response.body());
                    if (response.body() != null) {
                        runOnUiThread(() -> resultFieldFormatter.print(response.body().toString()));
                    }

                    if (response.errorBody() != null) {
                        PaymentResponse paymentResponse = new Gson().fromJson(response.errorBody().string(), PaymentResponse.class);
                        runOnUiThread(() -> resultFieldFormatter.print(paymentResponse.toString()));
                    }
                    runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    handler.removeCallbacks(runnable);
                    runnable = null;
                } else {
                    String errorMessage = response.errorBody() != null ? response.errorBody().string() : "No body";
                    runOnUiThread(() -> resultFieldFormatter.print("Reservation finalization failed with response: \n" + errorMessage));
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                runOnUiThread(() -> resultFieldFormatter.print("Reservation finalization failed to call"));
            }
        });

        displayMessages();
    }

    private void terminalActionRestCall(Call<JsonObject> call) {
        call.enqueue(new Callback<JsonObject>() {
            @SneakyThrows
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
                if (response.code() == 200) {
                    TerminalResponse terminalResponse = null;
                    if (response.body() != null) {
                        terminalResponse = new Gson().fromJson(response.body().toString(), TerminalResponse.class);
                    }

                    if (response.errorBody() != null) {
                        terminalResponse = new Gson().fromJson(response.errorBody().toString(), TerminalResponse.class);
                    }

                    if (terminalResponse != null) {
                        TerminalResponse finalTerminalResponse = terminalResponse;
                        runOnUiThread(() -> resultFieldFormatter.print(finalTerminalResponse.toString()));
                    } else {
                        runOnUiThread(() -> resultFieldFormatter.print(response.message()));
                    }
                    handler.removeCallbacks(runnable);
                    runnable = null;
                }
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                System.out.println("Failed");
            }
        });

        displayMessages();
    }

    private void setPaymentAdministrationResult(PaymentAdministrationResult paymentAdministrationResult) {
        this.paymentAdministrationResult = paymentAdministrationResult;
    }

    private void setTokenResult(TokenResult tokenResult) {
        this.tokenResult = tokenResult;
    }

    private void financialAdvice(FinancialAdviceCancellationReasonCode financialAdviceCancellationReasonCode) {
        financialAdvice(financialAdviceCancellationReasonCode, false);
    }

    private void financialAdvice(FinancialAdviceCancellationReasonCode financialAdviceCancellationReasonCode, boolean germanEichrecht) {
        initialisePaymentService();
        if (tokenResult != null && tokenResult.preAuthReference() != null) {
            FinancialAdviceRequest financialAdviceRequest = FinancialAdviceRequest.builder()
                    .totalAmount(financialAdviceCancellationReasonCode == null ? Money.EUR(amount) : Money.EUR(0))
                    .environment(Environment.EVCHARGING)
                    .preAuthReferenceRequest(tokenResult.preAuthReference())
                    .eReceiptAdditionalTextRequestList(eReceiptAdditionalTextRequest())
                    .financialAdviceCancellationReasonCode(financialAdviceCancellationReasonCode)
                    .germanEichrecht(germanEichrecht ? new GermanEichrecht("3031323334", "3031323334") : null)
                    .build();
            paymentService.financialAdvice(sdkTerminal, financialAdviceRequest, tokenDelegate);
        } else {
            Toast.makeText(this, "You can only perform a CardFinancialAdvice after having performed a CardPreAuthorisation", Toast.LENGTH_LONG).show();
        }
    }


    private void log(String line) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {

                if (resultTextField != null)
                    resultTextField.append(String.format("\n\n%s\n\n", line));
            }
        });

    }

    @SuppressLint("LongLogTag")
    private void loadLastAppState() {
        String xml = null;
        try {
            File file = getApplicationContext().getFileStreamPath("state.json");

            int size = (int) file.length();
            byte[] bytes = new byte[size];
            BufferedInputStream buf;

            buf = new BufferedInputStream(new FileInputStream(file));
            buf.read(bytes, 0, bytes.length);
            buf.close();

            xml = new String(bytes);
        } catch (IOException e) {
            Log.e("UnAttendedTerminalActivity", "Unable to read previous app state");
        }

        this.appState = xml != null ? new Gson().fromJson(xml, MapiDemoState.class) : new MapiDemoState();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            Intent intent = new Intent(this, UnAttendedSettingsActivity.class);
            startActivity(intent);
            return true;
        } else if (id == R.id.action_clear_output) {
            resultTextField.setText("");
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void refreshDisplayedSettings() {
        String protocol = "OPI-NL";
        if (appState.getUnAttendedHostname(protocol) != null) {
            RestClientProvider.instance().setBaseUrl(appState.getUnAttendedHostname(protocol));
            updateRestClientServices();
        }
        terminalIp = appState.getUnAttendedHostname(protocol);
        amount = new BigDecimal(appState.getUnAttendedAmount(protocol));
        minimumAmount = new BigDecimal(appState.getUnAttendedMinimumAmount(protocol));
        totalAmount = new BigDecimal(appState.getUnAttendedTotalAmount(protocol));
        restApi = appState.getRestApi(protocol);
    }

    private void initialisePaymentService() {
        if (paymentService == null) {
            paymentService = new PaymentService();
        }
    }

    private void initTerminalService() {
        if (terminalService == null) {
            terminalService = new TerminalService();
        }
    }

    private void initialiseTokenService() {
        if (tokenService == null) {
            tokenService = new TokenService();
        }
    }

    private void updateRestClientServices() {
        paymentRestService = new PaymentServiceImp(RestClientProvider.instance().createService(PaymentRestService.class));
        tokenRestService = new TokenServiceImp(RestClientProvider.instance().createService(TokenRestService.class));
        terminalRestService = new TerminalServiceImp(RestClientProvider.instance().createService(TerminalRestService.class));
    }
}
