package com.neptune.modules;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.provider.Settings;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.CellSignalStrength;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;

import com.neptune.base.NeptuneAppMgmt;
import com.neptune.plugin.models.SignalInfoModel;
import com.neptune.utils.MyLog;
import com.pax.dal.IPhoneManager;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Phone {

    private static Phone phone;
    private IPhoneManager iPhoneManager = null;
    private Context context = null;
    private TelephonyManager telephonyManager = null;

    private Phone() {
        context = NeptuneAppMgmt.getAppContext();
        telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        try {
            iPhoneManager = NeptuneAppMgmt.getDal().getPhoneManager();
        } catch (Exception e) {
            MyLog.logE("getPhoneManager: " + e.getMessage());
        }
    }

    public static Phone getInstance() {
        if (phone == null) {
            phone = new Phone();
        }
        return phone;
    }

    public SignalInfoModel getSignalInfo(long timeout) {
        int simState = getSimState();
        boolean airplane = isAirplaneModeEnable();
        String networkType = getNetworkType();
        String operator = getOperator();

        Integer level = null;
        Integer dbm = null;
        Integer asu = null;
        String source = null;

        if (simState == TelephonyManager.SIM_STATE_READY && !airplane) {
            CellSignalStrength cell = getRegisteredCellSignalStrength();
            if (cell != null) {
                level = cell.getLevel();
                dbm = cell.getDbm();
                asu = cell.getAsuLevel();
                source = "cellInfo";
            } else {
                SignalStrength signal = listenSignalStrength(timeout);
                if (signal != null) {
                    level = signal.getLevel();
                    if (signal.isGsm() && signal.getGsmSignalStrength() != 99) {
                        asu = signal.getGsmSignalStrength();
                    }
                    source = "listener";
                }
            }
        }
        MyLog.logI("getSignalInfo level=" + level + " dbm=" + dbm + " source=" + source);

        return new SignalInfoModel(level, dbm, asu, networkType, operator, simStateName(simState), airplane, Build.VERSION.SDK_INT, source);
    }

    public boolean isAirplaneModeEnable() {
        try {
            if (iPhoneManager != null) {
                return iPhoneManager.isAirplaneModeEnable();
            }
        } catch (Exception e) {
            MyLog.logE("isAirplaneModeEnable: " + e.getMessage());
        }
        return Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
    }

    public int getSimState() {
        try {
            return telephonyManager.getSimState();
        } catch (Exception e) {
            MyLog.logE("getSimState: " + e.getMessage());
            return TelephonyManager.SIM_STATE_UNKNOWN;
        }
    }

    public String getOperator() {
        try {
            String operator = telephonyManager.getNetworkOperatorName();
            return (operator != null && !operator.isEmpty()) ? operator : null;
        } catch (Exception e) {
            MyLog.logE("getOperator: " + e.getMessage());
            return null;
        }
    }

    public String getNetworkType() {
        try {
            return networkTypeName(telephonyManager.getNetworkType());
        } catch (Exception e) {
            MyLog.logE("getNetworkType: " + e.getMessage());
            return null;
        }
    }

    private boolean hasPermission(String permission) {
        return context.checkPermission(permission, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED;
    }

    private CellSignalStrength getRegisteredCellSignalStrength() {
        if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
            MyLog.logI("getAllCellInfo: ACCESS_COARSE_LOCATION not granted");
            return null;
        }
        try {
            List<CellInfo> cells = telephonyManager.getAllCellInfo();
            if (cells == null) {
                return null;
            }
            for (CellInfo cell : cells) {
                if (!cell.isRegistered()) {
                    continue;
                }
                if (cell instanceof CellInfoLte) {
                    return ((CellInfoLte) cell).getCellSignalStrength();
                } else if (cell instanceof CellInfoWcdma) {
                    return ((CellInfoWcdma) cell).getCellSignalStrength();
                } else if (cell instanceof CellInfoGsm) {
                    return ((CellInfoGsm) cell).getCellSignalStrength();
                } else if (cell instanceof CellInfoCdma) {
                    return ((CellInfoCdma) cell).getCellSignalStrength();
                }
            }
        } catch (Exception e) {
            MyLog.logE("getAllCellInfo: " + e.getMessage());
        }
        return null;
    }

    private SignalStrength listenSignalStrength(long timeout) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            MyLog.logE("listenSignalStrength: can't wait on main thread");
            return null;
        }
        final SignalStrength[] result = new SignalStrength[1];
        final PhoneStateListener[] listener = new PhoneStateListener[1];
        final CountDownLatch latch = new CountDownLatch(1);
        Handler mainHandler = new Handler(Looper.getMainLooper());

        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    listener[0] = new PhoneStateListener() {
                        @Override
                        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
                            result[0] = signalStrength;
                            latch.countDown();
                        }
                    };
                    telephonyManager.listen(listener[0], PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
                } catch (Exception e) {
                    MyLog.logE("listenSignalStrength: " + e.getMessage());
                    latch.countDown();
                }
            }
        });

        try {
            latch.await(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            MyLog.logE("listenSignalStrength: " + e.getMessage());
        }

        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (listener[0] != null) {
                    telephonyManager.listen(listener[0], PhoneStateListener.LISTEN_NONE);
                }
            }
        });
        return result[0];
    }

    private static String simStateName(int simState) {
        switch (simState) {
            case TelephonyManager.SIM_STATE_ABSENT:             return "ABSENT";
            case TelephonyManager.SIM_STATE_PIN_REQUIRED:       return "PIN_REQUIRED";
            case TelephonyManager.SIM_STATE_PUK_REQUIRED:       return "PUK_REQUIRED";
            case TelephonyManager.SIM_STATE_NETWORK_LOCKED:     return "NETWORK_LOCKED";
            case TelephonyManager.SIM_STATE_READY:              return "READY";
            case TelephonyManager.SIM_STATE_NOT_READY:          return "NOT_READY";
            case TelephonyManager.SIM_STATE_PERM_DISABLED:      return "PERM_DISABLED";
            case TelephonyManager.SIM_STATE_CARD_IO_ERROR:      return "CARD_IO_ERROR";
            case TelephonyManager.SIM_STATE_CARD_RESTRICTED:    return "CARD_RESTRICTED";
            default:                                            return "UNKNOWN";
        }
    }

    private static String networkTypeName(int networkType) {
        switch (networkType) {
            case TelephonyManager.NETWORK_TYPE_GPRS:        return "GPRS";
            case TelephonyManager.NETWORK_TYPE_EDGE:        return "EDGE";
            case TelephonyManager.NETWORK_TYPE_GSM:         return "GSM";
            case TelephonyManager.NETWORK_TYPE_CDMA:        return "CDMA";
            case TelephonyManager.NETWORK_TYPE_1xRTT:       return "1xRTT";
            case TelephonyManager.NETWORK_TYPE_IDEN:        return "IDEN";
            case TelephonyManager.NETWORK_TYPE_UMTS:        return "UMTS";
            case TelephonyManager.NETWORK_TYPE_EVDO_0:      return "EVDO_0";
            case TelephonyManager.NETWORK_TYPE_EVDO_A:      return "EVDO_A";
            case TelephonyManager.NETWORK_TYPE_EVDO_B:      return "EVDO_B";
            case TelephonyManager.NETWORK_TYPE_HSDPA:       return "HSDPA";
            case TelephonyManager.NETWORK_TYPE_HSUPA:       return "HSUPA";
            case TelephonyManager.NETWORK_TYPE_HSPA:        return "HSPA";
            case TelephonyManager.NETWORK_TYPE_HSPAP:       return "HSPAP";
            case TelephonyManager.NETWORK_TYPE_EHRPD:       return "EHRPD";
            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:    return "TD_SCDMA";
            case TelephonyManager.NETWORK_TYPE_LTE:         return "LTE";
            case TelephonyManager.NETWORK_TYPE_IWLAN:       return "IWLAN";
            default:                                        return "UNKNOWN";
        }
    }
}
