package com.punchthrough.bean.sdk.util;

import android.os.Looper;
import android.test.AndroidTestCase;

import com.punchthrough.bean.sdk.Bean;
import com.punchthrough.bean.sdk.BeanDiscoveryListener;
import com.punchthrough.bean.sdk.BeanListener;
import com.punchthrough.bean.sdk.BeanManager;
import com.punchthrough.bean.sdk.message.BeanError;
import com.punchthrough.bean.sdk.message.ScratchBank;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;

public class BeanTestCase extends AndroidTestCase {

    private LooperRunner lr = new LooperRunner(BeanManager.getInstance().getHandler().getLooper());

    private final CountDownLatch connectLatch = new CountDownLatch(1);
    private final CountDownLatch disconnectLatch = new CountDownLatch(1);

    private class LooperRunner extends Thread {

        private Looper looper;

        public LooperRunner(Looper looper) {
            this.looper = looper;
        }

        public void run() {
            Looper.prepare();
            Looper.loop();
        }

    }

    protected void setUp() {
        lr.start();
    }

    private final BeanListener beanConnectionListener = new BeanListener() {
        @Override
        public void onConnected() {
            connectLatch.countDown();
        }

        @Override
        public void onConnectionFailed() {
            fail("Connection failed");
        }

        @Override
        public void onDisconnected() {
            disconnectLatch.countDown();
        }

        @Override
        public void onSerialMessageReceived(byte[] data) {}

        @Override
        public void onScratchValueChanged(ScratchBank bank, byte[] value) {}

        @Override
        public void onError(BeanError error) {
            fail(error.toString());
        }
    };

    protected void ensureConnected(Bean bean, CountDownLatch connectLatch) throws InterruptedException {
        /**
         * This method assumes that `connectLatch` is getting counted down by
         * the .onConnected() callback in a BeanListener.
         */

        connectLatch.await(20, TimeUnit.SECONDS);
        assertThat(bean.isConnected()).isTrue();
    }

    protected void ensureDisconnected(Bean bean, CountDownLatch disconnectLatch) throws InterruptedException {
        /**
         * This method assumes that `disconnectLatch` is getting counted down by
         * the .onDisconnected() callback in a BeanListener.
         */

        disconnectLatch.await(20, TimeUnit.SECONDS);
        assertThat(bean.isConnected()).isFalse();
    }

    protected void synchronousConnect(Bean bean) throws InterruptedException {
        /**
         * Synchronously connect to a Bean
         */

        if (!bean.isConnected()) {
            bean.connect(getContext(), beanConnectionListener);
            ensureConnected(bean, connectLatch);
        }
    }

    protected void synchronousDisconnect(Bean bean) throws InterruptedException {
        /**
         * Synchronously disconnect from a Bean
         */

        if (bean.isConnected()) {
            bean.disconnect();
            ensureDisconnected(bean, disconnectLatch);
        }
    }

    protected static List<Bean> discoverBeans(int num) throws InterruptedException {
        final CountDownLatch beanLatch = new CountDownLatch(num);
        final List<Bean> beans = new ArrayList<>();

        BeanDiscoveryListener listener = new BeanDiscoveryListener() {
            @Override
            public void onBeanDiscovered(Bean bean, int rssi) {
                beans.add(bean);
                beanLatch.countDown();
            }

            @Override
            public void onDiscoveryComplete() {
                beanLatch.countDown();
            }
        };

        boolean startedOK = BeanManager.getInstance().startDiscovery(listener);
        assertThat(startedOK).isTrue();
        beanLatch.await(60, TimeUnit.SECONDS);
        assertThat(beans.size()).isEqualTo(num);
        return beans;
    }

    protected static Bean discoverBean(String name) throws Exception {
        final CountDownLatch beanLatch = new CountDownLatch(1);
        final List<Bean> beans = new ArrayList<>();

        final String targetName = name;

        BeanDiscoveryListener listener = new BeanDiscoveryListener() {
            @Override
            public void onBeanDiscovered(Bean bean, int rssi) {
                if (bean.getDevice().getName().equals(targetName)) {
                    System.out.println("[BeanUtils] Found Bean by name: " + targetName);
                    beans.add(bean);
                    beanLatch.countDown();
                }
            }

            @Override
            public void onDiscoveryComplete() {
                System.out.println("[BeanUtils] Discovery Complete!");
                beanLatch.countDown();
            }
        };

        boolean startedOK = BeanManager.getInstance().startDiscovery(listener);
        assertThat(startedOK).isTrue();
        beanLatch.await(60, TimeUnit.SECONDS);
        if (beans.isEmpty()) {
            throw new Exception("No bean named: " + name);
        }
        return beans.get(0);
    }

}

@@ -338,16 +338,120 @@
 nError;%0A
+import com.punchthrough.bean.sdk.message.Callback;%0Aimport com.punchthrough.bean.sdk.message.DeviceInfo;%0A
 import c
@@ -3392,23 +3392,16 @@
 otected 
-static 
 List%3CBea
@@ -3448,32 +3448,32 @@
 ptedException %7B%0A
+
         final Co
@@ -4213,23 +4213,16 @@
 otected 
-static 
 Bean dis
@@ -5314,32 +5314,32 @@
 ame);%0A        %7D%0A
-
         return b
@@ -5358,10 +5358,730 @@
 %0A    %7D%0A%0A
+    protected DeviceInfo getDeviceInformation(Bean bean) throws Exception %7B%0A        final CountDownLatch deviceInfoLatch = new CountDownLatch(1);%0A        final List%3CDeviceInfo%3E deviceInfos = new ArrayList%3C%3E();%0A%0A        bean.readDeviceInfo(new Callback%3CDeviceInfo%3E() %7B%0A            @Override%0A            public void onResult(DeviceInfo deviceInfo) %7B%0A                assertThat(deviceInfo).isNotNull();%0A                deviceInfos.add(deviceInfo);%0A                deviceInfoLatch.countDown();%0A            %7D%0A        %7D);%0A%0A        deviceInfoLatch.await(20, TimeUnit.SECONDS);%0A        if (deviceInfos.isEmpty()) %7B%0A            throw new Exception(%22Couldn't get device info%22);%0A        %7D%0A        return deviceInfos.get(0);%0A    %7D%0A%0A
 %7D%0A

make a helper function for getting Device Information