/*
 * Copyright (C) 2018 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 android.telephony;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC.
 */
public final class UiccCardInfo implements Parcelable {
    private final boolean mIsEuicc;
    private final int mCardId;
    private final String mEid;
    private final String mIccId;
    private final int mPhysicalSlotIndex;
    private final boolean mIsRemovable;
    private final boolean mIsMultipleEnabledProfilesSupported;
    private final List<UiccPortInfo> mPortList;
    private boolean mIccIdAccessRestricted = false;

    public static final @NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
        @Override
        public UiccCardInfo createFromParcel(Parcel in) {
            return new UiccCardInfo(in);
        }

        @Override
        public UiccCardInfo[] newArray(int size) {
            return new UiccCardInfo[size];
        }
    };

    private UiccCardInfo(Parcel in) {
        mIsEuicc = in.readBoolean();
        mCardId = in.readInt();
        mEid = in.readString8();
        mIccId = in.readString8();
        mPhysicalSlotIndex = in.readInt();
        mIsRemovable = in.readBoolean();
        mIsMultipleEnabledProfilesSupported = in.readBoolean();
        mPortList = new ArrayList<UiccPortInfo>();
        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
        mIccIdAccessRestricted = in.readBoolean();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeBoolean(mIsEuicc);
        dest.writeInt(mCardId);
        dest.writeString8(mEid);
        dest.writeString8(mIccId);
        dest.writeInt(mPhysicalSlotIndex);
        dest.writeBoolean(mIsRemovable);
        dest.writeBoolean(mIsMultipleEnabledProfilesSupported);
        dest.writeTypedList(mPortList, flags);
        dest.writeBoolean(mIccIdAccessRestricted);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Construct a UiccCardInfo.
     *
     * @param isEuicc is a flag to check is eUICC or not
     * @param cardId is unique ID used to identify a UiccCard.
     * @param eid is unique eUICC Identifier
     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
     * @param isRemovable is a flag to check is removable or embedded
     * @param isMultipleEnabledProfilesSupported is a flag to check is MEP enabled or not
     * @param portList has the information regarding port, ICCID and its active status
     *
     * @hide
     */
    public UiccCardInfo(boolean isEuicc, int cardId, String eid, int physicalSlotIndex,
            boolean isRemovable, boolean isMultipleEnabledProfilesSupported,
            @NonNull List<UiccPortInfo> portList) {
        this.mIsEuicc = isEuicc;
        this.mCardId = cardId;
        this.mEid = eid;
        this.mIccId = null;
        this.mPhysicalSlotIndex = physicalSlotIndex;
        this.mIsRemovable = isRemovable;
        this.mIsMultipleEnabledProfilesSupported = isMultipleEnabledProfilesSupported;
        this.mPortList = portList;
    }

    /**
     * Return whether the UICC is an eUICC.
     *
     * @return true if the UICC is an eUICC.
     */
    public boolean isEuicc() {
        return mIsEuicc;
    }

    /**
     * Get the card ID of the UICC. See {@link TelephonyManager#getCardIdForDefaultEuicc()} for more
     * details on card ID.
     */
    public int getCardId() {
        return mCardId;
    }

    /**
     * Get the embedded ID (EID) of the eUICC. If the UiccCardInfo is not an eUICC
     * (see {@link #isEuicc()}), or the EID is not available, returns null.
     * <p>
     * Note that this field may be omitted if the caller does not have the correct permissions
     * (see {@link TelephonyManager#getUiccCardsInfo()}).
     */
    @Nullable
    public String getEid() {
        if (!mIsEuicc) {
            return null;
        }
        return mEid;
    }

    /**
     * Get the ICCID of the UICC. If the ICCID is not availble, returns null.
     * <p>
     * Note that this field may be omitted if the caller does not have the correct permissions
     * (see {@link TelephonyManager#getUiccCardsInfo()}).
     *
     * @deprecated with support for MEP(multiple enabled profile)
     * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, a SIM card can have more than one
     * ICCID active at the same time. Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
     * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}.
     *
     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
     */
    @Nullable
    @Deprecated
    public String getIccId() {
        if (mIccIdAccessRestricted) {
            throw new UnsupportedOperationException("getIccId() is not supported by UiccCardInfo."
                + " Please Use UiccPortInfo API instead");
        }
        //always return ICCID from first port.
        return mPortList.isEmpty() ? null : mPortList.get(0).getIccId();
    }

    /**
     * Gets the slot index for the slot that the UICC is currently inserted in.
     *
     * @deprecated use {@link #getPhysicalSlotIndex()}
     */
    @Deprecated
    public int getSlotIndex() {
        return mPhysicalSlotIndex;
    }

    /**
     * Gets the physical slot index for the slot that the UICC is currently inserted in.
     */
    public int getPhysicalSlotIndex() {
        return mPhysicalSlotIndex;
    }

    /**
     * Return whether the UICC or eUICC is removable.
     * <p>
     * UICCs are generally removable, but eUICCs may be removable or built in to the device.
     *
     * @return true if the UICC or eUICC is removable
     */
    public boolean isRemovable() {
        return mIsRemovable;
    }

    /*
     * Whether the UICC card supports multiple enabled profile(MEP)
     * UICCs are generally MEP disabled, there can be only one active profile on the physical
     * sim card.
     *
     * @return {@code true} if the UICC is supporting multiple enabled profile(MEP).
     */
    public boolean isMultipleEnabledProfilesSupported() {
        return mIsMultipleEnabledProfilesSupported;
    }

    /**
     * Get information regarding port, ICCID and its active status.
     *
     * For device which support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, it should return
     * more than one {@link UiccPortInfo} object if the card is eUICC.
     *
     * @return Collection of {@link UiccPortInfo}
     */
    public @NonNull Collection<UiccPortInfo> getPorts() {
        return Collections.unmodifiableList(mPortList);
    }

    /**
     * if the flag is set to {@code true} the calling app is not allowed to access deprecated
     * {@link #getIccId()}
     * @param iccIdAccessRestricted is the flag to check if app is allowed to access ICCID
     *
     * @hide
     */
    public void setIccIdAccessRestricted(boolean iccIdAccessRestricted) {
        this.mIccIdAccessRestricted = iccIdAccessRestricted;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        UiccCardInfo that = (UiccCardInfo) obj;
        return ((mIsEuicc == that.mIsEuicc)
                && (mCardId == that.mCardId)
                && (Objects.equals(mEid, that.mEid))
                && (Objects.equals(mIccId, that.mIccId))
                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
                && (mIsRemovable == that.mIsRemovable)
                && (mIsMultipleEnabledProfilesSupported == that.mIsMultipleEnabledProfilesSupported)
                && (Objects.equals(mPortList, that.mPortList)));
    }

    @Override
    public int hashCode() {
        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mPhysicalSlotIndex, mIsRemovable,
                mIsMultipleEnabledProfilesSupported, mPortList);
    }

    @Override
    public String toString() {
        return "UiccCardInfo (mIsEuicc="
                + mIsEuicc
                + ", mCardId="
                + mCardId
                + ", mEid="
                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
                + ", mPhysicalSlotIndex="
                + mPhysicalSlotIndex
                + ", mIsRemovable="
                + mIsRemovable
                + ", mIsMultipleEnabledProfilesSupported="
                + mIsMultipleEnabledProfilesSupported
                + ", mPortList="
                + mPortList
                + ", mIccIdAccessRestricted="
                + mIccIdAccessRestricted
                + ")";
    }
}