#include "ble/ByteBuffer.h"

#define BOOST_TEST_MODULE "ByteBuffer"

#include <boost/test/unit_test.hpp>

#define checkBuffer(buffer, capacity, size, cursor) \
    if(false) {cout << "capacity=" << buffer.getCapacity() << ", size=" << buffer.getSize() << ", cursor=" << buffer.getCursor();} \
    BOOST_CHECK_EQUAL(buffer.getSize(), size); \
    BOOST_CHECK_EQUAL(buffer.getCapacity(), capacity); \
    BOOST_CHECK_EQUAL(buffer.getCursor(), cursor)

using namespace std;

class Bytes {
public:
    Bytes(size_t size) : capacity(size) {
        uint8_t *secret = new uint8_t[size + 0x100];
        auto tmp = (uint8_t *) (((uint64_t) &secret[0x100]) & 0xffffffffffffff00);
        for (int i = 0; i < size; i++) {
            tmp[i] = (uint8_t) i;
        }

        bytes = shared_ptr<uint8_t>(tmp, [secret](uint8_t *) {
            delete[]secret;
        });
    }
    shared_ptr<uint8_t> bytes;
    size_t capacity;
};

BOOST_AUTO_TEST_CASE(empty_buffer) {
    Bytes b(0);
    ByteBuffer buffer(b.bytes, b.capacity);

    checkBuffer(buffer, 0, 0, 0);

    try {
        buffer.read8();
        BOOST_FAIL("Expected exception");
    } catch (ByteBufferException e) {
    }
}

BOOST_AUTO_TEST_CASE(basic) {
    Bytes b(1000);
    ByteBuffer buffer(b.bytes, 1000, 1000);
    checkBuffer(buffer, 1000, 1000, 0);

    BOOST_CHECK_EQUAL(buffer.read8(), 0);
    checkBuffer(buffer, 1000, 1000, 1);

    for (int i = 1; i < b.capacity; i++) {
        BOOST_CHECK_EQUAL(buffer.read8(), b.bytes.get()[i]);
    }
}

BOOST_AUTO_TEST_CASE(setCursor) {
    Bytes b(1000);
    ByteBuffer buffer(b.bytes, 1000, 10, 0);
    checkBuffer(buffer, 1000, 10, 0);

    BOOST_CHECK_EQUAL(buffer.read8(), 0);
    checkBuffer(buffer, 1000, 10, 1);

    buffer.setCursor(0);
    checkBuffer(buffer, 1000, 10, 0);

    BOOST_CHECK_EQUAL(buffer.read8(), 0);
    checkBuffer(buffer, 1000, 10, 1);

    buffer.setCursor(9);
    checkBuffer(buffer, 1000, 10, 9);
}

BOOST_AUTO_TEST_CASE(view) {
    Bytes b(1000);
    ByteBuffer buffer(b.bytes, b.capacity, 10, 0);

    BOOST_CHECK_EQUAL(buffer.read8(), 0);
    ByteBuffer view1 = buffer.view();
    checkBuffer(view1, 9, 9, 0);

    BOOST_CHECK_EQUAL(view1.read8(), 1);
    BOOST_CHECK_EQUAL(view1.read8(), 2);

    ByteBuffer view2 = view1.view();
    checkBuffer(view2, 7, 7, 0);

    BOOST_CHECK_EQUAL(view1.read8(), 3);
}