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

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.SimpleClock;
import android.os.SystemClock;

import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;

import java.time.Clock;
import java.time.ZoneOffset;

// TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController.
/**
 * This places a preference into a PreferenceGroup owned by some parent
 * controller class when there is a wifi connection present.
 */
public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
        WifiPickerTracker.WifiPickerTrackerCallback, LifecycleObserver {

    private static final String TAG = "WifiConnPrefCtrl";

    private static final String KEY = "active_wifi_connection";

    // Max age of tracked WifiEntries.
    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
    // Interval between initiating WifiPickerTracker scans.
    private static final long SCAN_INTERVAL_MILLIS = 10_000;

    private UpdateListener mUpdateListener;
    private Context mPrefContext;
    private String mPreferenceGroupKey;
    private PreferenceGroup mPreferenceGroup;
    @VisibleForTesting
    public WifiPickerTracker mWifiPickerTracker;
    private WifiEntryPreference mPreference;
    private int order;
    private int mMetricsCategory;
    // Worker thread used for WifiPickerTracker work.
    private HandlerThread mWorkerThread;

    /**
     * Used to notify a parent controller that this controller has changed in availability, or has
     * updated the content in the preference that it manages.
     */
    public interface UpdateListener {
        void onChildrenUpdated();
    }

    /**
     * @param context            the context for the UI where we're placing the preference
     * @param lifecycle          for listening to lifecycle events for the UI
     * @param updateListener     for notifying a parent controller of changes
     * @param preferenceGroupKey the key to use to lookup the PreferenceGroup where this controller
     *                           will add its preference
     * @param order              the order that the preference added by this controller should use -
     *                           useful when this preference needs to be ordered in a specific way
     *                           relative to others in the PreferenceGroup
     * @param metricsCategory    - the category to use as the source when handling the click on the
     *                           pref to go to the wifi connection detail page
     */
    public WifiConnectionPreferenceController(Context context, Lifecycle lifecycle,
            UpdateListener updateListener, String preferenceGroupKey, int order,
            int metricsCategory) {
        super(context);
        lifecycle.addObserver(this);
        mUpdateListener = updateListener;
        mPreferenceGroupKey = preferenceGroupKey;
        this.order = order;
        mMetricsCategory = metricsCategory;

        mWorkerThread = new HandlerThread(
                TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
                Process.THREAD_PRIORITY_BACKGROUND);
        mWorkerThread.start();
        final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
            @Override
            public long millis() {
                return SystemClock.elapsedRealtime();
            }
        };
        mWifiPickerTracker = FeatureFactory.getFactory(context)
                .getWifiTrackerLibProvider()
                .createWifiPickerTracker(lifecycle, context,
                        new Handler(Looper.getMainLooper()),
                        mWorkerThread.getThreadHandler(),
                        elapsedRealtimeClock,
                        MAX_SCAN_AGE_MILLIS,
                        SCAN_INTERVAL_MILLIS,
                        this);
    }

    /**
     * This event is triggered when users click back button at 'Network & internet'.
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        mWorkerThread.quit();
    }

    @Override
    public boolean isAvailable() {
        return mWifiPickerTracker.getConnectedWifiEntry() != null;
    }

    @Override
    public String getPreferenceKey() {
        return KEY;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreferenceGroup = screen.findPreference(mPreferenceGroupKey);
        mPrefContext = screen.getContext();
        update();
    }

    private void updatePreference(WifiEntry wifiEntry) {
        if (mPreference != null) {
            mPreferenceGroup.removePreference(mPreference);
            mPreference = null;
        }
        if (wifiEntry == null || mPrefContext == null) {
            return;
        }

        mPreference = new WifiEntryPreference(mPrefContext, wifiEntry);
        mPreference.setKey(KEY);
        mPreference.refresh();
        mPreference.setOrder(order);
        mPreference.setOnPreferenceClickListener(pref -> {
            final Bundle args = new Bundle();
            args.putString(WifiNetworkDetailsFragment.KEY_CHOSEN_WIFIENTRY_KEY,
                    wifiEntry.getKey());
            new SubSettingLauncher(mPrefContext)
                    .setTitleRes(R.string.pref_title_network_details)
                    .setDestination(WifiNetworkDetailsFragment.class.getName())
                    .setArguments(args)
                    .setSourceMetricsCategory(mMetricsCategory)
                    .launch();
            return true;
        });
        mPreferenceGroup.addPreference(mPreference);
    }

    private void update() {
        final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
        if (connectedWifiEntry == null) {
            updatePreference(null);
        } else {
            if (mPreference == null || !mPreference.getWifiEntry().equals(connectedWifiEntry)) {
                updatePreference(connectedWifiEntry);
            } else if (mPreference != null) {
                mPreference.refresh();
            }
        }
        mUpdateListener.onChildrenUpdated();
    }

    /** Called when the state of Wifi has changed. */
    @Override
    public void onWifiStateChanged() {
        update();
    }

    /**
     * Update the results when data changes.
     */
    @Override
    public void onWifiEntriesChanged() {
        update();
    }

    @Override
    public void onNumSavedSubscriptionsChanged() {
        // Do nothing.
    }

    @Override
    public void onNumSavedNetworksChanged() {
        // Do nothing.
    }
}
