package com.idevicesinc.sweetblue;


import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.os.Build;
import android.os.DeadObjectException;
import com.idevicesinc.sweetblue.compat.L_Util;
import com.idevicesinc.sweetblue.compat.M_Util;
import com.idevicesinc.sweetblue.utils.Interval;
import com.idevicesinc.sweetblue.utils.Utils;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import static com.idevicesinc.sweetblue.BleManagerState.OFF;
import static com.idevicesinc.sweetblue.BleManagerState.ON;


@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public final class P_AndroidBluetoothManager implements P_NativeManagerLayer
{

    private BluetoothManager m_manager;
    private BluetoothAdapter m_adaptor;
    private BleManager m_bleManager;
    private static Method m_getLeState_marshmallow;
    private static Integer m_refState;
    private static Integer m_state;


    public final void setBleManager(BleManager mgr)
    {
        m_bleManager = mgr;
    }


    @Override
    public final int getConnectionState(P_NativeDeviceLayer device, int profile)
    {
        if (m_manager != null)
        {
            return m_manager.getConnectionState(device.getNativeDevice(), BluetoothGatt.GATT_SERVER);
        }
        return 0;
    }

    @Override
    public final boolean startDiscovery()
    {
        if (m_adaptor != null)
        {
            return m_adaptor.startDiscovery();
        }
        return false;
    }

    @Override public final boolean isManagerNull()
    {
        return m_manager == null || m_adaptor == null;
    }

    @Override public final void resetManager(Context context)
    {
        m_manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        m_adaptor = m_manager.getAdapter();
    }

    @Override public final boolean disable()
    {
        if (m_adaptor != null)
            return m_adaptor.disable();

        return false;
    }

    @Override public final boolean enable()
    {
        if (m_adaptor != null)
            return m_adaptor.enable();

        return false;
    }

    @Override public final boolean isMultipleAdvertisementSupported()
    {
        return L_Util.isAdvertisingSupportedByChipset(m_adaptor);
    }

    @Override public final int getState()
    {
        if (m_adaptor != null)
            return m_adaptor.getState();

        return BleStatuses.STATE_OFF;
    }

    @Override public final int getBleState()
    {
        try
        {
            if (m_getLeState_marshmallow == null)
            {
                m_getLeState_marshmallow = BluetoothAdapter.class.getDeclaredMethod("getLeState");
            }
            m_refState = (Integer) m_getLeState_marshmallow.invoke(m_adaptor);
            m_state = m_adaptor.getState();
            // This is to fix an issue on the S7 (and perhaps other phones as well), where the OFF
            // state is never returned from the getLeState method. This is because the BLE_ states represent if LE only mode is on/off. This does NOT
            // relate to the Bluetooth radio being on/off. So, we check if STATE_BLE_ON, and the normal getState() method returns OFF, we
            // will return a state of OFF here.
            if (m_refState == BleStatuses.STATE_BLE_ON && m_state == OFF.getNativeCode())
            {
                return m_state;
            }
            else
            {
                // --- RB  > Not sure why this was setting to off, when the above handles the exception we want to handle. Commenting out for now.
//                m_refState = BleStatuses.STATE_OFF;
            }
            return m_refState;
        } catch (Exception e)
        {
            if (e instanceof DeadObjectException)
            {
                BleManager.UhOhListener.UhOh uhoh = BleManager.UhOhListener.UhOh.DEAD_OBJECT_EXCEPTION;
                m_bleManager.uhOh(uhoh);
            }
            return m_adaptor.getState();
        }
    }

    @Override public final String getAddress()
    {
        if (m_adaptor != null)
            return m_adaptor.getAddress();

        return BleDevice.NULL_MAC();
    }

    @Override
    public String getName()
    {
        if (m_adaptor != null)
            return m_adaptor.getName();
        return "";
    }

    @Override
    public void setName(String name)
    {
        if (m_adaptor != null)
            m_adaptor.setName(name);
    }

    @Override public final P_NativeServerLayer openGattServer(Context context, P_BleServer_Listeners listeners)
    {
        return new P_AndroidBleServer(m_manager.openGattServer(context, listeners));
    }

    @Override
    public final void startAdvertising(AdvertiseSettings settings, AdvertiseData adData, L_Util.AdvertisingCallback callback)
    {
        if (!L_Util.startAdvertising(m_adaptor, settings, adData, callback))
        {
            m_bleManager.ASSERT(false, "Unable to start advertising!");
            m_bleManager.getLogger().e("Failed to start advertising!");
        }
    }

    @Override
    public final void stopAdvertising()
    {
        L_Util.stopAdvertising(m_adaptor);
    }

    @Override public final Set<BluetoothDevice> getBondedDevices()
    {
        if (m_adaptor != null)
            return m_adaptor.getBondedDevices();

        return new HashSet<>(0);
    }

    @Override
    public final boolean cancelDiscovery()
    {
        if (m_adaptor != null)
        {
            return m_adaptor.cancelDiscovery();
        }
        return false;
    }

    @Override public final BluetoothDevice getRemoteDevice(String macAddress)
    {
        if (m_adaptor != null)
            return m_adaptor.getRemoteDevice(macAddress);

        return null;
    }

    @Override
    public final BluetoothAdapter getNativeAdaptor()
    {
        return m_adaptor;
    }

    @Override
    public final BluetoothManager getNativeManager()
    {
        return m_manager;
    }

    @Override public final boolean isLocationEnabledForScanning_byOsServices()
    {
        return Utils.isLocationEnabledForScanning_byOsServices(m_bleManager.getApplicationContext());
    }

    @Override public final boolean isLocationEnabledForScanning_byRuntimePermissions()
    {
        return Utils.isLocationEnabledForScanning_byRuntimePermissions(m_bleManager.getApplicationContext());
    }

    @Override public final boolean isLocationEnabledForScanning()
    {
        return Utils.isLocationEnabledForScanning(m_bleManager.getApplicationContext());
    }

    @Override public final boolean isBluetoothEnabled()
    {
        if (m_adaptor != null)
        {
            return m_adaptor.isEnabled();
        }
        else
        {
            // If the BleManager instance is somehow null here, we'll try to assign it now
            if (m_bleManager == null)
            {
                m_bleManager = BleManager.s_instance;
                
                // If the manager is still somehow null here, we'll just return false for the time being
                if (m_bleManager /*still*/ == null)
                    return false;
            }
            return m_bleManager.is(ON);
        }
    }

    @Override public final void startLScan(int scanMode, Interval delay, L_Util.ScanCallback callback)
    {
        L_Util.startNativeScan(m_adaptor, scanMode, delay, callback);
    }

    @Override public final void startMScan(int scanMode, Interval delay, L_Util.ScanCallback callback)
    {
        M_Util.startNativeScan(m_adaptor, scanMode, delay, callback);
    }

    @Override public final boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
    {
        if (m_adaptor != null)
            return m_adaptor.startLeScan(callback);

        return false;
    }

    @Override public final void stopLeScan(BluetoothAdapter.LeScanCallback callback)
    {
        if (m_adaptor != null)
        {
            if (m_bleManager.getScanManager().isPostLollipopScan())
            {
                L_Util.stopNativeScan(m_adaptor);
            }
            else
            {
                m_adaptor.stopLeScan(callback);
            }
        }
        else
            m_bleManager.getLogger().e("Tried to stop scan (if it's even running), but the Bluetooth Adaptor is null!");
    }
}