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

import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;

import static com.android.settings.network.TetherEnabler.TETHERING_BLUETOOTH_ON;
import static com.android.settings.network.TetherEnabler.TETHERING_ETHERNET_ON;
import static com.android.settings.network.TetherEnabler.TETHERING_OFF;
import static com.android.settings.network.TetherEnabler.TETHERING_USB_ON;
import static com.android.settings.network.TetherEnabler.TETHERING_WIFI_ON;
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestrictionEnforced;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Lifecycle.Event;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
import com.android.settings.widget.GenericSwitchController;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.TetherUtil;

import java.util.concurrent.atomic.AtomicReference;

/**
 * This controller helps to manage the switch state and visibility of "Hotspot & tethering" switch
 * preference. It updates the preference summary text based on tethering state.
 */
public class AllInOneTetherPreferenceController extends BasePreferenceController implements
        LifecycleObserver, TetherEnabler.OnTetherStateUpdateListener {
    private static final String TAG = "AllInOneTetherPreferenceController";

    private int mTetheringState;

    private final boolean mAdminDisallowedTetherConfig;
    private final AtomicReference<BluetoothPan> mBluetoothPan;
    private final BluetoothAdapter mBluetoothAdapter;
    @VisibleForTesting
    final BluetoothProfile.ServiceListener mBtProfileServiceListener =
            new BluetoothProfile.ServiceListener() {
                @Override
                public void onServiceConnected(int profile, BluetoothProfile proxy) {
                    if (mBluetoothPan.get() == null) {
                        mBluetoothPan.set((BluetoothPan) proxy);
                    }
                }

                @Override
                public void onServiceDisconnected(int profile) { /* Do nothing */ }
            };

    private PrimarySwitchPreference mPreference;
    private TetherEnabler mTetherEnabler;
    private BroadcastReceiver mBluetoothStateReceiver;

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    AllInOneTetherPreferenceController() {
        super(null /*context*/, "test");
        mAdminDisallowedTetherConfig = false;
        mBluetoothPan = new AtomicReference<>();
        mBluetoothAdapter = null;
    }

    public AllInOneTetherPreferenceController(Context context, String key) {
        super(context, key);
        mBluetoothPan = new AtomicReference<>();
        mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
                context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = screen.findPreference(mPreferenceKey);
    }

    @Override
    public int getAvailabilityStatus() {
        if (!TetherUtil.isTetherAvailable(mContext)
                || !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
            return CONDITIONALLY_UNAVAILABLE;
        } else {
            return AVAILABLE;
        }
    }

    @Override
    public CharSequence getSummary() {
        switch (mTetheringState) {
            case TETHERING_OFF:
                return mContext.getString(R.string.tether_settings_summary_off);
            case TETHERING_WIFI_ON:
                return mContext.getString(R.string.tether_settings_summary_hotspot_only);
            case TETHERING_USB_ON:
                return mContext.getString(R.string.tether_settings_summary_usb_tethering_only);
            case TETHERING_BLUETOOTH_ON:
                return mContext.getString(
                        R.string.tether_settings_summary_bluetooth_tethering_only);
            case TETHERING_ETHERNET_ON:
                return mContext.getString(R.string.tether_settings_summary_ethernet_tethering_only);
            case TETHERING_WIFI_ON | TETHERING_USB_ON:
                return mContext.getString(R.string.tether_settings_summary_hotspot_and_usb);
            case TETHERING_WIFI_ON | TETHERING_BLUETOOTH_ON:
                return mContext.getString(R.string.tether_settings_summary_hotspot_and_bluetooth);
            case TETHERING_WIFI_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(R.string.tether_settings_summary_hotspot_and_ethernet);
            case TETHERING_USB_ON | TETHERING_BLUETOOTH_ON:
                return mContext.getString(R.string.tether_settings_summary_usb_and_bluetooth);
            case TETHERING_USB_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(R.string.tether_settings_summary_usb_and_ethernet);
            case TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(R.string.tether_settings_summary_bluetooth_and_ethernet);
            case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_BLUETOOTH_ON:
                return mContext.getString(
                        R.string.tether_settings_summary_hotspot_and_usb_and_bluetooth);
            case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(
                        R.string.tether_settings_summary_hotspot_and_usb_and_ethernet);
            case TETHERING_WIFI_ON | TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(
                        R.string.tether_settings_summary_hotspot_and_bluetooth_and_ethernet);
            case TETHERING_USB_ON | TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
                return mContext.getString(
                        R.string.tether_settings_summary_usb_and_bluetooth_and_ethernet);
            case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_BLUETOOTH_ON
                    | TETHERING_ETHERNET_ON:
                return mContext.getString(R.string.tether_settings_summary_all);
            default:
                Log.e(TAG, "Unknown tethering state");
                return mContext.getString(R.string.summary_placeholder);
        }
    }

    @OnLifecycleEvent(Event.ON_CREATE)
    public void onCreate() {
        if (mBluetoothAdapter != null
                && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
            mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
                        BluetoothProfile.PAN);
        }
        if (mBluetoothStateReceiver == null) {
            mBluetoothStateReceiver = new BluetoothStateReceiver();
            mContext.registerReceiver(
                    mBluetoothStateReceiver,
                            new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
        }
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onResume() {
        if (mTetherEnabler != null) {
            mTetherEnabler.addListener(this);
        }
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onPause() {
        if (mTetherEnabler != null) {
            mTetherEnabler.removeListener(this);
        }
    }

    @OnLifecycleEvent(Event.ON_DESTROY)
    public void onDestroy() {
        final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
        if (profile != null && mBluetoothAdapter != null) {
            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
        }
        if (mBluetoothStateReceiver != null) {
            mContext.unregisterReceiver(mBluetoothStateReceiver);
            mBluetoothStateReceiver = null;
        }
    }

    void initEnabler(Lifecycle lifecycle) {
        if (mPreference != null) {
            mTetherEnabler = new TetherEnabler(
                    mContext, new GenericSwitchController(mPreference), mBluetoothPan);
            if (lifecycle != null) {
                lifecycle.addObserver(mTetherEnabler);
            }
        } else {
            Log.e(TAG, "TetherEnabler is not initialized");
        }
    }

    @Override
    public void onTetherStateUpdated(@TetherEnabler.TetheringState int state) {
        mTetheringState = state;
        updateState(mPreference);
    }

    private class BluetoothStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            Log.i(TAG, "onReceive: action: " + action);

            if (TextUtils.equals(action, BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state =
                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                Log.i(TAG, "onReceive: state: " + BluetoothAdapter.nameForState(state));
                final BluetoothProfile profile = mBluetoothPan.get();
                switch(state) {
                    case BluetoothAdapter.STATE_ON:
                        if (profile == null && mBluetoothAdapter != null) {
                            mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
                                    BluetoothProfile.PAN);
                        }
                        break;
                }
            }
        }
    }
}
