/*
 * Copyright (C) 2020 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.settings.network.telephony;

import android.Manifest;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;

import com.android.settings.SidecarFragment;
import com.android.settings.network.SwitchSlotSidecar;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * The sidecar base class that an Euicc sidecar can extend from. The extended class should implement
 * getReceiverAction() to return the action string for the broadcast receiver. The extended class
 * should implement its own get() function to return an instance of that class, and implement the
 * functional class like run() to actually trigger the function in EuiccManager.
 */
public abstract class EuiccOperationSidecar extends SidecarFragment
        implements SidecarFragment.Listener{
    private static final String TAG = "EuiccOperationSidecar";
    private static final int REQUEST_CODE = 0;
    private static final String EXTRA_OP_ID = "op_id";
    private static AtomicInteger sCurrentOpId =
            new AtomicInteger((int) SystemClock.elapsedRealtime());

    protected EuiccManager mEuiccManager;
    protected TelephonyManager mTelephonyManager;
    protected SwitchSlotSidecar mSwitchSlotSidecar;


    private int mResultCode;
    private int mDetailedCode;
    private Intent mResultIntent;
    private int mOpId;

    protected final BroadcastReceiver mReceiver =
            new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (getReceiverAction().equals(intent.getAction())
                            && mOpId == intent.getIntExtra(EXTRA_OP_ID, -1)) {
                        mResultCode = getResultCode();
                        /* TODO: This relies on our LUI and LPA to coexist, should think about how
                        to generalize this further. */
                        mDetailedCode =
                                intent.getIntExtra(
                                        EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                                        0 /* defaultValue*/);
                        mResultIntent = intent;
                        Log.i(
                                TAG,
                                String.format(
                                        "Result code : %d; detailed code : %d",
                                        mResultCode, mDetailedCode));
                        onActionReceived();
                    }
                }
            };

    /**
     * This is called when the broadcast action is received. The subclass may override this to
     * perform different logic. The broadcast result code may be obtained with {@link
     * #getResultCode()} and the Intent may be obtained with {@link #getResultIntent()}.
     */
    protected void onActionReceived() {
        if (mResultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
            setState(State.SUCCESS, Substate.UNUSED);
        } else {
            setState(State.ERROR, mResultCode);
        }
    }

    /**
     * The extended class should implement it to return a string for the broadcast action. The class
     * should be unique across all the child classes.
     */
    protected abstract String getReceiverAction();

    protected PendingIntent createCallbackIntent() {
        mOpId = sCurrentOpId.incrementAndGet();
        Intent intent = new Intent(getReceiverAction());
        intent.putExtra(EXTRA_OP_ID, mOpId);
        return PendingIntent.getBroadcast(
                getContext(), REQUEST_CODE, intent,
                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEuiccManager = getContext().getSystemService(EuiccManager.class);
        mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
        mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());

        getContext()
                .getApplicationContext()
                .registerReceiver(
                        mReceiver,
                        new IntentFilter(getReceiverAction()),
                        Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
                        null,
                        Context.RECEIVER_EXPORTED);
    }

    @Override
    public void onResume() {
        super.onResume();
        mSwitchSlotSidecar.addListener(this);
    }

    @Override
    public void onPause() {
        mSwitchSlotSidecar.removeListener(this);
        super.onPause();
    }

    @Override
    public void onDestroy() {
        getContext().getApplicationContext().unregisterReceiver(mReceiver);
        super.onDestroy();
    }

    @Override
    public void onStateChange(SidecarFragment fragment) {
        if (fragment == mSwitchSlotSidecar) {
            switch (mSwitchSlotSidecar.getState()) {
                case State.SUCCESS:
                    mSwitchSlotSidecar.reset();
                    Log.i(TAG, "mSwitchSlotSidecar SUCCESS");
                    break;
                case State.ERROR:
                    mSwitchSlotSidecar.reset();
                    Log.i(TAG, "mSwitchSlotSidecar ERROR");
                    break;
            }
        } else {
            Log.wtf(TAG, "Received state change from a sidecar not expected.");
        }
    }

    public int getResultCode() {
        return mResultCode;
    }

    public int getDetailedCode() {
        return mDetailedCode;
    }

    public Intent getResultIntent() {
        return mResultIntent;
    }
}