package android.telephony;

import static android.telephony.ServiceState.DUPLEX_MODE_FDD;
import static android.telephony.ServiceState.DUPLEX_MODE_TDD;
import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN;

import android.telephony.AccessNetworkConstants.EutranBand;
import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency;
import android.telephony.AccessNetworkConstants.GeranBand;
import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency;
import android.telephony.AccessNetworkConstants.NgranArfcnFrequency;
import android.telephony.AccessNetworkConstants.NgranBands;
import android.telephony.AccessNetworkConstants.UtranBand;
import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency;
import android.telephony.ServiceState.DuplexMode;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Utilities to map between radio constants.
 *
 * @hide
 */
public class AccessNetworkUtils {

    // do not instantiate
    private AccessNetworkUtils() {}

    public static final int INVALID_BAND = -1;
    public static final int INVALID_FREQUENCY = -1;

    /** ISO country code of Japan. */
    private static final String JAPAN_ISO_COUNTRY_CODE = "jp";
    private static final String TAG = "AccessNetworkUtils";

    private static final int FREQUENCY_KHZ = 1000;
    private static final int FREQUENCY_RANGE_LOW_KHZ = 1000000;
    private static final int FREQUENCY_RANGE_MID_KHZ = 3000000;
    private static final int FREQUENCY_RANGE_HIGH_KHZ = 6000000;

