/*
 * Copyright (c) 2015, Motorola Mobility LLC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     - Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     - Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     - Neither the name of Motorola Mobility nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

package com.android.service.ims;

import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;

import com.android.ims.internal.Logger;
import com.android.internal.telephony.flags.Flags;

import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class RcsSettingUtils {
    static private Logger logger = Logger.getLogger("RcsSettingUtils");
    private static final int TIMEOUT_GET_CONFIGURATION_MS = 5000;

    // Default number of entries for getMaxNumbersInRCL
    private static final int DEFAULT_NUM_ENTRIES_IN_RCL = 100;
    // Default for getCapabPollListSubExp in seconds.
    private static final int DEFAULT_CAPABILITY_POLL_LIST_SUB_EXPIRATION_SEC = 30;
    // Default for getAvailabilityCacheExpiration in seconds.
    private static final int DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC = 30;
    // Default for getPublishThrottle in milliseconds
    private static final int DEFAULT_PUBLISH_THROTTLE_MS = 60000;

    public static boolean isVoLteProvisioned(int subId) {
        try {
            boolean isProvisioned;
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            isProvisioned = manager.getProvisioningStatusForCapability(
                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
            logger.debug("isVoLteProvisioned=" + isProvisioned);
            return isProvisioned;
        } catch (Exception e) {
            logger.debug("isVoLteProvisioned, exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isVowifiProvisioned(int subId) {
        try {
            boolean isProvisioned;
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            isProvisioned = manager.getProvisioningStatusForCapability(
                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                    ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
            logger.debug("isVowifiProvisioned=" + isProvisioned);
            return isProvisioned;
        } catch (Exception e) {
            logger.debug("isVowifiProvisioned, exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isLvcProvisioned(int subId) {
        try {
            boolean isProvisioned;
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            isProvisioned = manager.getProvisioningStatusForCapability(
                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
            logger.debug("isLvcProvisioned=" + isProvisioned);
            return isProvisioned;
        } catch (Exception e) {
            logger.debug("isLvcProvisioned, exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isEabProvisioned(Context context, int subId) {
        boolean isProvisioned = false;
        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            logger.debug("isEabProvisioned: no valid subscriptions!");
            return false;
        }
        CarrierConfigManager configManager = (CarrierConfigManager)
                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        if (configManager != null) {
            PersistableBundle config = configManager.getConfigForSubId(subId);
            if (config != null && !config.getBoolean(
                    CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) {
                // If we don't need provisioning, just return true.
                return true;
            }
        }
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            isProvisioned = manager.getRcsProvisioningStatusForCapability(
                    ImsRcsManager.CAPABILITY_TYPE_PRESENCE_UCE,
                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
        } catch (Exception e) {
            logger.debug("isEabProvisioned: exception=" + e.getMessage());
        }
        logger.debug("isEabProvisioned=" + isProvisioned);
        return isProvisioned;
    }

    public static boolean isPublishEnabled(Context context, int subId) {
        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            logger.debug("isPublishEnabled: no valid subscriptions!");
            return false;
        }
        CarrierConfigManager configManager = (CarrierConfigManager)
                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        if (configManager != null) {
            PersistableBundle config = configManager.getConfigForSubId(subId);
            return (config != null) && config.getBoolean(
                    CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
        }
        return false;
    }

    public static boolean hasUserEnabledContactDiscovery(Context context, int subId) {
        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            logger.debug("hasUserEnabledContactDiscovery: no valid subscriptions!");
            return false;
        }
        try {
            ImsManager imsManager = context.getSystemService(ImsManager.class);
            ImsRcsManager rcsManager = imsManager.getImsRcsManager(subId);
            return rcsManager.getUceAdapter().isUceSettingEnabled();
        } catch (Exception e) {
            logger.warn("hasUserEnabledContactDiscovery: Exception = " + e.getMessage());
            return false;
        }
    }

    public static int getSIPT1Timer(int subId) {
        int sipT1Timer = 0;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            sipT1Timer = manager.getProvisioningIntValue(ProvisioningManager.KEY_T1_TIMER_VALUE_MS);
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("getSIPT1Timer: exception=" + e.getMessage());
        }
        logger.debug("getSIPT1Timer=" + sipT1Timer);
        return sipT1Timer;
    }

    /**
     * Capability discovery status of Enabled (1), or Disabled (0).
     */
    public static boolean getCapabilityDiscoveryEnabled(int subId) {
        boolean capabilityDiscoveryEnabled = false;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            capabilityDiscoveryEnabled = manager.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED) ==
                    ProvisioningManager.PROVISIONING_VALUE_ENABLED;
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("capabilityDiscoveryEnabled: exception=" + e.getMessage());
        }
        logger.debug("capabilityDiscoveryEnabled=" + capabilityDiscoveryEnabled);
        return capabilityDiscoveryEnabled;
    }

    /**
     * The Maximum number of MDNs contained in one Request Contained List.
     */
    public static int getMaxNumbersInRCL(int subId) {
        int maxNumbersInRCL = DEFAULT_NUM_ENTRIES_IN_RCL;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            maxNumbersInRCL = manager.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL);
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("getMaxNumbersInRCL: exception=" + e.getMessage());
        }
        logger.debug("getMaxNumbersInRCL=" + maxNumbersInRCL);
        return maxNumbersInRCL;
    }

    /**
     * Expiration timer for subscription of a Request Contained List, used in capability polling.
     */
    public static int getCapabPollListSubExp(int subId) {
        int capabPollListSubExp = DEFAULT_CAPABILITY_POLL_LIST_SUB_EXPIRATION_SEC;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            capabPollListSubExp = manager.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC);
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("getCapabPollListSubExp: exception=" + e.getMessage());
        }
        logger.debug("getCapabPollListSubExp=" + capabPollListSubExp);
        return capabPollListSubExp;
    }

    /**
     * Period of time the availability information of a contact is cached on device.
     */
    public static int getAvailabilityCacheExpiration(int subId) {
        int availabilityCacheExpiration = DEFAULT_AVAILABILITY_CACHE_EXPIRATION_SEC;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            availabilityCacheExpiration = manager.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC);
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("getAvailabilityCacheExpiration: exception=" + e.getMessage());
        }
        logger.debug("getAvailabilityCacheExpiration=" + availabilityCacheExpiration);
        return availabilityCacheExpiration;
    }

    public static int getPublishThrottle(int subId) {
        // Default
        int publishThrottle = DEFAULT_PUBLISH_THROTTLE_MS;
        try {
            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
            publishThrottle = manager.getProvisioningIntValue(
                    ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS);
        } catch (Exception e) {
            // If there is no active subscriptions, this will throw an exception.
            logger.debug("publishThrottle: exception=" + e.getMessage());
        }
        logger.debug("publishThrottle=" + publishThrottle);
        return publishThrottle;
    }

    public static boolean isVtEnabledByUser(int subId) {
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
            return mmTelManager.isVtSettingEnabled();
        } catch (Exception e) {
            logger.warn("isVtEnabledByUser exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isWfcEnabledByUser(int subId) {
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
        return mmTelManager.isVoWiFiSettingEnabled();
        } catch (Exception e) {
            logger.warn("isWfcEnabledByUser exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isAdvancedCallingEnabledByUser(int subId) {
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
            return mmTelManager.isAdvancedCallingSettingEnabled();
        } catch (Exception e) {
            logger.warn("isAdvancedCallingEnabledByUser exception = " + e.getMessage());
            return false;
        }
    }

    public static boolean isVoLteSupported(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            return false;
        }
        LinkedBlockingQueue<Boolean> resultQueue = new LinkedBlockingQueue<>(1);
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
            mmTelManager.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN, Runnable::run, resultQueue::offer);
        } catch (ImsException e) {
            logger.warn("isVoLteSupported: ImsException = " + e.getMessage());
            return false;
        }
        try {
            Boolean result = resultQueue.poll(TIMEOUT_GET_CONFIGURATION_MS, TimeUnit.MILLISECONDS);
            return (result != null) ? result : false;
        } catch (InterruptedException e) {
            logger.warn("isVoLteSupported, InterruptedException=" + e.getMessage());
            return false;
        }
    }

    public static boolean isVoWiFiSupported(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            return false;
        }
        LinkedBlockingQueue<Boolean> resultQueue = new LinkedBlockingQueue<>(1);
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
            mmTelManager.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                    AccessNetworkConstants.TRANSPORT_TYPE_WLAN, Runnable::run, resultQueue::offer);
        } catch (ImsException e) {
            logger.warn("isVoWiFiSupported: ImsException = " + e.getMessage());
            return false;
        }
        try {
            Boolean result = resultQueue.poll(TIMEOUT_GET_CONFIGURATION_MS, TimeUnit.MILLISECONDS);
            return (result != null) ? result : false;
        } catch (InterruptedException e) {
            logger.warn("isVoWiFiSupported, InterruptedException=" + e.getMessage());
            return false;
        }
    }

    public static boolean isVtSupported(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            return false;
        }
        LinkedBlockingQueue<Boolean> resultQueue = new LinkedBlockingQueue<>(1);
        try {
            ImsMmTelManager mmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
            mmTelManager.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN, Runnable::run, resultQueue::offer);
        } catch (ImsException e) {
            logger.warn("isVoWiFiSupported: ImsException = " + e.getMessage());
            return false;
        }
        try {
            Boolean result = resultQueue.poll(TIMEOUT_GET_CONFIGURATION_MS, TimeUnit.MILLISECONDS);
            return (result != null) ? result : false;
        } catch (InterruptedException e) {
            logger.warn("isVtSupported, InterruptedException=" + e.getMessage());
            return false;
        }
    }

    public static int getDefaultSubscriptionId(Context context) {
        SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
        if (sm == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        sm = sm.createForAllUserProfiles();

        List<SubscriptionInfo> infos = sm.getActiveSubscriptionInfoList();
        if (infos == null || infos.isEmpty()) {
            // There are no active subscriptions right now.
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        }
        // This code does not support MSIM unfortunately, so only provide presence on the default
        // voice subscription that the user chose.
        int defaultSub = SubscriptionManager.getDefaultVoiceSubscriptionId();
        if (!SubscriptionManager.isValidSubscriptionId(defaultSub)) {
            // The voice sub may not have been specified, in this case, use the default data.
            defaultSub = SubscriptionManager.getDefaultDataSubscriptionId();
        }
        // If the user has no default set, just pick the first as backup.
        if (!SubscriptionManager.isValidSubscriptionId(defaultSub)) {
            for (SubscriptionInfo info : infos) {
                if (!info.isOpportunistic()) {
                    defaultSub = info.getSubscriptionId();
                    break;
                }
            }
        }
        return defaultSub;
    }
}

