/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.nfc.reader;


import android.app.Instrumentation;
import android.content.Intent;
import android.os.RemoteException;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;

import com.android.nfc.utils.CommandApdu;
import com.android.nfc.utils.HceUtils;
import com.android.nfc.utils.NfcSnippet;
import com.android.nfc.service.AccessService;
import com.android.nfc.service.LargeNumAidsService;
import com.android.nfc.service.OffHostService;
import com.android.nfc.service.PaymentService1;
import com.android.nfc.service.PaymentService2;
import com.android.nfc.service.PaymentServiceDynamicAids;
import com.android.nfc.service.PrefixAccessService;
import com.android.nfc.service.PrefixPaymentService1;
import com.android.nfc.service.PrefixTransportService1;
import com.android.nfc.service.PrefixTransportService2;
import com.android.nfc.service.ScreenOffPaymentService;
import com.android.nfc.service.ScreenOnOnlyOffHostService;
import com.android.nfc.service.ThroughputService;
import com.android.nfc.service.TransportService1;
import com.android.nfc.service.TransportService2;

import com.google.android.mobly.snippet.rpc.AsyncRpc;
import com.google.android.mobly.snippet.rpc.Rpc;

public class NfcReaderDeviceSnippet extends NfcSnippet {
    protected static final String TAG = "NfcSnippet";

    private BaseReaderActivity mActivity;

