#ifndef BLUETOOTH_IMPL_H
#define BLUETOOTH_IMPL_H

#include "Bluetooth.h"
#include <boost/uuid/uuid_io.hpp>
#include <cstring>

#define BLUETOOTH_UUID_INITIALIZER \
    { \
        0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, \
        0x10, 0x00, \
        0x80, 0x00, \
        0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb \
    };

namespace trygvis {
namespace bluetooth {

typedef boost::uuids::uuid uuid_t;
template<class t>
using o = boost::optional<t>;

class DefaultBluetoothGattCharacteristic : public BluetoothGattCharacteristic {
public:
    DefaultBluetoothGattCharacteristic(BluetoothGattService &service, uint16_t handle, uuid_t uuid, uint8_t properties, uint16_t valueHandle)
            : service(service), handle(handle), uuid(uuid), properties(properties), valueHandle(valueHandle) {
    }

    virtual ~DefaultBluetoothGattCharacteristic() {
    }

    BluetoothGattService &getService() const {
        return service;
    }

    uint16_t getHandle() const {
        return handle;
    }

    const uuid_t getUuid() const {
        return uuid;
    }

    uint8_t getProperties() const {
        return properties;
    }

    uint16_t getValueHandle() const {
        return valueHandle;
    }

protected:
    BluetoothGattService &service;
    uint16_t handle;
    uuid_t uuid;
    uint8_t properties;
    uint16_t valueHandle;
};

class DefaultBluetoothGattService : public BluetoothGattService {
public:
    DefaultBluetoothGattService(BluetoothDevice &device, const uuid_t uuid, const uint16_t handle, const uint16_t endGroupHandle)
            : device(device), uuid(uuid), handle(handle), endGroupHandle(endGroupHandle) {
        DF;
    }

    virtual ~DefaultBluetoothGattService() {
        DF;
        removeCharacteristics();
    }

    virtual BluetoothDevice &getDevice() const {
        return device;
    }

    virtual uuid_t getUuid() const {
        return uuid;
    }

    virtual uint16_t getHandle() const {
        return handle;
    }

    virtual uint16_t getEndGroupHandle() const {
        return endGroupHandle;
    }

    virtual const vector<BluetoothGattCharacteristic *> getCharacteristics() const {
        return characteristics;
    }

    virtual void addCharacteristic(BluetoothGattCharacteristic *characteristic) {
        characteristics.push_back(characteristic);
    }

    virtual const o<BluetoothGattCharacteristic *> findCharacteristic(uuid_t uuid) const {
        for (auto c: characteristics) {
            if (memcmp(c->getUuid().data, uuid.data, 16) == 0) {
                return o<BluetoothGattCharacteristic *>(c);
            }
        }

        return o<BluetoothGattCharacteristic *>();
    }

protected:
    BluetoothDevice &device;
    const uuid_t uuid;
    const uint16_t handle;
    const uint16_t endGroupHandle;
    vector<BluetoothGattCharacteristic *> characteristics;

    void removeCharacteristics() {
        DF;
        for (auto &c: characteristics) {
            delete c;
        }
        characteristics.clear();
    }
};

template<class A>
class DefaultBluetoothDevice : public BluetoothDevice {
public:

    virtual Mac const &getMac() override {
        return mac;
    }

    virtual A &getAdapter() override {
        return adapter;
    }

protected:
    DefaultBluetoothDevice(A &adapter, Mac &mac) :
            adapter(adapter), mac(mac) {
        DF;
    }

    virtual ~DefaultBluetoothDevice() {
        DF;
        removeServices();
    }

    void removeServices() {
        for (auto s: services) {
            delete s;
        }
        services.clear();
    }

    A &adapter;
    Mac &mac;
    vector<BluetoothGattService *> services;
};

template<class _D>
class DefaultBluetoothGatt : public BluetoothGatt {
public:
    virtual _D &getDevice() const {
        return device;
    }

    virtual const vector<BluetoothGattService *> getServices() const {
        return services;
    };

    virtual const o<BluetoothGattService *> findService(uuid_t uuid) const {
        for (auto s: services) {
            if (memcmp(s->getUuid().data, uuid.data, 16) == 0) {
                return o<BluetoothGattService *>(s);
            }
        }

        return o<BluetoothGattService *>();
    }

    virtual void addService(BluetoothGattService *service) {
        services.push_back(service);
    }

protected:
    DefaultBluetoothGatt(_D &device) : device(device) {
        DF;
    }

    virtual ~DefaultBluetoothGatt() {
        DF;
        removeServices();
    }

    void removeServices() {
        for (auto s: services) {
            delete s;
        }
        services.clear();
    }

    _D &device;
    vector<BluetoothGattService *> services;
};

BluetoothAdapter &getAdapterImpl(int hciDevice);

void shutdownImpl();

}
};

#endif