/*! \file pibroadcast.h
 * \ingroup IO-Utils
 * \~\brief
 * \~english Broadcast for all interfaces, including loopback
 * \~russian Широкое вещание на все интерфейсы, включая loopback
 */
/*
	PIP - Platform Independent Primitives
	Broadcast for all interfaces, including loopback
	Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIBROADCAST_H
#define PIBROADCAST_H

#include "pip_io_utils_export.h"
#include "piethutilbase.h"
#include "piethernet.h"


class PIP_IO_UTILS_EXPORT PIBroadcast: public PIThread, public PIEthUtilBase {
	PIOBJECT_SUBCLASS(PIBroadcast, PIThread);
public:

	//! %PIBroadcast channels, can be used independently
	enum Channel {
		Multicast /** Use multicast addresses */ = 0x01,
		Broadcast /** Use broadcast addresses */ = 0x02,
		Loopback  /** Use loopback addresses */ = 0x04,
		All /** Use all channels */ = 0xFFFF,
	};

	typedef PIFlags<Channel> Channels;

	/** Contructs %PIBroadcast, if \"send_only\" not set
	 * all PIEthernets will be binded to receive data
	 * */
	PIBroadcast(bool send_only = false);

	~PIBroadcast();

	//! Set channels to \"ch\" and queue to reinit
	void setChannels(Channels ch);

	//! Returns channels
	Channels channels() const {return _channels;}

	//! Returns if is send_only
	bool isSendOnly() const {return _send_only;}


	//! Set multicast IP to \"mg\" and queue to reinit
	void setMulticastGroup(const PIString & mg);

	//! Returns multicast IP
	PIString multicastGroup() const {return mcast_address.ipString();}

	//! Set multicast port to \"port\" and queue to reinit
	void setMulticastPort(ushort port);

	//! Returns multicast port
	ushort multicastPort() const {return mcast_address.port();}

	//! Set multicast address to \"addr\" and queue to reinit
	void setMulticastAddress(const PIEthernet::Address & addr);

	//! Returns multicast address
	PIEthernet::Address multicastAddress() const {return mcast_address;}


	//! Set broadcast port to \"port\" and queue to reinit
	void setBroadcastPort(ushort port);

	//! Returns broadcast port
	ushort broadcastPort() {return bcast_port;}


	//! Set loopback start port to \"port\" and queue to reinit
	void setLoopbackPort(ushort port);

	//! Returns loopback start port
	ushort loopbackPort() {return lo_port;}

	//! Set loopback ports count to \"count\" and queue to reinit
	void setLoopbackPortsCount(int count);

	//! Returns loopback ports count
	int loopbackPortsCount() const {return lo_pcnt;}


	//! If not send_only starts all threaded reads
	void startRead();

	//! Stop all threaded reads
	void stopRead();

	//! Reinit all PIEthernets with current \a PIEthernet::allAddresses()
	void reinit();


	//! Send packet
	void send(const PIByteArray & data);

	EVENT1(receiveEvent, PIByteArray, data);

	//! \events
	//! \{

		//! \fn void receiveEvent(PIByteArray data)
		//! \brief Raise on packet received

	//! \}

protected:

	//! Called when packet received
	virtual void received(PIByteArray data) {}

	//! Called when addresses are changed
	virtual void addressesChanged() {}

private:
	EVENT_HANDLER2(void, mcastRead, const uchar * , data, ssize_t, size);
	void destroyAll();
	void initAll(PIVector<PIEthernet::Address> al);
	void run() override;

	Channels _channels;
	PIEthernet::Address mcast_address;
	PIMutex mcast_mutex;
	PIVector<PIEthernet*> eth_mcast;
	PIEthernet * eth_lo;
	PIVector<PIEthernet::Address> prev_al;
	ushort lo_port, bcast_port;
	int lo_pcnt;
	bool _started, _send_only, _reinit;

};

#endif // PIBROADCAST_H