    /** Opens NFC reader for single non-payment test */
    @Rpc(description = "Open simple reader activity for single non-payment test")
    public void startSingleNonPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(TransportService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(TransportService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for single non-payment test */
    @Rpc(description = "Open simple reader activity for single non-payment test")
    public void startSinglePaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(PaymentService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(PaymentService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for dual payment services test */
    @Rpc(description = "Opens simple reader activity for dual payment services test")
    public void startDualPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(PaymentService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(PaymentService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for foreground payment test */
    @Rpc(description = "Opens simple reader activity for foreground payment test")
    public void startForegroundPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(PaymentService2.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(PaymentService2.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for dynamic AID test */
    @Rpc(description = "Opens simple reader activity for dynamic AID test")
    public void startDynamicAidReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                PaymentServiceDynamicAids.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                PaymentServiceDynamicAids.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for prefix payment test */
    @Rpc(description = "Opens simple reader activity for prefix payment test")
    public void startPrefixPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                PrefixPaymentService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                PrefixPaymentService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for prefix payment 2 test */
    @Rpc(description = "Opens simple reader activity for prefix payment 2 test")
    public void startPrefixPaymentReader2Activity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                PrefixPaymentService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                PrefixPaymentService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Opens simple reader activity for non-payment prefix test */
    @Rpc(description = "Opens simple reader activity for non-payment prefix test.")
    public void startDualNonPaymentPrefixReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        CommandApdu[] prefixTransportService1Commands =
                HceUtils.COMMAND_APDUS_BY_SERVICE.get(PrefixTransportService1.class.getName());
        CommandApdu[] prefixAccessServiceCommands =
                HceUtils.COMMAND_APDUS_BY_SERVICE.get(PrefixAccessService.class.getName());

        // Combine command/response APDU arrays
        CommandApdu[] combinedCommands =
                new CommandApdu
                        [prefixTransportService1Commands.length
                                + prefixAccessServiceCommands.length];
        System.arraycopy(
                prefixTransportService1Commands,
                0,
                combinedCommands,
                0,
                prefixTransportService1Commands.length);
        System.arraycopy(
                prefixAccessServiceCommands,
                0,
                combinedCommands,
                prefixTransportService1Commands.length,
                prefixAccessServiceCommands.length);

        String[] prefixTransportService1Responses =
                HceUtils.RESPONSE_APDUS_BY_SERVICE.get(PrefixTransportService1.class.getName());
        String[] prefixAccessServiceResponses =
                HceUtils.RESPONSE_APDUS_BY_SERVICE.get(PrefixAccessService.class.getName());

        String[] combinedResponses =
                new String
                        [prefixTransportService1Responses.length
                                + prefixAccessServiceResponses.length];
        System.arraycopy(
                prefixTransportService1Responses,
                0,
                combinedResponses,
                0,
                prefixTransportService1Responses.length);
        System.arraycopy(
                prefixAccessServiceResponses,
                0,
                combinedResponses,
                prefixTransportService1Responses.length,
                prefixAccessServiceResponses.length);
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation, combinedCommands, combinedResponses);
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for off host test. */
    @Rpc(description = "Open simple reader activity for off host test")
    public void startOffHostReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(OffHostService.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(OffHostService.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for on and off host test. */
    @Rpc(description = "Open simple reader activity for off host test")
    public void startOnAndOffHostReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        // Merge command/response APDU sequences.
        CommandApdu[] offHostCommands = HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                OffHostService.class.getName());
        CommandApdu[] onHostCommands = HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                TransportService1.class.getName());
        CommandApdu[] combinedCommands =
                new CommandApdu[offHostCommands.length + onHostCommands.length];
        System.arraycopy(offHostCommands, 0, combinedCommands, 0, offHostCommands.length);
        System.arraycopy(onHostCommands, 0, combinedCommands, offHostCommands.length,
                onHostCommands.length);

        String[] offHostResponses = HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                OffHostService.class.getName());
        String[] onHostResponses = HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                TransportService1.class.getName());
        String[] combinedResponses = new String[offHostResponses.length + onHostResponses.length];

        System.arraycopy(offHostResponses, 0, combinedResponses, 0, offHostResponses.length);
        System.arraycopy(onHostResponses, 0, combinedResponses, offHostResponses.length,
                onHostResponses.length);

        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        combinedCommands,
                        combinedResponses);
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for dual non-payment test */
    @Rpc(description = "Open simple reader activity for dual non-payment test")
    public void startDualNonPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        CommandApdu[] transportService2CommandApdus =
                HceUtils.COMMAND_APDUS_BY_SERVICE.get(TransportService2.class.getName());
        String[] transportService2ResponseApdus =
                HceUtils.RESPONSE_APDUS_BY_SERVICE.get(TransportService2.class.getName());

        CommandApdu[] accessServiceCommandApdus =
                HceUtils.COMMAND_APDUS_BY_SERVICE.get(AccessService.class.getName());
        String[] accessServiceResponseApdus =
                HceUtils.RESPONSE_APDUS_BY_SERVICE.get(AccessService.class.getName());
        // Combine command/response APDU arrays
        CommandApdu[] commandSequences =
                new CommandApdu
                        [transportService2CommandApdus.length + accessServiceCommandApdus.length];
        System.arraycopy(
                transportService2CommandApdus,
                0,
                commandSequences,
                0,
                transportService2CommandApdus.length);
        System.arraycopy(
                accessServiceCommandApdus,
                0,
                commandSequences,
                transportService2CommandApdus.length,
                accessServiceCommandApdus.length);
        String[] responseSequences =
                new String
                        [transportService2ResponseApdus.length + accessServiceResponseApdus.length];
        System.arraycopy(
                transportService2ResponseApdus,
                0,
                responseSequences,
                0,
                transportService2ResponseApdus.length);
        System.arraycopy(
                accessServiceResponseApdus,
                0,
                responseSequences,
                transportService2ResponseApdus.length,
                accessServiceResponseApdus.length);

        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation, commandSequences, responseSequences);
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for foreground non-payment test */
    @Rpc(description = "Open simple reader activity for foreground non-payment test")
    public void startForegroundNonPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(TransportService2.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(TransportService2.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for throughput test */
    @Rpc(description = "Open simple reader activity for throughput test")
    public void startThroughputReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(ThroughputService.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(ThroughputService.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for tap test */
    @Rpc(description = "Open simple reader activity for tap test")
    public void startTapTestReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(TransportService1.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(TransportService1.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for large num AIDs test */
    @Rpc(description = "Open simple reader activity for large num AIDs Test")
    public void startLargeNumAidsReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(LargeNumAidsService.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                LargeNumAidsService.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for screen off payment test */
    @Rpc(description = "Open simple reader activity for screen off payment Test")
    public void startScreenOffPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                ScreenOffPaymentService.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                ScreenOffPaymentService.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open protocol params reader activity */
    @Rpc(description = "Open protocol params reader activity")
    public void startProtocolParamsReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClassName(
                instrumentation.getTargetContext(), ProtocolParamsReaderActivity.class.getName());
        mActivity = (ProtocolParamsReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for conflicting non-payment test */
    @Rpc(description = "Open simple reader activity for conflicting non-payment Test")
    public void startConflictingNonPaymentReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(TransportService2.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(TransportService2.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for conflicting non-payment prefix test */
    @Rpc(description = "Open simple reader activity for conflicting non-payment prefix Test")
    public void startConflictingNonPaymentPrefixReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                PrefixTransportService2.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                PrefixTransportService2.class.getName()));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Open simple reader activity for screen on only off-host service test */
    @Rpc(description = "Open simple reader activity for screen on only off-host service Test")
    public void startScreenOnOnlyOffHostReaderActivity() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Intent intent =
                buildReaderIntentWithApduSequence(
                        instrumentation,
                        HceUtils.COMMAND_APDUS_BY_SERVICE.get(
                                ScreenOnOnlyOffHostService.class.getName()),
                        HceUtils.RESPONSE_APDUS_BY_SERVICE.get(
                                ScreenOnOnlyOffHostService.class.getName()
                        ));
        mActivity = (SimpleReaderActivity) instrumentation.startActivitySync(intent);
    }

    /** Registers receiver for Test Pass event */
    @AsyncRpc(description = "Waits for Test Pass event")
    public void asyncWaitForTestPass(String callbackId, String eventName) {
        registerSnippetBroadcastReceiver(
                callbackId, eventName, BaseReaderActivity.ACTION_TEST_PASSED);
    }

    /** Sets the poll tech for the active reader activity */
    @Rpc(description = "Set the listen tech for the emulator")
    public void setPollTech(Integer pollTech) {
        if (mActivity == null) {
            Log.e(TAG, "Activity is null.");
            return;
        }
        mActivity.setPollTech(pollTech);
    }

    /** Resets the poll tech for the active reader activity */
    @Rpc(description = "Reset the listen tech for the emulator")
    public void resetPollTech() {
        if (mActivity == null) {
            Log.e(TAG, "Activity is null.");
            return;
        }
        mActivity.resetPollTech();
    }

    /** Closes reader activity between tests */
    @Rpc(description = "Close activity if one was opened.")
    public void closeActivity() {
        if (mActivity != null) {
            mActivity.finish();
        }
    }

    private Intent buildReaderIntentWithApduSequence(
            Instrumentation instrumentation, CommandApdu[] commandApdus, String[] responseApdus) {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClassName(
                instrumentation.getTargetContext(), SimpleReaderActivity.class.getName());
        intent.putExtra(SimpleReaderActivity.EXTRA_APDUS, commandApdus);
        intent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, responseApdus);
        return intent;
    }
}