package org.thosp.yourlocalweather.service;

import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.preference.PreferenceManager;

import org.thosp.yourlocalweather.model.VoiceSettingParametersDbHelper;
import org.thosp.yourlocalweather.utils.Constants;
import org.thosp.yourlocalweather.utils.VoiceSettingParamType;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static org.thosp.yourlocalweather.utils.LogToFile.appendLog;

public class BluetoothEventsReceiver extends BroadcastReceiver {

    private static final String TAG = "BluetoothEventsReceiver";

    private Context context;
    private Long voiceSettingId;

    Handler timerHandler = new Handler();
    Runnable timerRunnable = new Runnable() {

        @Override
        public void run() {
            if (context == null) {
                return;
            }
            sendMessageToWeatherByVoiceService(context);
        }
    };

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        appendLog(context, TAG, "Receiver started with intent: " + intent + " and action " + action);
        this.context = context;
        BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        appendLog(context, TAG, "onReceive:bluetoothDevice: " + bluetoothDevice);
        if (bluetoothDevice == null) {
            return;
        }
        switch (action) {
            case BluetoothDevice.ACTION_ACL_CONNECTED:
                btDeviceConnected(bluetoothDevice); break;
            case BluetoothDevice.ACTION_ACL_DISCONNECTED:
            case BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED:
                btDeviceDisConnected(bluetoothDevice); break;
        }
    }

    private void btDeviceConnected(BluetoothDevice bluetoothDevice) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        Set<String> connectedBtDevices = sharedPreferences.getStringSet(Constants.CONNECTED_BT_DEVICES, new HashSet<String>());
        if (!connectedBtDevices.contains(bluetoothDevice.getAddress())) {
            connectedBtDevices.add(bluetoothDevice.getAddress());
            sharedPreferences.edit().putStringSet(Constants.CONNECTED_BT_DEVICES, connectedBtDevices).apply();
        }
        if (isBtTriggerEnabled(bluetoothDevice)) {
            timerHandler.postDelayed(timerRunnable, 15000);
        }
    }

    private void btDeviceDisConnected(BluetoothDevice bluetoothDevice) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        Set<String> connectedBtDevices = sharedPreferences.getStringSet(Constants.CONNECTED_BT_DEVICES, new HashSet<String>());
        if (connectedBtDevices.contains(bluetoothDevice.getAddress())) {
            connectedBtDevices.remove(bluetoothDevice.getAddress());
            sharedPreferences.edit().putStringSet(Constants.CONNECTED_BT_DEVICES, connectedBtDevices).apply();
        }
    }

    private boolean isBtTriggerEnabled(BluetoothDevice bluetoothDevice) {
        VoiceSettingParametersDbHelper voiceSettingParametersDbHelper = VoiceSettingParametersDbHelper.getInstance(context);
        Map<Long, Long> voiceTriggetType = voiceSettingParametersDbHelper.getLongParam(
                VoiceSettingParamType.VOICE_SETTING_TRIGGER_TYPE.getVoiceSettingParamTypeId());
        appendLog(context, TAG, "isBtTriggerEnabled: " + voiceTriggetType);
        for (Long currentVoiceSettingId: voiceTriggetType.keySet()) {
            Long value = voiceTriggetType.get(currentVoiceSettingId);
            appendLog(context, TAG, "isBtTriggerEnabled:value: " + value);
            if (value == null) {
                continue;
            }
            if (value == 1) {
                Boolean allBtDevices = voiceSettingParametersDbHelper.getBooleanParam(
                        currentVoiceSettingId,
                        VoiceSettingParamType.VOICE_SETTING_TRIGGER_ENABLED_BT_DEVICES.getVoiceSettingParamTypeId());
                appendLog(context, TAG, "isBtTriggerEnabled:allBtDevices: " + allBtDevices);
                if ((allBtDevices != null) && allBtDevices) {
                    voiceSettingId = currentVoiceSettingId;
                    return true;
                }
                String enabledBtDevices = voiceSettingParametersDbHelper.getStringParam(
                        currentVoiceSettingId,
                        VoiceSettingParamType.VOICE_SETTING_TRIGGER_ENABLED_BT_DEVICES.getVoiceSettingParamTypeId());
                if (bluetoothDevice == null) {
                    return false;
                }
                if ((enabledBtDevices != null) && enabledBtDevices.contains(bluetoothDevice.getAddress())) {
                    voiceSettingId = currentVoiceSettingId;
                    return true;
                }
            }
        }
        return false;
    }

    private Messenger weatherByVoiceService;
    private Lock weatherByVoiceServiceLock = new ReentrantLock();
    private Queue<Message> weatherByvOiceUnsentMessages = new LinkedList<>();

    protected void sendMessageToWeatherByVoiceService(Context context) {
        weatherByVoiceServiceLock.lock();
        try {
            Message msg = Message.obtain(
                    null,
                    WeatherByVoiceService.START_VOICE_WEATHER_ALL,
                    new WeatherByVoiceRequestDataHolder(voiceSettingId)
            );
            if (checkIfWeatherByVoiceServiceIsNotBound(context)) {
                //appendLog(getBaseContext(), TAG, "WidgetIconService is still not bound");
                weatherByvOiceUnsentMessages.add(msg);
                return;
            }
            //appendLog(getBaseContext(), TAG, "sendMessageToService:");
            weatherByVoiceService.send(msg);
        } catch (RemoteException e) {
            appendLog(context, TAG, e.getMessage(), e);
        } finally {
            weatherByVoiceServiceLock.unlock();
        }
    }

    private boolean checkIfWeatherByVoiceServiceIsNotBound(Context context) {
        if (weatherByVoiceService != null) {
            return false;
        }
        try {
            bindWeatherByVoiceService(context);
        } catch (Exception ie) {
            appendLog(context, TAG, "currentWeatherServiceIsNotBound interrupted:", ie);
        }
        return (weatherByVoiceService == null);
    }

    private void bindWeatherByVoiceService(Context context) {
        context.getApplicationContext().bindService(
                new Intent(context.getApplicationContext(), WeatherByVoiceService.class),
                weatherByVoiceServiceConnection,
                Context.BIND_AUTO_CREATE);
    }

    private void unbindWeatherByVoiceService(Context context) {
        if (weatherByVoiceService == null) {
            return;
        }
        context.getApplicationContext().unbindService(weatherByVoiceServiceConnection);
    }

    private ServiceConnection weatherByVoiceServiceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder binderService) {
            weatherByVoiceService = new Messenger(binderService);
            weatherByVoiceServiceLock.lock();
            try {
                while (!weatherByvOiceUnsentMessages.isEmpty()) {
                    weatherByVoiceService.send(weatherByvOiceUnsentMessages.poll());
                }
            } catch (RemoteException e) {
                //appendLog(getBaseContext(), TAG, e.getMessage(), e);
            } finally {
                weatherByVoiceServiceLock.unlock();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
            weatherByVoiceService = null;
        }
    };
}