/*
 * 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.wifi;

import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
import static android.net.wifi.SoftApConfiguration.BAND_5GHZ;

import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.net.wifi.SoftApConfiguration;
import android.util.SparseIntArray;

import androidx.preference.ListPreference;

import com.android.car.settings.Flags;
import com.android.car.settings.R;
import com.android.car.settings.common.FragmentController;
import com.android.internal.annotations.VisibleForTesting;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Controls WiFi Hotspot AP Band configuration.
 */
public class WifiTetherApBandPreferenceController extends
        WifiTetherBasePreferenceController<ListPreference> {
    private static final String TAG = "CarWifiTetherApBandPref";

    /** Wi-Fi hotspot band 2.4GHz and 5GHz. */
    @VisibleForTesting
    static final int BAND_2GHZ_5GHZ = BAND_2GHZ | BAND_5GHZ;

    /** Wi-Fi hotspot dual band for 2.4GHz and 5GHz. */
    @VisibleForTesting
    static final int[] DUAL_BANDS = new int[] {
            BAND_2GHZ,
            BAND_2GHZ_5GHZ };

    private final Map<Integer, String> mHotspotBandMap = new LinkedHashMap<>();

    private int mBand;

    public WifiTetherApBandPreferenceController(Context context, String preferenceKey,
            FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
        this(context, preferenceKey, fragmentController, uxRestrictions,
                new CarWifiManager(context, fragmentController.getSettingsLifecycle()));
    }

    @VisibleForTesting
    WifiTetherApBandPreferenceController(Context context, String preferenceKey,
            FragmentController fragmentController, CarUxRestrictions uxRestrictions,
            CarWifiManager carWifiManager) {
        super(context, preferenceKey, fragmentController, uxRestrictions, carWifiManager);

        String[] bandNames = getContext().getResources().getStringArray(
                R.array.wifi_ap_band_summary);
        String[] bandValues = getContext().getResources().getStringArray(
                R.array.wifi_ap_band);
        for (int i = 0; i < bandNames.length; i++) {
            mHotspotBandMap.put(Integer.parseInt(bandValues[i]), bandNames[i]);
        }
    }

    @Override
    protected Class<ListPreference> getPreferenceType() {
        return ListPreference.class;
    }

    @Override
    protected void onCreateInternal() {
        super.onCreateInternal();
        updatePreferenceEntries();
    }

    @Override
    public void updateState(ListPreference preference) {
        super.updateState(preference);

        SoftApConfiguration config = getCarSoftApConfig();
        int configBand = getBandFromConfig();
        if (config == null) {
            mBand = BAND_2GHZ;
        } else if (!is5GhzBandSupported() && configBand > BAND_2GHZ) {
            SoftApConfiguration newConfig = new SoftApConfiguration.Builder(config)
                    .setBand(BAND_2GHZ)
                    .build();
            setCarSoftApConfig(newConfig);
            mBand = BAND_2GHZ;
        } else {
            mBand = validateSelection(configBand);
        }

        if (!is5GhzBandSupported()) {
            preference.setEnabled(false);
            preference.setSummary(R.string.wifi_ap_choose_2G);
        } else {
            preference.setValue(Integer.toString(mBand));
            preference.setSummary(getSummary());
        }
    }

    @Override
    protected String getSummary() {
        return mHotspotBandMap.getOrDefault(mBand,
                getContext().getString(R.string.wifi_ap_prefer_5G));
    }

    @Override
    protected String getDefaultSummary() {
        return null;
    }

    @Override
    public boolean handlePreferenceChanged(ListPreference preference, Object newValue) {
        mBand = validateSelection(Integer.parseInt((String) newValue));
        updateApBand(); // updating AP band because mBandIndex may have been assigned a new value.
        refreshUi();
        return true;
    }

    private void updatePreferenceEntries() {
        // If 5 GHz is not supported, default to 2 GHz
        if (!is5GhzBandSupported()) {
            mHotspotBandMap.keySet().removeIf(key -> key > BAND_2GHZ);
        }

        if (!isDualBandSupported()) {
            mHotspotBandMap.keySet().removeIf(key -> key == BAND_2GHZ_5GHZ);
        }

        // If dual band is supported then there is no need to allow users to select
        // between 2.4 GHz and 5 GHz since both bands will be available to connect to.
        if (isDualBandSupported()) {
            mHotspotBandMap.keySet().removeIf(key -> key < BAND_2GHZ_5GHZ);
        }

        getPreference().setEntries(mHotspotBandMap.values().toArray(CharSequence[]::new));
        getPreference().setEntryValues(
                mHotspotBandMap.keySet().stream().map(Object::toString).toArray(
                        CharSequence[]::new));

        mBand = validateSelection(getBandFromConfig());
        getPreference().setValue(Integer.toString(mBand));
    }

    private int getBandFromConfig() {
        int band = 0;

        SparseIntArray channels = getCarSoftApConfig().getChannels();
        for (int i = 0; i < channels.size(); i++) {
            band |= channels.keyAt(i);
        }

        if (band == BAND_2GHZ_5GHZ && channels.size() == 1) {
            // band should be set to BAND_5GHZ to differentiate between dual band which would also
            // be BAND_2GHZ_5GHZ.
            band = BAND_5GHZ;
        }

        return band;
    }

    private int validateSelection(int band) {
        return mHotspotBandMap.containsKey(band) ? band : BAND_2GHZ;
    }

    private void updateApBand() {
        SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(
                getCarSoftApConfig());

        if (mBand == BAND_5GHZ) {
            // Only BAND_5GHZ is not supported, must include BAND_2GHZ since some of countries
            // don't support 5G
            configBuilder.setBand(BAND_2GHZ_5GHZ);
        } else if (Flags.hotspotUiSpeedUpdate() && mBand == BAND_2GHZ_5GHZ) {
            configBuilder.setBands(DUAL_BANDS);
        } else {
            configBuilder.setBand(BAND_2GHZ);
        }

        setCarSoftApConfig(configBuilder.build());
        getPreference().setValue(Integer.toString(mBand));
    }

    private boolean is5GhzBandSupported() {
        String countryCode = getCarWifiManager().getCountryCode();
        return getCarWifiManager().is5GhzBandSupported() && countryCode != null;
    }

    private boolean isDualBandSupported() {
        return Flags.hotspotUiSpeedUpdate() && getCarWifiManager().isDualBandSupported();
    }
}