package com.clusterrr.sonyheadphonescontrol;

/*
 * Copyright 2013 two forty four a.m. LLC <http://www.twofortyfouram.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * <http://www.apache.org/licenses/LICENSE-2.0>
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 */

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.util.Log;
import android.widget.Toast;

import com.twofortyfouram.spackle.bundle.BundleScrubber;

import net.dinglisch.android.tasker.TaskerPlugin;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

public final class TaskerFireReceiver extends BroadcastReceiver {
    public static final String TAG = "SonyHeadphonesControl";
    public static final UUID uuid = UUID.fromString("96cc203e-5068-46ad-b32d-e316f5e069ba");
    public static final UUID uuid_alt = UUID.fromString("ba69e0f5-16e3-2db3-ad46-68503e20cc96");
    public static final String ACTION_FIRE_SETTING = "com.twofortyfouram.locale.intent.action.FIRE_SETTING"; //$NON-NLS-1$
    public static final String EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE"; //$NON-NLS-1$
    public static final String EXTRA_STRING_BLURB = "com.twofortyfouram.locale.intent.extra.BLURB"; //$NON-NLS-1$
    public static final String EXTRA_STRING_MODE = "mode";
    public static final String EXTRA_STRING_VOLUME = "volume";
    public static final String EXTRA_STRING_VOICE = "voice";

    @Override
    public void onReceive(final Context context, final Intent intent) {
        if (!ACTION_FIRE_SETTING.equals(intent.getAction()))
            return;

        final Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE);
        BundleScrubber.scrub(bundle);

        boolean voice = false;
        int volume = 0;

        int mode = bundle.getInt(EXTRA_STRING_MODE, 0);
        volume = bundle.getInt(EXTRA_STRING_VOLUME, 20);
        voice = bundle.getBoolean(EXTRA_STRING_VOICE, false);

        execute(context, intent, mode, volume, voice);
    }

    public static void execute(Context context, Intent intent, int mode, int volume, boolean voice) {

        boolean enabled = false;
        int noiseCancelling = 0;
        switch (mode) {
            case 0:
                enabled = false;
                break;
            case 1:
                enabled = true;
                noiseCancelling = 2;
                break;
            case 2:
                enabled = true;
                noiseCancelling = 1;
                break;
            case 3:
                enabled = true;
                noiseCancelling = 0;
        }

        try {
            if (setAmbientSound(context, enabled, noiseCancelling, volume, voice)) {
                if (intent != null)
                    TaskerPlugin.Setting.signalFinish(context, intent, TaskerPlugin.Setting.RESULT_CODE_OK, null);
                else
                    Toast.makeText(context, context.getString(R.string.ok), Toast.LENGTH_SHORT).show();
            } else {
                final String message = context.getString(R.string.headset_not_found);
                if (intent != null) {
                    Bundle vars = new Bundle();
                    vars.putString(TaskerPlugin.Setting.VARNAME_ERROR_MESSAGE, message);
                    TaskerPlugin.Setting.signalFinish(context, intent, TaskerPlugin.Setting.RESULT_CODE_FAILED, vars);
                } else {
                    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            final String message = context.getString(R.string.io_error);
            if (intent != null) {
                Bundle vars = new Bundle();
                vars.putString(TaskerPlugin.Setting.VARNAME_ERROR_MESSAGE, message);
                TaskerPlugin.Setting.signalFinish(context, intent, TaskerPlugin.Setting.RESULT_CODE_FAILED, vars);
            } else {
                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static boolean setAmbientSound(Context context, boolean enabled, int noiseCancelling, int volume, boolean voice) throws IOException, InterruptedException {
        return sendData(context, new byte[]{0x00, 0x00, 0x00, 0x08, 0x68, 0x02, (byte) (enabled ? 0x10 : 0x00), 0x02, (byte) (noiseCancelling), 0x01, (byte) (voice ? 1 : 0), (byte) volume});
    }

    static boolean sendData(Context context, byte[] data) throws IOException, InterruptedException {
        BluetoothDevice headset = null;
        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        BluetoothAdapter adapter = bluetoothManager.getAdapter();

        Set<BluetoothDevice> devices = adapter.getBondedDevices();
        for (BluetoothDevice device : devices) {
            ParcelUuid[] uuids = device.getUuids();

            if(uuids == null)
                continue;

            for (ParcelUuid u : uuids) {
                if (u.toString().equals(uuid.toString()) || u.toString().equals(uuid_alt.toString())) {
                    headset = device;
                    break;
                }
            }
            if (headset != null)
                break;
        }
        if (headset == null) {
//            Log.e(TAG, "Headset not found");
            return false;
        } else {
//            Log.d(TAG, "Headset found: " + headset.getAddress() + " " + headset.getName());
        }

        BluetoothSocket socket = headset.createRfcommSocketToServiceRecord(uuid);
        try {
//            Log.i(TAG, "Socket connected: " + socket.isConnected());
            socket.connect();
//            Log.i(TAG, "Socket connected: " + socket.isConnected());

            byte[] packet = new byte[data.length + 2];
            packet[0] = 0x0c;
            packet[1] = 0;
            for (int j = 0; j < data.length; j++) {
                packet[j + 2] = data[j];
            }
            sendPacket(socket, packet);
            packet[1] = 1;
            sendPacket(socket, packet);

            return true;
        } finally {
            socket.close();
        }
    }

    static void sendPacket(BluetoothSocket socket, byte[] data) throws IOException, InterruptedException {
        OutputStream o = socket.getOutputStream();
        InputStream i = socket.getInputStream();
        byte[] packet = new byte[data.length + 3];
        packet[0] = 0x3e;
        packet[packet.length - 1] = 0x3c;
        byte crc = 0;
        for (int j = 0; j < data.length; j++) {
            crc += data[j];
            packet[j + 1] = data[j];
        }
        packet[packet.length - 2] = crc;
        o.write(packet);

        byte[] buffer = new byte[256];
        Date date = new Date();
        while (new Date().getTime() - date.getTime() < 200) {
            if (i.available() > 0) {
                int r = i.read(buffer);
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < r; j++) {
                    sb.append(String.format(" %02x", buffer[j]));
                }
//                Log.i(TAG, "Read: " + r + " bytes:" + sb);
                break;
            }
            Thread.sleep(50);
        }
    }
}