package com.hoxfon.react.RNTwilioVoice;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static com.hoxfon.react.RNTwilioVoice.EventManager.EVENT_PROXIMITY;
import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.TAG;

public class ProximityManager {

    private static final String ERROR_PROXIMITY_SENSOR_NOT_SUPPORTED = "Proximity sensor is not supported.";
    private static final String ERROR_PROXIMITY_LOCK_NOT_SUPPORTED = "Proximity lock is not supported.";

    private SensorManager sensorManager;

    private Sensor proximitySensor;
    private SensorEventListener proximityListener;

    private WakeLock proximityWakeLock = null;
    private PowerManager powerManager;

    private EventManager eventManager;

    public ProximityManager(ReactApplicationContext context, EventManager em) {
        eventManager = em;
        powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        initProximityWakeLock();
    }

    private void initProximityWakeLock() {
        // Check if PROXIMITY_SCREEN_OFF_WAKE_LOCK is implemented, not part of public api.
        // PROXIMITY_SCREEN_OFF_WAKE_LOCK and isWakeLockLevelSupported are available from api 21
        try {
            boolean isSupported;
            int proximityScreenOffWakeLock;
            if (android.os.Build.VERSION.SDK_INT < 21) {
                Field field = PowerManager.class.getDeclaredField("PROXIMITY_SCREEN_OFF_WAKE_LOCK");
                proximityScreenOffWakeLock = (Integer) field.get(null);

                Method method = powerManager.getClass().getDeclaredMethod("getSupportedWakeLockFlags");
                int powerManagerSupportedFlags = (Integer) method.invoke(powerManager);
                isSupported = ((powerManagerSupportedFlags & proximityScreenOffWakeLock) != 0x0);
            } else {
                proximityScreenOffWakeLock = powerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
                isSupported = powerManager.isWakeLockLevelSupported(proximityScreenOffWakeLock);
            }
            if (isSupported) {
                proximityWakeLock = powerManager.newWakeLock(proximityScreenOffWakeLock, TAG);
                proximityWakeLock.setReferenceCounted(false);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to get proximity screen locker.");
        }
    }

    private void turnScreenOn() {
        if (proximityWakeLock == null) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, ERROR_PROXIMITY_LOCK_NOT_SUPPORTED);
            }
            return;
        }
        synchronized (proximityWakeLock) {
            if (proximityWakeLock.isHeld()) {
                if (BuildConfig.DEBUG) {
                    Log.d(TAG, "turnScreenOn()");
                }
                if (android.os.Build.VERSION.SDK_INT >= 21) {
                    proximityWakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
                }
            }
        }
    }

    private void turnScreenOff() {
        if (proximityWakeLock == null) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, ERROR_PROXIMITY_LOCK_NOT_SUPPORTED);
            }
            return;
        }
        synchronized (proximityWakeLock) {
            if (!proximityWakeLock.isHeld()) {
                if (BuildConfig.DEBUG) {
                    Log.d(TAG, "turnScreenOff()");
                }
                proximityWakeLock.acquire();
            }
        }
    }

    private void initProximitySensorEventListener() {
        if (proximityListener != null) {
            return;
        }
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "initProximitySensorEventListener()");
        }
        proximityListener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent sensorEvent) {
                if (sensorEvent.sensor.getType() == Sensor.TYPE_PROXIMITY) {
                    boolean isNear = false;
                    if (sensorEvent.values[0] < proximitySensor.getMaximumRange()) {
                        isNear = true;
                    }
                    if (isNear) {
                        turnScreenOff();
                    } else {
                        turnScreenOn();
                    }
                    WritableMap data = Arguments.createMap();
                    data.putBoolean("isNear", isNear);
                    eventManager.sendEvent(EVENT_PROXIMITY, data);
                }
            }

            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }
        };
    }

    public void startProximitySensor() {
        if (proximitySensor == null) {
            Log.e(TAG, ERROR_PROXIMITY_SENSOR_NOT_SUPPORTED);
            return;
        }
        initProximitySensorEventListener();
        if (proximityListener != null) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "register proximity listener");
            }
            // SensorManager.SENSOR_DELAY_FASTEST(0 ms),
            // SensorManager.SENSOR_DELAY_GAME(20 ms),
            // SensorManager.SENSOR_DELAY_UI(60 ms),
            // SensorManager.SENSOR_DELAY_NORMAL(200 ms)
            sensorManager.registerListener(
                proximityListener,
                proximitySensor,
                SensorManager.SENSOR_DELAY_NORMAL
            );
        }
    }

    public void stopProximitySensor() {
        if (proximitySensor == null) {
            Log.e(TAG, ERROR_PROXIMITY_SENSOR_NOT_SUPPORTED);
            return;
        }
        if (proximityListener != null) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "unregister proximity listener");
            }
            sensorManager.unregisterListener(proximityListener);
            proximityListener = null;
        }
    }
}