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

import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;

import static com.android.internal.util.CollectionUtils.emptyIfNull;

import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccPortInfo;
import android.telephony.UiccSlotInfo;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/** Shared logic related to dealing with multiple subscriptions. */
public final class SubscriptionUtils {

    private SubscriptionUtils() {
    }

    /**
     * Returns the list of available subscriptions, accounting for duplicates possible through a
     * virtual network provider.
     */
    public static List<SubscriptionInfo> getAvailableSubscriptions(
            SubscriptionManager subscriptionManager, TelephonyManager telephonyManager) {
        List<SubscriptionInfo> subscriptions = new ArrayList<>(
                emptyIfNull(subscriptionManager.getSelectableSubscriptionInfoList()));

        // Look for inactive but present physical SIMs that are missing from the selectable list.
        List<UiccSlotInfo> missing = new ArrayList<>();
        UiccSlotInfo[] slotsInfo = telephonyManager.getUiccSlotsInfo();
        for (int i = 0; slotsInfo != null && i < slotsInfo.length; i++) {
            UiccSlotInfo slotInfo = slotsInfo[i];
            if (isInactiveInsertedPSim(slotInfo)) {
                // With MEP support each slot will have multiple ports.
                for (UiccPortInfo portInfo : slotInfo.getPorts()) {
                    int index = portInfo.getLogicalSlotIndex();
                    String cardId = slotInfo.getCardId();

                    boolean found = subscriptions.stream().anyMatch(
                            info -> index == info.getSimSlotIndex() && cardId.equals(
                                    info.getCardString()));
                    if (!found) {
                        missing.add(slotInfo);
                    }
                }
            }
        }
        if (!missing.isEmpty()) {
            for (SubscriptionInfo info : subscriptionManager.getAllSubscriptionInfoList()) {
                for (UiccSlotInfo slotInfo : missing) {
                    for (UiccPortInfo portInfo : slotInfo.getPorts()) {
                        if (info.getSimSlotIndex() == portInfo.getLogicalSlotIndex()
                                && info.getCardString().equals(slotInfo.getCardId())) {
                            subscriptions.add(info);
                            break;
                        }
                    }
                }
            }
        }

        // With some carriers such as Google Fi which provide a sort of virtual service that spans
        // across multiple underlying networks, we end up with subscription entries for the
        // underlying networks that need to be hidden from the user in the UI.
        for (Iterator<SubscriptionInfo> iter = subscriptions.iterator(); iter.hasNext(); ) {
            SubscriptionInfo info = iter.next();
            if (TextUtils.isEmpty(info.getMncString())) {
                iter.remove();
            }
        }
        return subscriptions;
    }

    private static boolean isInactiveInsertedPSim(UiccSlotInfo slotInfo) {
        return slotInfo != null && !slotInfo.getIsEuicc()
                && slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT
                && slotInfo.getPorts().stream().anyMatch(portInfo -> !portInfo.isActive());
    }
}
