#ifndef BROADCAST_LISTENER_H
#define BROADCAST_LISTENER_H

#include <vector>
#include <string>
#include <memory>
#include <atomic>
#include <boost/asio/ip/udp.hpp>
#include <user/senseApiDesc.h>
#include "device.h"

namespace livoxProto1 {
namespace comms {

/**		EXPLANATION:
 * This class merely listens for UDP bcast dgrams on the designated listening
 * port. It then builds a list of client device IP addrs that it has heard from.
 * It doesn't connect to them or signal any events to the rest of the lib,
 * except in the case that a device which the lib is using has gone away.
 *
 * Other than that, its role is to tell the lib which devices are available
 * on the network.
 */
#define UDP_BCAST_MSG_BUFFER_NBYTES		(1024)

class BroadcastListener
{
public:
	BroadcastListener(
		const std::shared_ptr<smo::ComponentThread>& componentThread,
		uint16_t listeningPort=55000, uint16_t connectPort=65000);

	~BroadcastListener() = default;

	typedef void (DeviceGoneAwayCbFn)(const DiscoveredDevice &device);
	void setDeviceGoneAwayCb(DeviceGoneAwayCbFn *cb)
		{ deviceGoneAwayCb = cb; }

	bool deviceExists(const std::string &deviceIdentifier) const
		{ return getDevice(deviceIdentifier) != nullptr; }

	std::shared_ptr<DiscoveredDevice>
	getDevice(const std::string &deviceIdentifier) const;

	void start(void);
	void stop(void);

	void broadcastMsgInd(
		const boost::system::error_code& ec, std::size_t bytes_received);

private:
	void startReceive(void);

private:
	std::shared_ptr<smo::ComponentThread> componentThread;
	/**		EXPLANATION:
	 * The Livox proto says that client devices will spam broadcast UDP
	 * dgrams to us on the listening port. We can then use the source IP from
	 * the bcast dgram to figure out the client device's IP addr. Then we
	 * should send a connect dgram to the connect port. This will tell the
	 * client device our IP addr.
	 */
	uint16_t listeningPort, connectPort;
	DeviceGoneAwayCbFn *deviceGoneAwayCb;
	std::vector<std::shared_ptr<DiscoveredDevice>> discoveredDevices;

	boost::asio::ip::udp::socket socket;
	boost::asio::ip::udp::endpoint listeningEndpoint, senderEndpoint;
	std::atomic<bool> isListening;

	uint8_t bcastMsgRecvBuffer[UDP_BCAST_MSG_BUFFER_NBYTES];
};

} // namespace comms
} // namespace livoxProto1

#endif // BROADCAST_LISTENER_H
