package org.altbeacon.beacon;

import android.content.Context;

import org.altbeacon.beacon.logging.LogManager;
import org.altbeacon.beacon.logging.Loggers;
import org.altbeacon.beacon.utils.UrlBeaconUrlCompressor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.RuntimeEnvironment;

import java.util.Arrays;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;

@Config(sdk = 28)

/**
 * Created by dyoung on 2/6/15.
 * This test verifies that a beacon can be parsed that uses a Gatt UUID
 */
@RunWith(RobolectricTestRunner.class)
public class GattBeaconTest {
    @Test
    public void testDetectsGattBeacon() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        System.err.println("verbose logging:"+LogManager.isVerboseLoggingEnabled());
        byte[] bytes = hexStringToByteArray("020106030334121516341200e72f234454f4911ba9ffa6000000000001000000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=1234,m:2-2=00,p:3-3:-41,i:4-13,i:14-19");
        assertNotNull("Service uuid parsed should not be null", parser.getServiceUuid());
        Beacon gattBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("GattBeacon should be not null if parsed successfully", gattBeacon);
        assertEquals("id1 should be parsed", "0x2f234454f4911ba9ffa6", gattBeacon.getId1().toString());
        assertEquals("id2 should be parsed", "0x000000000001", gattBeacon.getId2().toString());
        assertEquals("serviceUuid should be parsed", 0x1234, gattBeacon.getServiceUuid());
        assertEquals("txPower should be parsed", -66, gattBeacon.getTxPower());
    }

    @Test
    public void testDetectsGattBeacon2MaxLength() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        byte[] bytes = hexStringToByteArray("020106030334121616341210ec007261646975736e6574776f726b7373070000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=1234,m:2-2=10,p:3-3:-41,i:4-20v");
        Beacon gattBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("GattBeacon should be not null if parsed successfully", gattBeacon);
        assertEquals("GattBeacon identifier length should be proper length",
                17,
                gattBeacon.getId1().toByteArray().length);

    }

    @Test
    public void testDetectsGattBeacon2WithShortIdentifier() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        LogManager.d("GattBeaconTest", "Parsing short packet");
        byte[] bytes = hexStringToByteArray("020106030334121516341210ec007261646975736e6574776f726b7307000000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=1234,m:2-2=10,p:3-3:-41,i:4-20v");
        Beacon gattBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("GattBeacon should be not null if parsed successfully", gattBeacon);
        assertEquals("GattBeacon identifier length should be adjusted smaller if packet is short",
                     16,
                     gattBeacon.getId1().toByteArray().length);
        assertEquals("GattBeacon identifier should have proper first byte",
                (byte)0x00,
                gattBeacon.getId1().toByteArray()[0]);
        assertEquals("GattBeacon identifier should have proper second to last byte",
                (byte) 0x73,
                gattBeacon.getId1().toByteArray()[14]);
        assertEquals("GattBeacon identifier should have proper last byte",
                (byte)0x07,
                gattBeacon.getId1().toByteArray()[15]);

    }


    @Test
    public void testDetectsEddystoneUID() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        byte[] bytes = hexStringToByteArray("0201060303aafe1516aafe00e700010203040506070809010203040506000000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT);
        Beacon eddystoneUidBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("Eddystone-UID should be not null if parsed successfully", eddystoneUidBeacon);
    }


    @Test
    public void testDetectsGattBeaconWithCnn() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        LogManager.d("GattBeaconTest", "Parsing short packet");
        byte[] bytes = hexStringToByteArray("020106030334120a16341210ed00636e6e070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=1234,m:2-2=10,p:3-3:-41,i:4-20v");
        LogManager.d("xxx", "------");
        Beacon gattBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("GattBeacon should be not null if parsed successfully", gattBeacon);
        assertEquals("GattBeacon identifier length should be adjusted smaller if packet is short",
                5,
                gattBeacon.getId1().toByteArray().length);
    }

    @Test
    public void testBeaconAdvertisingBytes() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        Context context = RuntimeEnvironment.application;


        Beacon beacon = new Beacon.Builder()
                .setId1("0x454452e29735323d81c0")
                .setId2("0x060504030201")
                .setDataFields(Arrays.asList(0x25l))
                .setTxPower(-59)
                .build();
        // TODO: need to use something other than the d: prefix here for an internally generated field
        BeaconParser beaconParser = new BeaconParser()
                .setBeaconLayout("s:0-1=0123,m:2-2=00,d:3-3,p:4-4,i:5-14,i:15-20");
        byte[] data = beaconParser.getBeaconAdvertisementData(beacon);
//        BeaconTransmitter beaconTransmitter = new BeaconTransmitter(context, beaconParser);
        // TODO: can't actually start transmitter here because Robolectric does not support API 21

        assertEquals("Data should be 19 bytes long", 19, data.length);
        String byteString = "";
        for (int i = 0; i < data.length; i++) {
            byteString += String.format("%02X", data[i]);
            byteString += " ";
        }
        assertEquals("Advertisement bytes should be as expected", "00 25 C5 45 44 52 E2 97 35 32 3D 81 C0 06 05 04 03 02 01 ", byteString);
    }

    @Test
    public void testDetectsUriBeacon() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        //"https://goo.gl/hqBXE1"
        byte[] bytes = {2, 1, 4, 3, 3, (byte) 216, (byte) 254, 19, 22, (byte) 216, (byte) 254, 0, (byte) 242, 3, 103, 111, 111, 46, 103, 108, 47, 104, 113, 66, 88, 69, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=fed8,m:2-2=00,p:3-3:-41,i:4-21v");
        LogManager.d("xxx", "------");
        Beacon uriBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNotNull("UriBeacon should be not null if parsed successfully", uriBeacon);
        assertEquals("UriBeacon identifier length should be correct",
                14,
                uriBeacon.getId1().toByteArray().length);
        String urlString = UrlBeaconUrlCompressor.uncompress(uriBeacon.getId1().toByteArray());
        assertEquals("URL should be decompressed successfully", "https://goo.gl/hqBXE1", urlString);
    }


    @Test
    public void doesNotCrashOnMalformedEddystoneBeacon() {
        org.robolectric.shadows.ShadowLog.stream = System.err;
        LogManager.setLogger(Loggers.verboseLogger());
        LogManager.setVerboseLoggingEnabled(true);
        LogManager.d("GattBeaconTest", "Parsing malformed packet");
        byte[] bytes = hexStringToByteArray("0201060303aafe0416aafe100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
        BeaconParser parser = new BeaconParser().setBeaconLayout("s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v");
        LogManager.d("xxx", "------");
        Beacon gattBeacon = parser.fromScanData(bytes, -55, null, 123456L);
        assertNull("GattBeacon should be null when not parsed successfully", gattBeacon);
    }




    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

}