package nyc.friendlyrobot.demo.util;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.support.annotation.NonNull;

import javax.inject.Inject;
import javax.inject.Singleton;

import rx.Observable;
import rx.functions.Func1;
import rx.subjects.BehaviorSubject;

import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.telephony.TelephonyManager.NETWORK_TYPE_1xRTT;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;

/**
 * object to track network status
 */
@Singleton
public class NetworkStatus extends BroadcastReceiver {

    private final ConnectivityManager connectivityManager;
    private final WifiManager wifiManager;
    private final BehaviorSubject<Boolean> onChangeSubject = BehaviorSubject.create();

    @Inject
    public NetworkStatus(Application context) {
        connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
        wifiManager = (WifiManager) context.getSystemService(WIFI_SERVICE);
    }

    public Observable<Void> onConnect() {
        return onChange().filter(connected -> connected).map((Func1<Boolean, Void>) connected -> null);
    }

    public Observable<Void> onDisconnect() {
        return onChange().filter(connected -> !connected).map((Func1<Boolean, Void>) connected -> null);
    }

    public Observable<Boolean> onChange() {
        return onChangeSubject.distinctUntilChanged();
    }

    public String getNetworkStatus() {
        NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
        return (netInfo == null) ? "None" : getNetworkStatus(netInfo);
    }

    public boolean isInternetConnected() {
        return isOnGoodConnection(); // seems weird that these are the same
    }

    public boolean isOnGoodConnection() {
        NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
        return netInfo != null && isOnGoodConnection(netInfo);
    }

    public boolean isOnGoodWIFIConnection() {
        NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
        return netInfo != null && isOnGoodWIFIConnection(netInfo);
    }

    private boolean isOnGoodConnection(@NonNull NetworkInfo netInfo) {
        return netInfo.isConnected() && (isOnGoodWIFIConnection(netInfo) || isOnGoodCellularConnection(netInfo));
    }

    private boolean isOnGoodCellularConnection(@NonNull NetworkInfo netInfo) {
        return netInfo.getSubtype() != NETWORK_TYPE_1xRTT && netInfo.getSubtype() != NETWORK_TYPE_EDGE;
    }

    private boolean isOnGoodWIFIConnection(@NonNull NetworkInfo netInfo) {
        return (netInfo.getType() == TYPE_WIFI)
                && (WifiManager.calculateSignalLevel(wifiManager.getConnectionInfo().getRssi(), 4) > 0);
    }

    private String getNetworkStatus(@NonNull NetworkInfo netInfo) {
        return (netInfo.getType() == TYPE_MOBILE) ? netInfo.getSubtypeName() : netInfo.getTypeName();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        boolean isInternetConnected = isInternetConnected();
        onChangeSubject.onNext(isInternetConnected);
    }

}