//! \~\file pibroadcast.h
//! \~\ingroup IO-Utils
//! \~\brief
//! \~english Multi-interface UDP broadcast, multicast and loopback helper
//! \~russian Вспомогательный класс для UDP broadcast, multicast и 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 "piethernet.h"
#include "piethutilbase.h"
#include "pip_io_utils_export.h"


//! \~\ingroup IO-Utils
//! \~\brief
//! \~english Multi-channel sender and receiver over multicast, broadcast and loopback endpoints.
//! \~russian Многоканальный отправитель и приемник через multicast-, broadcast- и loopback-конечные точки.
class PIP_IO_UTILS_EXPORT PIBroadcast
	: public PIThread
	, public PIEthUtilBase {
	PIOBJECT_SUBCLASS(PIBroadcast, PIThread);

public:
	//! \~english Transport channels that may be enabled independently.
	//! \~russian Транспортные каналы, которые можно включать независимо.
	enum Channel {
		Multicast = 0x01 /** \~english Use multicast addresses \~russian Использовать multicast-адреса */,
		Broadcast = 0x02 /** \~english Use broadcast addresses \~russian Использовать broadcast-адреса */,
		Loopback  = 0x04 /** \~english Use loopback addresses \~russian Использовать loopback-адреса */,
		All       = 0xFFFF /** \~english Use all channels \~russian Использовать все каналы */
	};

	//! \~english Bitmask of enabled \a Channel values.
	//! \~russian Битовая маска включенных значений \a Channel.
	typedef PIFlags<Channel> Channels;

	//! \~english Constructs broadcaster, optionally in send-only mode.
	//! \~russian Создает broadcaster, при необходимости в режиме только отправки.
	//! \~\details
	//! \~english
	//! When "send_only" is false, receive sockets are initialized too.
	//! \~russian
	//! Если "send_only" равно false, также инициализируются приемные сокеты.
	PIBroadcast(bool send_only = false);

	//! \~english Destroys broadcaster and owned transport endpoints.
	//! \~russian Уничтожает broadcaster и принадлежащие ему транспортные конечные точки.
	~PIBroadcast();

	//! \~english Sets enabled channels and schedules reinitialization.
	//! \~russian Устанавливает включенные каналы и планирует переинициализацию.
	void setChannels(Channels ch);

	//! \~english Returns enabled channels.
	//! \~russian Возвращает включенные каналы.
	Channels channels() const { return _channels; }

	//! \~english Returns whether this instance is send-only.
	//! \~russian Возвращает, работает ли экземпляр только на отправку.
	bool isSendOnly() const { return _send_only; }


	//! \~english Sets multicast group IP and schedules reinitialization.
	//! \~russian Устанавливает IP multicast-группы и планирует переинициализацию.
	void setMulticastGroup(const PIString & mg);

	//! \~english Returns multicast group IP.
	//! \~russian Возвращает IP multicast-группы.
	PIString multicastGroup() const { return mcast_address.ipString(); }

	//! \~english Sets multicast port and schedules reinitialization.
	//! \~russian Устанавливает multicast-порт и планирует переинициализацию.
	void setMulticastPort(ushort port);

	//! \~english Returns multicast port.
	//! \~russian Возвращает multicast-порт.
	ushort multicastPort() const { return mcast_address.port(); }

	//! \~english Sets full multicast address and schedules reinitialization.
	//! \~russian Устанавливает полный multicast-адрес и планирует переинициализацию.
	void setMulticastAddress(const PINetworkAddress & addr);

	//! \~english Returns multicast endpoint address.
	//! \~russian Возвращает адрес multicast-конечной точки.
	PINetworkAddress multicastAddress() const { return mcast_address; }


	//! \~english Sets broadcast port and schedules reinitialization.
	//! \~russian Устанавливает broadcast-порт и планирует переинициализацию.
	void setBroadcastPort(ushort port);

	//! \~english Returns broadcast port.
	//! \~russian Возвращает broadcast-порт.
	ushort broadcastPort() { return bcast_port; }


	//! \~english Sets first loopback port and schedules reinitialization.
	//! \~russian Устанавливает первый loopback-порт и планирует переинициализацию.
	void setLoopbackPort(ushort port);

	//! \~english Returns first loopback port.
	//! \~russian Возвращает первый loopback-порт.
	ushort loopbackPort() { return lo_port; }

	//! \~english Sets number of loopback ports and schedules reinitialization.
	//! \~russian Устанавливает число loopback-портов и планирует переинициализацию.
	void setLoopbackPortsCount(int count);

	//! \~english Returns number of loopback ports.
	//! \~russian Возвращает число loopback-портов.
	int loopbackPortsCount() const { return lo_pcnt; }


	//! \~english Starts receiving on all initialized channels.
	//! \~russian Запускает прием на всех инициализированных каналах.
	void startRead();

	//! \~english Stops receiving on all initialized channels.
	//! \~russian Останавливает прием на всех инициализированных каналах.
	void stopRead();

	//! \~english Rebuilds transport endpoints for current \a PIEthernet::allAddresses().
	//! \~russian Пересоздает транспортные конечные точки для текущего списка \a PIEthernet::allAddresses().
	void reinit();


	//! \~english Sends one packet through all enabled channels.
	//! \~russian Отправляет один пакет через все включенные каналы.
	void send(const PIByteArray & data);

	//! \events
	//! \{

	//! \fn void receiveEvent(PIByteArray data)
	//! \~english Emitted when a packet is received on any active channel.
	//! \~russian Генерируется при получении пакета на любом активном канале.
	EVENT1(receiveEvent, PIByteArray, data);

	//! \}

protected:
	//! \~english Called when a packet is received.
	//! \~russian Вызывается при получении пакета.
	virtual void received(PIByteArray data) {}

	//! \~english Called after local interface address list changes.
	//! \~russian Вызывается после изменения списка адресов локальных интерфейсов.
	virtual void addressesChanged() {}

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

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

#endif // PIBROADCAST_H
