//! \~\file pistreampacker.h
//! \~\ingroup IO-Utils
//! \~\brief
//! \~english Packet framing helper for byte-stream devices
//! \~russian Вспомогательный класс пакетирования для потоковых устройств
/*
    PIP - Platform Independent Primitives
    Simple packet wrap aroud any PIIODevice
    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 PISTREAMPACKER_H
#define PISTREAMPACKER_H

#include "piethutilbase.h"
#include "piobject.h"
#include "pip_io_utils_export.h"


class PIIODevice;

//! \~\ingroup IO-Utils
//! \~\brief
//! \~english Configuration for %PIStreamPacker packet framing.
//! \~russian Конфигурация пакетирования для %PIStreamPacker.
class PIStreamPackerConfig: public PIEthUtilBase {
	friend class PIStreamPacker;

public:
	//! \~english Constructs configuration with default packet framing parameters.
	//! \~russian Создает конфигурацию с параметрами пакетирования по умолчанию.
	PIStreamPackerConfig() {
		crypt_size              = false;
		aggressive_optimization = true;
		max_packet_size         = 1400;
		packet_sign             = 0xAFBE;
	}

	//! \~english Sets maximum size of one emitted frame chunk.
	//! \~russian Устанавливает максимальный размер одного отправляемого фрагмента.
	void setMaxPacketSize(int max_size) { max_packet_size = max_size; }

	//! \~english Returns maximum size of one emitted frame chunk.
	//! \~russian Возвращает максимальный размер одного отправляемого фрагмента.
	int maxPacketSize() const { return max_packet_size; }

	//! \~english Sets packet signature used to detect frame boundaries.
	//! \~russian Устанавливает сигнатуру пакета для поиска границ кадра.
	void setPacketSign(ushort sign_) { packet_sign = sign_; }

	//! \~english Returns packet signature.
	//! \~russian Возвращает сигнатуру пакета.
	ushort packetSign() const { return packet_sign; }

	//! \~english Enables faster resynchronization on invalid stream data.
	//! \~russian Включает более быструю ресинхронизацию при неверных данных в потоке.
	//! \~\details
	//! \~english
	//! When enabled, %PIStreamPacker drops the whole current read chunk after a
	//! signature mismatch instead of scanning byte by byte.
	//! \~russian
	//! Когда режим включен, %PIStreamPacker отбрасывает весь текущий прочитанный
	//! блок после несовпадения сигнатуры вместо побайтного поиска.
	void setaAggressiveOptimization(bool yes) { aggressive_optimization = yes; }

	//! \~english Returns whether aggressive resynchronization is enabled.
	//! \~russian Возвращает, включена ли агрессивная ресинхронизация.
	bool aggressiveOptimization() const { return aggressive_optimization; }

	//! \~english Returns whether packet size field is encrypted too.
	//! \~russian Возвращает, шифруется ли также поле размера пакета.
	bool cryptSizeEnabled() const { return crypt_size; }

	//! \~english Enables or disables encryption of the packet size field.
	//! \~russian Включает или выключает шифрование поля размера пакета.
	void setCryptSizeEnabled(bool on) { crypt_size = on; }

	//! \~english Returns configuration as a const self-reference.
	//! \~russian Возвращает конфигурацию как константную ссылку на себя.
	const PIStreamPackerConfig & configuration() const { return *this; }

	//! \~english Returns configuration as a mutable self-reference.
	//! \~russian Возвращает конфигурацию как изменяемую ссылку на себя.
	PIStreamPackerConfig & configuration() { return *this; }

	//! \~english Replaces current framing configuration with "config".
	//! \~russian Заменяет текущую конфигурацию пакетирования на "config".
	void setConfiguration(const PIStreamPackerConfig & config) { *this = config; }

private:
	bool crypt_size, aggressive_optimization;
	ushort packet_sign;
	int max_packet_size;
};


//! \~\ingroup IO-Utils
//! \~\brief
//! \~english Simple packet wrapper around any PIIODevice
//! \~russian Простая фрагментация пакетов, использует любой PIIODevice
//! \details
//! \~english Provides packet framing and deframing for data transmission over any PIIODevice implementation
//! \~russian Предоставляет фрагментацию и дефрагментацию пакетов для передачи данных через любую реализацию PIIODevice
class PIP_IO_UTILS_EXPORT PIStreamPacker
	: public PIObject
	, public PIStreamPackerConfig {
	PIOBJECT(PIStreamPacker)

public:
	//! \~english Constructs packer and optionally binds it to "dev".
	//! \~russian Создает упаковщик и при необходимости привязывает его к "dev".
	PIStreamPacker(PIIODevice * dev = nullptr);

	//! \~english Returns number of payload bytes collected for current packet.
	//! \~russian Возвращает число байтов полезной нагрузки, собранных для текущего пакета.
	int receivePacketProgress() const { return packet.size_s(); }

	//! \~english Clears buffered stream state and incomplete packet data.
	//! \~russian Очищает буферизованное состояние потока и данные незавершенного пакета.
	void clear();

	//! \~english Fragments data and raises \a sendRequest() for each fragment
	//! \~russian Фрагментирует данные и вызывает \a sendRequest() для каждого фрагмента
	void send(const PIByteArray & data);

	//! \~english Feeds received bytes into the unpacker.
	//! \~russian Передает полученные байты в распаковщик.
	//! \~\details
	//! \~english Calls \a packetReceived() and emits \a packetReceiveEvent() when a packet is complete.
	//! \~russian Вызывает \a packetReceived() и генерирует \a packetReceiveEvent(), когда пакет собран полностью.
	void received(const PIByteArray & data);


	//! \~english Binds "dev" read and write callbacks to this packer.
	//! \~russian Связывает обратные вызовы чтения и записи устройства "dev" с этим упаковщиком.
	//! \~\details
	//! \~english Connects \a PIIODevice::threadedReadEvent() to \a received() and \a sendRequest() to \a PIIODevice::write().
	//! \~russian Подключает \a PIIODevice::threadedReadEvent() к \a received(), а \a sendRequest() к \a PIIODevice::write().
	void assignDevice(PIIODevice * dev);


	//! \handlers
	//! \{

	//! \fn void received(const uchar * readed, ssize_t size)
	//! \~english Handler overload for raw byte buffers from \a PIIODevice::threadedReadEvent().
	//! \~russian Перегрузка обработчика для сырых буферов байтов из \a PIIODevice::threadedReadEvent().
	EVENT_HANDLER2(void, received, const uchar *, readed, ssize_t, size);

	//! \}

	//! \events
	//! \{

	//! \fn void packetReceiveEvent(PIByteArray data)
	//! \~english Emitted when a complete packet is received.
	//! \~russian Генерируется, когда пакет полностью принят.
	EVENT1(packetReceiveEvent, PIByteArray &, data);

	//! \fn void startPacketReceive(int size)
	//! \~english Emitted when a new packet with payload size "size" starts.
	//! \~russian Генерируется при начале приема нового пакета с размером полезной нагрузки "size".
	EVENT1(startPacketReceive, int, size);

	//! \fn void endPacketReceive()
	//! \~english Emitted after the current packet has been fully received.
	//! \~russian Генерируется после полного приема текущего пакета.
	EVENT0(endPacketReceive);

	//! \fn void sendRequest(PIByteArray data)
	//! \~english Emitted by \a send() for every framed chunk ready to write to the device.
	//! \~russian Генерируется методом \a send() для каждого готового к записи во устройство фрагмента.
	EVENT1(sendRequest, PIByteArray, data);

	//! \}

protected:
	//! \~english Called after a packet payload has been reconstructed.
	//! \~russian Вызывается после восстановления полезной нагрузки пакета.
	virtual void packetReceived(PIByteArray data) {}

private:
	uint sizeCryptedSize();

	PIByteArray stream, packet;
	int packet_size;
	mutable PIMutex prog_s_mutex, prog_r_mutex;
};

#endif // PISTREAMPACKER_H
