package com.avg.zaap.crashlibrary.offline;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.avg.zaap.crashlibrary.ConnectionStatus;
import com.avg.zaap.crashlibrary.CrashLib;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;

/**
 * This class serves as the default connectivity helper.
 * <p/>
 * Created by marek.osvald on 27.7.2016.
 */
public class ConnectivityHelper implements IConnectivityHelper {

    private static final String TAG = ConnectivityHelper.class.getSimpleName();
    private ConnectivityManager mConnectivityManager;
    private IConnectivityHelperFactory mFactory;

    /**
     * Parametric constructor. Sets the Android context.s
     *
     * @param context Android context instance
     */
    public ConnectivityHelper(@NonNull Context context) {
        this(context, new ConnectivityHelperFactory());
    }

    /**
     * Parametric constructor. Sets the Android context.s
     *
     * @param context Android context instance
     */
    public ConnectivityHelper(@NonNull Context context, IConnectivityHelperFactory factory) {
        mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        mFactory = factory;
    }

    @Override
    @NonNull
    public ConnectionStatus getConnectionStatus() {
        final NetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
        return processNetworkInfo(activeNetworkInfo);
    }

    @Override
    @NonNull
    public ConnectionStatus processNetworkInfo(@Nullable NetworkInfo networkInfo) {
        ConnectionStatus connectionStatus = mFactory.getConnectionStatus();

        if (networkInfo != null) {
            connectionStatus.setConnected(networkInfo.isConnected() && networkInfo.isAvailable());

            if (isNetworkTypeMobile(networkInfo)) {
                connectionStatus.setConnection(Connection.MOBILE);
            } else if (isNetworkTypeWifi(networkInfo)) {
                connectionStatus.setConnection(Connection.WIFI);
            } else {
                connectionStatus.setConnection(Connection.OTHER);
            }

            connectionStatus.setRoaming(networkInfo.isRoaming());
        } else {
            // no connection default values
            connectionStatus.setConnected(false);
            connectionStatus.setConnection(Connection.NONE);
            connectionStatus.setRoaming(false);
        }

        return connectionStatus;
    }

    @Override
    public boolean isConnectivitySuitable(ConnectionStatus connectionStatus) {
        //@formatter:off
        return checkOffline(connectionStatus.isConnected())           &&
               checkConnectionLevel(connectionStatus.getConnection()) &&
               checkRoaming(connectionStatus.isRoaming());
        //@formatter:on
    }

    private boolean isNetworkTypeMobile(NetworkInfo networkInfo) {
        switch (networkInfo.getType()) {
            case ConnectivityManager.TYPE_MOBILE:
            case ConnectivityManager.TYPE_MOBILE_DUN:
                //noinspection deprecation
            case ConnectivityManager.TYPE_MOBILE_HIPRI:
                //noinspection deprecation
            case ConnectivityManager.TYPE_MOBILE_MMS:
                //noinspection deprecation
            case ConnectivityManager.TYPE_MOBILE_SUPL:
                return true;

            default:
                return false;
        }
    }

    private boolean isNetworkTypeWifi(NetworkInfo networkInfo) {
        switch (networkInfo.getType()) {
            case ConnectivityManager.TYPE_WIFI:
            case ConnectivityManager.TYPE_WIMAX:
                return true;

            default:
                return false;
        }
    }

    private boolean checkOffline(boolean connected) {
        final boolean offlineConfig = CrashLib.getConfig().isOfflineSuitable();
        boolean result = offlineConfig || connected;
        CrashLogger.d(TAG, "Offline suitable: %s (config: %s, connected: %s)", result, offlineConfig, connected);
        return result;
    }

    private boolean checkConnectionLevel(@NonNull Connection connection) {
        final Connection levelConfig = CrashLib.getConfig().getConnectionLevelSuitable();
        boolean result = connection.getLevel() >= levelConfig.getLevel();
        CrashLogger.d(TAG, "Connection level suitable: %s (config: %s, connection: %s)", result, levelConfig, connection);
        return result;
    }

    private boolean checkRoaming(boolean roaming) {
        final boolean roamingConfig = CrashLib.getConfig().isRoamingSuitable();
        boolean result = roamingConfig || !roaming;
        CrashLogger.d(TAG, "Roaming suitable: %s (config: %s, connection: %s)", result, roamingConfig, roaming);
        return result;
    }

}