    private static final Set<Integer> UARFCN_NOT_GENERAL_BAND;
    static {
        UARFCN_NOT_GENERAL_BAND = new HashSet<Integer>();
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_A);
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_B);
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_C);
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_D);
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_E);
        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_F);
    }

    /**
     * Gets the duplex mode for the given EUTRAN operating band.
     *
     * <p>See 3GPP 36.101 sec 5.5-1 for calculation
     *
     * @param band The EUTRAN band number
     * @return The duplex mode of the given EUTRAN band
     */
    @DuplexMode
    public static int getDuplexModeForEutranBand(int band) {
        if (band == INVALID_BAND) {
            return DUPLEX_MODE_UNKNOWN;
        }

        if (band > EutranBand.BAND_88) {
            return DUPLEX_MODE_UNKNOWN;
        } else if (band >= EutranBand.BAND_65) {
            return DUPLEX_MODE_FDD;
        } else if (band >= EutranBand.BAND_33) {
            return DUPLEX_MODE_TDD;
        } else if (band >= EutranBand.BAND_1) {
            return DUPLEX_MODE_FDD;
        }

        return DUPLEX_MODE_UNKNOWN;
    }

    /**
     * Gets the EUTRAN Operating band for a given downlink EARFCN.
     *
     * <p>See 3GPP TS 36.101 clause 5.7.3-1 for calculation.
     *
     * @param earfcn The downlink EARFCN
     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
     */
    public static int getOperatingBandForEarfcn(int earfcn) {
        if (earfcn > 70645) {
            return INVALID_BAND;
        } else if (earfcn >= 70596) {
            return EutranBand.BAND_88;
        } else if (earfcn >= 70546) {
            return EutranBand.BAND_87;
        } else if (earfcn >= 70366) {
            return EutranBand.BAND_85;
        } else if (earfcn > 69465) {
            return INVALID_BAND;
        } else if (earfcn >= 69036) {
            return EutranBand.BAND_74;
        } else if (earfcn >= 68986) {
            return EutranBand.BAND_73;
        } else if (earfcn >= 68936) {
            return EutranBand.BAND_72;
        } else if (earfcn >= 68586) {
            return EutranBand.BAND_71;
        } else if (earfcn >= 68336) {
            return EutranBand.BAND_70;
        } else if (earfcn > 67835) {
            return INVALID_BAND;
        } else if (earfcn >= 67536) {
            return EutranBand.BAND_68;
        } else if (earfcn >= 67366) {
            return INVALID_BAND; // band 67 only for CarrierAgg
        } else if (earfcn >= 66436) {
            return EutranBand.BAND_66;
        } else if (earfcn >= 65536) {
            return EutranBand.BAND_65;
        } else if (earfcn > 60254) {
            return INVALID_BAND;
        } else if (earfcn >= 60140) {
            return EutranBand.BAND_53;
        } else if (earfcn >= 59140) {
            return EutranBand.BAND_52;
        } else if (earfcn >= 59090) {
            return EutranBand.BAND_51;
        } else if (earfcn >= 58240) {
            return EutranBand.BAND_50;
        } else if (earfcn >= 56740) {
            return EutranBand.BAND_49;
        } else if (earfcn >= 55240) {
            return EutranBand.BAND_48;
        } else if (earfcn >= 54540) {
            return EutranBand.BAND_47;
        } else if (earfcn >= 46790) {
            return EutranBand.BAND_46;
        } else if (earfcn >= 46590) {
            return EutranBand.BAND_45;
        } else if (earfcn >= 45590) {
            return EutranBand.BAND_44;
        } else if (earfcn >= 43590) {
            return EutranBand.BAND_43;
        } else if (earfcn >= 41590) {
            return EutranBand.BAND_42;
        } else if (earfcn >= 39650) {
            return EutranBand.BAND_41;
        } else if (earfcn >= 38650) {
            return EutranBand.BAND_40;
        } else if (earfcn >= 38250) {
            return EutranBand.BAND_39;
        } else if (earfcn >= 37750) {
            return EutranBand.BAND_38;
        } else if (earfcn >= 37550) {
            return EutranBand.BAND_37;
        } else if (earfcn >= 36950) {
            return EutranBand.BAND_36;
        } else if (earfcn >= 36350) {
            return EutranBand.BAND_35;
        } else if (earfcn >= 36200) {
            return EutranBand.BAND_34;
        } else if (earfcn >= 36000) {
            return EutranBand.BAND_33;
        } else if (earfcn > 10359) {
            return INVALID_BAND;
        } else if (earfcn >= 9920) {
            return INVALID_BAND; // band 32 only for CarrierAgg
        } else if (earfcn >= 9870) {
            return EutranBand.BAND_31;
        } else if (earfcn >= 9770) {
            return EutranBand.BAND_30;
        } else if (earfcn >= 9660) {
            return INVALID_BAND; // band 29 only for CarrierAgg
        } else if (earfcn >= 9210) {
            return EutranBand.BAND_28;
        } else if (earfcn >= 9040) {
            return EutranBand.BAND_27;
        } else if (earfcn >= 8690) {
            return EutranBand.BAND_26;
        } else if (earfcn >= 8040) {
            return EutranBand.BAND_25;
        } else if (earfcn >= 7700) {
            return EutranBand.BAND_24;
        } else if (earfcn >= 7500) {
            return EutranBand.BAND_23;
        } else if (earfcn >= 6600) {
            return EutranBand.BAND_22;
        } else if (earfcn >= 6450) {
            return EutranBand.BAND_21;
        } else if (earfcn >= 6150) {
            return EutranBand.BAND_20;
        } else if (earfcn >= 6000) {
            return EutranBand.BAND_19;
        } else if (earfcn >= 5850) {
            return EutranBand.BAND_18;
        } else if (earfcn >= 5730) {
            return EutranBand.BAND_17;
        } else if (earfcn > 5379) {
            return INVALID_BAND;
        } else if (earfcn >= 5280) {
            return EutranBand.BAND_14;
        } else if (earfcn >= 5180) {
            return EutranBand.BAND_13;
        } else if (earfcn >= 5010) {
            return EutranBand.BAND_12;
        } else if (earfcn >= 4750) {
            return EutranBand.BAND_11;
        } else if (earfcn >= 4150) {
            return EutranBand.BAND_10;
        } else if (earfcn >= 3800) {
            return EutranBand.BAND_9;
        } else if (earfcn >= 3450) {
            return EutranBand.BAND_8;
        } else if (earfcn >= 2750) {
            return EutranBand.BAND_7;
        } else if (earfcn >= 2650) {
            return EutranBand.BAND_6;
        } else if (earfcn >= 2400) {
            return EutranBand.BAND_5;
        } else if (earfcn >= 1950) {
            return EutranBand.BAND_4;
        } else if (earfcn >= 1200) {
            return EutranBand.BAND_3;
        } else if (earfcn >= 600) {
            return EutranBand.BAND_2;
        } else if (earfcn >= 0) {
            return EutranBand.BAND_1;
        }

        return INVALID_BAND;
    }

    /**
     * Gets the NR Operating band for a given downlink NRARFCN.
     *
     * <p>See 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 and
     * Table 5.2-2 NR operating bands in FR2
     *
     * @param nrarfcn The downlink NRARFCN
     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
     */
    public static int getOperatingBandForNrarfcn(int nrarfcn) {
        if (nrarfcn >= 422000 && nrarfcn <= 434000) {
            return NgranBands.BAND_1;
        } else if (nrarfcn >= 386000 && nrarfcn <= 398000) {
            return NgranBands.BAND_2;
        } else if (nrarfcn >= 361000 && nrarfcn <= 376000) {
            return NgranBands.BAND_3;
        } else if (nrarfcn >= 173800 && nrarfcn <= 178800) {
            return NgranBands.BAND_5;
        } else if (nrarfcn >= 524000 && nrarfcn <= 538000) {
            return NgranBands.BAND_7;
        } else if (nrarfcn >= 185000 && nrarfcn <= 192000) {
            return NgranBands.BAND_8;
        } else if (nrarfcn >= 145800 && nrarfcn <= 149200) {
            return NgranBands.BAND_12;
        } else if (nrarfcn >= 151600 && nrarfcn <= 153600) {
            return NgranBands.BAND_14;
        } else if (nrarfcn >= 172000 && nrarfcn <= 175000) {
            return NgranBands.BAND_18;
        } else if (nrarfcn >= 158200 && nrarfcn <= 164200) {
            return NgranBands.BAND_20;
        } else if (nrarfcn >= 386000 && nrarfcn <= 399000) {
            return NgranBands.BAND_25;
        } else if (nrarfcn >= 171800 && nrarfcn <= 178800) {
            return NgranBands.BAND_26;
        } else if (nrarfcn >= 151600 && nrarfcn <= 160600) {
            return NgranBands.BAND_28;
        } else if (nrarfcn >= 143400 && nrarfcn <= 145600) {
            return NgranBands.BAND_29;
        } else if (nrarfcn >= 470000 && nrarfcn <= 472000) {
            return NgranBands.BAND_30;
        } else if (nrarfcn >= 402000 && nrarfcn <= 405000) {
            return NgranBands.BAND_34;
        } else if (nrarfcn >= 514000 && nrarfcn <= 524000) {
            return NgranBands.BAND_38;
        } else if (nrarfcn >= 376000 && nrarfcn <= 384000) {
            return NgranBands.BAND_39;
        } else if (nrarfcn >= 460000 && nrarfcn <= 480000) {
            return NgranBands.BAND_40;
        } else if (nrarfcn >= 499200 && nrarfcn <= 537999) {
            return NgranBands.BAND_41;
        } else if (nrarfcn >= 743334 && nrarfcn <= 795000) {
            return NgranBands.BAND_46;
        } else if (nrarfcn >= 636667 && nrarfcn <= 646666) {
            return NgranBands.BAND_48;
        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
            return NgranBands.BAND_50;
        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
            return NgranBands.BAND_51;
        } else if (nrarfcn >= 496700 && nrarfcn <= 499000) {
            return NgranBands.BAND_53;
        } else if (nrarfcn >= 422000 && nrarfcn <= 440000) {
            return NgranBands.BAND_65; // BAND_66 has the same channels
        } else if (nrarfcn >= 399000 && nrarfcn <= 404000) {
            return NgranBands.BAND_70;
        } else if (nrarfcn >= 123400 && nrarfcn <= 130400) {
            return NgranBands.BAND_71;
        } else if (nrarfcn >= 295000 && nrarfcn <= 303600) {
            return NgranBands.BAND_74;
        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
            return NgranBands.BAND_75;
        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
            return NgranBands.BAND_76;
        } else if (nrarfcn >= 620000 && nrarfcn <= 680000) {
            return NgranBands.BAND_77;
        } else if (nrarfcn >= 620000 && nrarfcn <= 653333) {
            return NgranBands.BAND_78;
        } else if (nrarfcn >= 693334 && nrarfcn <= 733333) {
            return NgranBands.BAND_79;
        } else if (nrarfcn >= 499200 && nrarfcn <= 538000) {
            return NgranBands.BAND_90;
        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
            return NgranBands.BAND_91;
        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
            return NgranBands.BAND_92;
        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
            return NgranBands.BAND_93;
        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
            return NgranBands.BAND_94;
        } else if (nrarfcn >= 795000 && nrarfcn <= 875000) {
            return NgranBands.BAND_96;
        } else if (nrarfcn >= 2054166 && nrarfcn <= 2104165) {
            return NgranBands.BAND_257;
        } else if (nrarfcn >= 2016667 && nrarfcn <= 2070832) {
            return NgranBands.BAND_258;
        } else if (nrarfcn >= 2229166 && nrarfcn <= 2279165) {
            return NgranBands.BAND_260;
        } else if (nrarfcn >= 2070833 && nrarfcn <= 2084999) {
            return NgranBands.BAND_261;
        }
        return INVALID_BAND;
    }

    /**
     * Gets the GERAN Operating band for a given ARFCN.
     *
     * <p>See 3GPP TS 45.005 clause 2 for calculation.
     *
     * @param arfcn The ARFCN
     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
     */
    public static int getOperatingBandForArfcn(int arfcn) {
        if (arfcn >= 0 && arfcn <= 124) {
            return GeranBand.BAND_E900;
        } else if (arfcn >= 128 && arfcn <= 251) {
            return GeranBand.BAND_850;
        } else if (arfcn >= 259 && arfcn <= 293) {
            return GeranBand.BAND_450;
        } else if (arfcn >= 306 && arfcn <= 340) {
            return GeranBand.BAND_480;
        } else if (arfcn >= 438 && arfcn <= 511) {
            return GeranBand.BAND_750;
        } else if (arfcn >= 512 && arfcn <= 885) {
            // ARFCN between 512 and 810 are also part of BAND_PCS1900.
            // Returning BAND_DCS1800 in both cases.
            return GeranBand.BAND_DCS1800;
        } else if (arfcn >= 940 && arfcn <= 974) {
            return GeranBand.BAND_ER900;
        } else if (arfcn >= 975 && arfcn <= 1023) {
            return GeranBand.BAND_E900;
        }
        return INVALID_BAND;
    }

    /**
     * Gets the UTRAN Operating band for a given downlink UARFCN.
     *
     * <p>See 3GPP TS 25.101 clause 5.4.4 for calculation.
     *
     * @param uarfcn The downlink UARFCN
     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
     */
    public static int getOperatingBandForUarfcn(int uarfcn) {
        // List of additional bands defined in TS 25.101.
        int[] addlBand2 = {412, 437, 462, 487, 512, 537, 562, 587, 612, 637, 662, 687};
        int[] addlBand4 = {1887, 1912, 1937, 1962, 1987, 2012, 2037, 2062, 2087};
        int[] addlBand5 = {1007, 1012, 1032, 1037, 1062, 1087};
        int[] addlBand6 = {1037, 1062};
        int[] addlBand7 =
                {2587, 2612, 2637, 2662, 2687, 2712, 2737, 2762, 2787, 2812, 2837, 2862,
                2887, 2912};
        int[] addlBand10 =
                {3412, 3437, 3462, 3487, 3512, 3537, 3562, 3587, 3612, 3637, 3662, 3687};
        int[] addlBand12 = {3932, 3957, 3962, 3987, 3992};
        int[] addlBand13 = {4067, 4092};
        int[] addlBand14 = {4167, 4192};
        int[] addlBand19 = {787, 812, 837};
        int[] addlBand25 =
                {6292, 6317, 6342, 6367, 6392, 6417, 6442, 6467, 6492, 6517, 6542, 6567, 6592};
        int[] addlBand26 = {5937, 5962, 5987, 5992, 6012, 6017, 6037, 6042, 6062, 6067, 6087};

        if (uarfcn >= 10562 && uarfcn <= 10838) {
            return UtranBand.BAND_1;
        } else if ((uarfcn >= 9662 && uarfcn <= 9938)
                || Arrays.binarySearch(addlBand2, uarfcn) >= 0) {
            return UtranBand.BAND_2;
        } else if (uarfcn >= 1162 && uarfcn <= 1513) {
            return UtranBand.BAND_3;
        } else if ((uarfcn >= 1537 && uarfcn <= 1738)
                || Arrays.binarySearch(addlBand4, uarfcn) >= 0) {
            return UtranBand.BAND_4;
        } else if (uarfcn >= 4387 && uarfcn <= 4413) {
            // Band 6 is a subset of band 5. Only Japan uses band 6 and Japan does not have band 5.
            String country = TelephonyManager.getDefault().getNetworkCountryIso();
            if (JAPAN_ISO_COUNTRY_CODE.compareToIgnoreCase(country) == 0) {
                return UtranBand.BAND_6;
            } else {
                return UtranBand.BAND_5;
            }
        } else if ((uarfcn >= 4357 && uarfcn <= 4458)
                || Arrays.binarySearch(addlBand5, uarfcn) >= 0) {
            return UtranBand.BAND_5;
        } else if (Arrays.binarySearch(addlBand6, uarfcn) >= 0) {
            return UtranBand.BAND_6;
        } else if ((uarfcn >= 2237 && uarfcn <= 2563)
                || Arrays.binarySearch(addlBand7, uarfcn) >= 0) {
            return UtranBand.BAND_7;
        } else if (uarfcn >= 2937 && uarfcn <= 3088) {
            return UtranBand.BAND_8;
        } else if (uarfcn >= 9237 && uarfcn <= 9387) {
            return UtranBand.BAND_9;
        } else if ((uarfcn >= 3112 && uarfcn <= 3388)
                || Arrays.binarySearch(addlBand10, uarfcn) >= 0) {
            return UtranBand.BAND_10;
        } else if (uarfcn >= 3712 && uarfcn <= 3787) {
            return UtranBand.BAND_11;
        } else if ((uarfcn >= 3842 && uarfcn <= 3903)
                || Arrays.binarySearch(addlBand12, uarfcn) >= 0) {
            return UtranBand.BAND_12;
        } else if ((uarfcn >= 4017 && uarfcn <= 4043)
                || Arrays.binarySearch(addlBand13, uarfcn) >= 0) {
            return UtranBand.BAND_13;
        } else if ((uarfcn >= 4117 && uarfcn <= 4143)
                || Arrays.binarySearch(addlBand14, uarfcn) >= 0) {
            return UtranBand.BAND_14;
        } else if ((uarfcn >= 712 && uarfcn <= 763)
                || Arrays.binarySearch(addlBand19, uarfcn) >= 0) {
            return UtranBand.BAND_19;
        } else if (uarfcn >= 4512 && uarfcn <= 4638) {
            return UtranBand.BAND_20;
        } else if (uarfcn >= 862 && uarfcn <= 912) {
            return UtranBand.BAND_21;
        } else if (uarfcn >= 4662 && uarfcn <= 5038) {
            return UtranBand.BAND_22;
        } else if ((uarfcn >= 5112 && uarfcn <= 5413)
                || Arrays.binarySearch(addlBand25, uarfcn) >= 0) {
            return UtranBand.BAND_25;
        } else if ((uarfcn >= 5762 && uarfcn <= 5913)
                || Arrays.binarySearch(addlBand26, uarfcn) >= 0) {
            return UtranBand.BAND_26;
        }
        return INVALID_BAND;
    }

    /**
     * Get geran bands from {@link PhysicalChannelConfig#getBand()}
     */
    public static int getFrequencyRangeGroupFromGeranBand(@GeranBand.GeranBands int band) {
        switch (band) {
            case GeranBand.BAND_T380:
            case GeranBand.BAND_T410:
            case GeranBand.BAND_450:
            case GeranBand.BAND_480:
            case GeranBand.BAND_710:
            case GeranBand.BAND_750:
            case GeranBand.BAND_T810:
            case GeranBand.BAND_850:
            case GeranBand.BAND_P900:
            case GeranBand.BAND_E900:
            case GeranBand.BAND_R900:
            case GeranBand.BAND_ER900:
                return ServiceState.FREQUENCY_RANGE_LOW;
            case GeranBand.BAND_DCS1800:
            case GeranBand.BAND_PCS1900:
                return ServiceState.FREQUENCY_RANGE_MID;
            default:
                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
        }
    }

    /**
     * Get utran bands from {@link PhysicalChannelConfig#getBand()}
     */
    public static int getFrequencyRangeGroupFromUtranBand(@UtranBand.UtranBands int band) {
        switch (band) {
            case UtranBand.BAND_5:
            case UtranBand.BAND_6:
            case UtranBand.BAND_8:
            case UtranBand.BAND_12:
            case UtranBand.BAND_13:
            case UtranBand.BAND_14:
            case UtranBand.BAND_19:
            case UtranBand.BAND_20:
            case UtranBand.BAND_26:
                return ServiceState.FREQUENCY_RANGE_LOW;
            case UtranBand.BAND_1:
            case UtranBand.BAND_2:
            case UtranBand.BAND_3:
            case UtranBand.BAND_4:
            case UtranBand.BAND_7:
            case UtranBand.BAND_9:
            case UtranBand.BAND_10:
            case UtranBand.BAND_11:
            case UtranBand.BAND_21:
            case UtranBand.BAND_25:
            case UtranBand.BAND_A:
            case UtranBand.BAND_B:
            case UtranBand.BAND_C:
            case UtranBand.BAND_D:
            case UtranBand.BAND_E:
            case UtranBand.BAND_F:
                return ServiceState.FREQUENCY_RANGE_MID;
            case UtranBand.BAND_22:
                return ServiceState.FREQUENCY_RANGE_HIGH;
            default:
                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
        }
    }

    /**
     * Get eutran bands from {@link PhysicalChannelConfig#getBand()}
     * 3GPP TS 36.101 Table 5.5 EUTRA operating bands
     */
    public static int getFrequencyRangeGroupFromEutranBand(@EutranBand.EutranBands int band) {
        switch (band) {
            case EutranBand.BAND_5:
            case EutranBand.BAND_6:
            case EutranBand.BAND_8:
            case EutranBand.BAND_12:
            case EutranBand.BAND_13:
            case EutranBand.BAND_14:
            case EutranBand.BAND_17:
            case EutranBand.BAND_18:
            case EutranBand.BAND_19:
            case EutranBand.BAND_20:
            case EutranBand.BAND_26:
            case EutranBand.BAND_27:
            case EutranBand.BAND_28:
            case EutranBand.BAND_31:
            case EutranBand.BAND_44:
            case EutranBand.BAND_50:
            case EutranBand.BAND_51:
            case EutranBand.BAND_68:
            case EutranBand.BAND_71:
            case EutranBand.BAND_72:
            case EutranBand.BAND_73:
            case EutranBand.BAND_85:
            case EutranBand.BAND_87:
            case EutranBand.BAND_88:
                return ServiceState.FREQUENCY_RANGE_LOW;
            case EutranBand.BAND_1:
            case EutranBand.BAND_2:
            case EutranBand.BAND_3:
            case EutranBand.BAND_4:
            case EutranBand.BAND_7:
            case EutranBand.BAND_9:
            case EutranBand.BAND_10:
            case EutranBand.BAND_11:
            case EutranBand.BAND_21:
            case EutranBand.BAND_23:
            case EutranBand.BAND_24:
            case EutranBand.BAND_25:
            case EutranBand.BAND_30:
            case EutranBand.BAND_33:
            case EutranBand.BAND_34:
            case EutranBand.BAND_35:
            case EutranBand.BAND_36:
            case EutranBand.BAND_37:
            case EutranBand.BAND_38:
            case EutranBand.BAND_39:
            case EutranBand.BAND_40:
            case EutranBand.BAND_41:
            case EutranBand.BAND_45:
            case EutranBand.BAND_53:
            case EutranBand.BAND_65:
            case EutranBand.BAND_66:
            case EutranBand.BAND_70:
            case EutranBand.BAND_74:
                return ServiceState.FREQUENCY_RANGE_MID;
            case EutranBand.BAND_22:
            case EutranBand.BAND_42:
            case EutranBand.BAND_43:
            case EutranBand.BAND_46:
            case EutranBand.BAND_47:
            case EutranBand.BAND_48:
            case EutranBand.BAND_49:
            case EutranBand.BAND_52:
                return ServiceState.FREQUENCY_RANGE_HIGH;
            default:
                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
        }
    }

    /**
     * Get ngran band from {@link PhysicalChannelConfig#getBand()}
     * 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1
     * 3GPP TS 38.104 Table 5.2-2 NR operating bands in FR2
     */
    public static int getFrequencyRangeGroupFromNrBand(@NgranBands.NgranBand int band) {
        switch (band) {
            case NgranBands.BAND_5:
            case NgranBands.BAND_8:
            case NgranBands.BAND_12:
            case NgranBands.BAND_14:
            case NgranBands.BAND_18:
            case NgranBands.BAND_20:
            case NgranBands.BAND_26:
            case NgranBands.BAND_28:
            case NgranBands.BAND_29:
            case NgranBands.BAND_71:
            case NgranBands.BAND_81:
            case NgranBands.BAND_82:
            case NgranBands.BAND_83:
            case NgranBands.BAND_89:
                return ServiceState.FREQUENCY_RANGE_LOW;
            case NgranBands.BAND_1:
            case NgranBands.BAND_2:
            case NgranBands.BAND_3:
            case NgranBands.BAND_7:
            case NgranBands.BAND_25:
            case NgranBands.BAND_30:
            case NgranBands.BAND_34:
            case NgranBands.BAND_38:
            case NgranBands.BAND_39:
            case NgranBands.BAND_40:
            case NgranBands.BAND_41:
            case NgranBands.BAND_50:
            case NgranBands.BAND_51:
            case NgranBands.BAND_53:
            case NgranBands.BAND_65:
            case NgranBands.BAND_66:
            case NgranBands.BAND_70:
            case NgranBands.BAND_74:
            case NgranBands.BAND_75:
            case NgranBands.BAND_76:
            case NgranBands.BAND_80:
            case NgranBands.BAND_84:
            case NgranBands.BAND_86:
            case NgranBands.BAND_90:
            case NgranBands.BAND_91:
            case NgranBands.BAND_92:
            case NgranBands.BAND_93:
            case NgranBands.BAND_94:
            case NgranBands.BAND_95:
                return ServiceState.FREQUENCY_RANGE_MID;
            case NgranBands.BAND_46:
            case NgranBands.BAND_48:
            case NgranBands.BAND_77:
            case NgranBands.BAND_78:
            case NgranBands.BAND_79:
                return ServiceState.FREQUENCY_RANGE_HIGH;
            case NgranBands.BAND_96:
            case NgranBands.BAND_257:
            case NgranBands.BAND_258:
            case NgranBands.BAND_260:
            case NgranBands.BAND_261:
                return ServiceState.FREQUENCY_RANGE_MMWAVE;
            default:
                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
        }
    }

    /**
     * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster.
     * Formula of NR-ARFCN convert to actual frequency:
     * Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET))
     */
    public static int getFrequencyFromNrArfcn(int nrArfcn) {

        if (nrArfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
        }

        int globalKhz = 0;
        int rangeOffset = 0;
        int arfcnOffset = 0;
        for (NgranArfcnFrequency nrArfcnFrequency : AccessNetworkConstants.
                NgranArfcnFrequency.values()) {
            if (nrArfcn >= nrArfcnFrequency.rangeFirst
                    && nrArfcn <= nrArfcnFrequency.rangeLast) {
                globalKhz = nrArfcnFrequency.globalKhz;
                rangeOffset = nrArfcnFrequency.rangeOffset;
                arfcnOffset = nrArfcnFrequency.arfcnOffset;
                break;
            }
        }
        return rangeOffset + globalKhz * (nrArfcn - arfcnOffset);
    }

    /**
     * Get actual frequency from E-UTRA ARFCN.
     */
    public static int getFrequencyFromEarfcn(int band, int earfcn, boolean isUplink) {

        int low = 0;
        int offset = 0;
        for (EutranBandArfcnFrequency earfcnFrequency : EutranBandArfcnFrequency.values()) {
            if (band == earfcnFrequency.band) {
                if (isInEarfcnRange(earfcn, earfcnFrequency, isUplink)) {
                    low = isUplink ? earfcnFrequency.uplinkLowKhz : earfcnFrequency.downlinkLowKhz;
                    offset = isUplink ? earfcnFrequency.uplinkOffset
                            : earfcnFrequency.downlinkOffset;
                    break;
                } else {
                    Rlog.w(TAG,"Band and the range of EARFCN are not consistent: band = " + band
                            + " ,earfcn = " + earfcn + " ,isUplink = " + isUplink);
                    return INVALID_FREQUENCY;
                }
            }
        }
        return convertEarfcnToFrequency(low, earfcn, offset);
    }

    /**
     * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers.
     * Formula of E-UTRA ARFCN convert to actual frequency:
     * Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ
     * Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ
     */
    private static int convertEarfcnToFrequency(int low, int earfcn, int offset) {
        return low + 100 * (earfcn - offset);
    }

    private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency,
            boolean isUplink) {
        if (isUplink) {
            return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange;
        } else {
            return earfcn >= earfcnFrequency.downlinkOffset
                    && earfcn <= earfcnFrequency.downlinkRange;
        }
    }

    /**
     * Get actual frequency from UTRA ARFCN.
     */
    public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) {

        if (uarfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
        }

        int offsetKhz = 0;
        for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants.
                UtranBandArfcnFrequency.values()) {
            if (band == uarfcnFrequency.band) {
                if (isInUarfcnRange(uarfcn, uarfcnFrequency, isUplink)) {
                    offsetKhz = isUplink ? uarfcnFrequency.uplinkOffset
                            : uarfcnFrequency.downlinkOffset;
                    break;
                } else {
                    Rlog.w(TAG,"Band and the range of UARFCN are not consistent: band = " + band
                            + " ,uarfcn = " + uarfcn + " ,isUplink = " + isUplink);
                    return INVALID_FREQUENCY;
                }
            }
        }

        if (!UARFCN_NOT_GENERAL_BAND.contains(band)) {
            return convertUarfcnToFrequency(offsetKhz, uarfcn);
        } else {
            return convertUarfcnTddToFrequency(band, uarfcn);
        }
    }

    /**
     * 3GPP TS 25.101, Table 5.1 UARFCN definition (general).
     * Formula of UTRA ARFCN convert to actual frequency:
     * For general bands:
     * Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
     * Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
     */
    private static int convertUarfcnToFrequency(int offsetKhz, int uarfcn) {
        return offsetKhz + (200 * uarfcn);
    }

    /**
     * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option.
     * For FDD bands A, B, C, E, F:
     * Actual frequency(kHz) =  5 * ARFCN * FREQUENCY_KHZ
     * For TDD bands D:
     * Actual frequency(kHz) =  (5 * (ARFCN - 2150.1MHz)) * FREQUENCY_KHZ
     */
    private static int convertUarfcnTddToFrequency(int band, int uarfcn) {
        if (band != UtranBand.BAND_D) {
            return 5 * uarfcn * FREQUENCY_KHZ;
        } else {
            return 5 * ((FREQUENCY_KHZ * uarfcn) - 2150100);
        }
    }

    private static boolean isInUarfcnRange(int uarfcn, UtranBandArfcnFrequency uarfcnFrequency,
                                           boolean isUplink) {
        if (isUplink) {
            return uarfcn >= uarfcnFrequency.uplinkRangeFirst
                    && uarfcn <= uarfcnFrequency.uplinkRangeLast;
        } else {
            if (uarfcnFrequency.downlinkRangeFirst != 0 && uarfcnFrequency.downlinkRangeLast != 0) {
                return uarfcn >= uarfcnFrequency.downlinkRangeFirst
                        && uarfcn <= uarfcnFrequency.downlinkRangeLast;
            } else {
                // BAND_C, BAND_D, BAND_E and BAND_F do not have the downlink range.
                return true;
            }
        }
    }

    /**
     * Get actual frequency from GERAN ARFCN.
     */
    public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) {

        if (arfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
        }

        int uplinkFrequencyFirst = 0;
        int arfcnOffset = 0;
        int downlinkOffset = 0;
        int frequency = 0;
        for (GeranBandArfcnFrequency arfcnFrequency : AccessNetworkConstants.
                GeranBandArfcnFrequency.values()) {
            if (band == arfcnFrequency.band) {
                if (arfcn >= arfcnFrequency.arfcnRangeFirst
                        && arfcn <= arfcnFrequency.arfcnRangeLast) {
                    uplinkFrequencyFirst = arfcnFrequency.uplinkFrequencyFirst;
                    downlinkOffset = arfcnFrequency.downlinkOffset;
                    arfcnOffset = arfcnFrequency.arfcnOffset;
                    frequency = convertArfcnToFrequency(arfcn, uplinkFrequencyFirst,
                            arfcnOffset);
                    break;
                } else {
                    Rlog.w(TAG,"Band and the range of ARFCN are not consistent: band = " + band
                            + " ,arfcn = " + arfcn + " ,isUplink = " + isUplink);
                    return INVALID_FREQUENCY;
                }
            }
        }

        return isUplink ? frequency : frequency + downlinkOffset;
    }

    /**
     * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN
     * Formula of Geran ARFCN convert to actual frequency:
     * Uplink actual frequency(kHz) =
     *      (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ
     * Downlink actual frequency(kHz) = Uplink actual frequency + 10
     */
    private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz,
            int arfcnOffset) {
        return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset);
    }

    public static int getFrequencyRangeFromArfcn(int frequency) {
        if (frequency < FREQUENCY_RANGE_LOW_KHZ) {
            return ServiceState.FREQUENCY_RANGE_LOW;
        } else if (frequency < FREQUENCY_RANGE_MID_KHZ
                && frequency >= FREQUENCY_RANGE_LOW_KHZ) {
            return ServiceState.FREQUENCY_RANGE_MID;
        } else if (frequency < FREQUENCY_RANGE_HIGH_KHZ
                && frequency >= FREQUENCY_RANGE_MID_KHZ) {
            return ServiceState.FREQUENCY_RANGE_HIGH;
        } else {
            return ServiceState.FREQUENCY_RANGE_MMWAVE;
        }
    }
}
