/*
 * ll_hsusart_impl.h
 *
 *  Created on: Oct 29, 2019
 *      Author: abody
 */
#include <string.h>
#include "f4ll/packetusart.h"

namespace f4ll {

template<typename T> static inline T RoundUpTo4(T input)
{
	return (input + 3) & (((T)-1) - 3);
}


PacketUsart::PacketUsart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t streamRx, uint32_t streamTx)
: UsartCore(usart, dma, streamRx, streamTx)
{
	CrcHandler::Instance().AttachSlot(m_crcSlot);
}


void PacketUsart::RxProcessed(bool second)
{
	m_rxBuffers[second].busy = false;
	m_rxBuffers[second].error = false;
}


void PacketUsart::SetCallback(IHsUsartCallback *callback, uintptr_t callbackParam)
{
	m_userCallback = callback;
	m_userCallbackParam = callbackParam;
}


void PacketUsart::PostPacket(uint8_t const *payload, uint8_t length, bool waitForCrcQueue)
{
	uint16_t payloadLength = RoundUpTo4((uint16_t)length);

	BuildHeader(m_txBuffer.packet, m_txSerialNo++, length);
	if(payload)
		memcpy(m_txBuffer.packet.payload, payload, length);
	m_txBuffer.requestedLength = sizeof(m_txBuffer.packet.header) + payloadLength + sizeof(uint32_t);
	m_txBuffer.busy = true;
	m_txBuffer.error = false;

	CrcHandler::Instance().Enqueue(m_crcSlot, 0, &m_txBuffer.packet, sizeof(PacketHeader) + payloadLength,
			nullptr, reinterpret_cast<uintptr_t>(m_txBuffer.packet.payload + payloadLength));

	while(waitForCrcQueue && CrcHandler::Instance().IsQueued(m_crcSlot, 0));

	SetupTransmit(&m_txBuffer.packet, m_txBuffer.requestedLength);

	++m_stats.sent;
}


void PacketUsart::SetupReceive()
{
	m_rxBuffers[m_rxBufferSelector].requestedLength = sizeof(m_rxBuffers[m_rxBufferSelector].packet);
	UsartCore::SetupReceive(&m_rxBuffers[m_rxBufferSelector], sizeof(m_rxBuffers[m_rxBufferSelector].packet));
}


//////////////////////////////////////
// UsartCore pure virtual functions //
//////////////////////////////////////

void PacketUsart::ReceiverIdle(void)
{
	uint16_t rcvdLen = m_rxBuffers[m_rxBufferSelector].requestedLength - LL_DMA_GetDataLength(m_rxDma.GetDma(), m_rxDma.GetStream());

	if(rcvdLen >= sizeof(PacketHeader)) {
		if(CheckHeader(m_rxBuffers[m_rxBufferSelector].packet.header)) {
			if(rcvdLen >= sizeof(PacketHeader) +
					RoundUpTo4((uint16_t)m_rxBuffers[m_rxBufferSelector].packet.header.payloadLength)
					+ sizeof(uint32_t))
				LL_DMA_DisableStream(m_rxDma.GetDma(), m_rxDma.GetStream());
			else
				++m_stats.premature_payload;
		} else {
			m_rxBuffers[m_rxBufferSelector].error = 1;
			LL_DMA_DisableStream(m_rxDma.GetDma(), m_rxDma.GetStream());
		}
	} else
		++m_stats.premature_hdr;
}


void PacketUsart::TransmissionComplete(void)
{
	LL_USART_DisableDirectionTx(m_usart);	// enforcing an idle frame
	LL_USART_EnableDirectionTx(m_usart);
	m_txBuffer.busy = 0;
}


void PacketUsart::FramingError(void) {}


void PacketUsart::Overrun(void) {}


void PacketUsart::RxDmaTransferComplete(void)
{
	if(CheckHeader(m_rxBuffers[m_rxBufferSelector].packet.header))
		CrcHandler::Instance().Enqueue(m_crcSlot, 1,
				&m_rxBuffers[m_rxBufferSelector].packet,
				sizeof(PacketHeader) + RoundUpTo4((uint16_t)m_rxBuffers[m_rxBufferSelector].packet.header.payloadLength),
				this, m_rxBufferSelector);
	else {
		++m_stats.hdrError;
		m_rxBuffers[m_rxBufferSelector].error = true;
	}
	SwitchRxBuffers();
}

void PacketUsart::RxDmaHalfTransfer(void)
{
}


void PacketUsart::RxDmaError(DmaHelper::DmaErrorType reason)
{
	m_rxBuffers[m_rxBufferSelector].error = 1;
	++m_stats.rxDmaError;
	SwitchRxBuffers();
}

void PacketUsart::TxDmaTransferComplete(void)
{
	LL_USART_EnableIT_TC(m_usart);
	LL_DMA_DisableStream(m_txDma.GetDma(), m_txDma.GetStream());
}


void PacketUsart::TxDmaHalfTransfer(void)
{
}


void PacketUsart::TxDmaError(DmaHelper::DmaErrorType reason)
{
	m_txBuffer.error = 1;
	++m_stats.txDmaError;
}


///////////////////////
// Private functions //
///////////////////////

void PacketUsart::BuildHeader(Packet &packet, uint8_t serialNo, uint8_t length)
{
	uint8_t hash = STARTMARKER;

	packet.header.startByte = STARTMARKER;
	packet.header.serial = serialNo;
	hash ^= serialNo;
	packet.header.payloadLength = length;
	hash ^= length;
	packet.header.hash = hash;
}


bool PacketUsart::CheckHeader(PacketHeader &header)
{
	return header.startByte == STARTMARKER && (header.startByte ^ header.serial ^ header.payloadLength) == header.hash;
}


void PacketUsart::SwitchRxBuffers(void)
{
	++m_stats.rcvd;
	m_rxBufferSelector = !m_rxBufferSelector;

	if(m_rxBuffers[m_rxBufferSelector].busy)
		++m_stats.overrun;
	SetupReceive();
}

///////////////////////////
// CrcHandler::ICallback //
///////////////////////////

void PacketUsart::CrcSucceeded(uintptr_t callbackParam, uint32_t crc, uint8_t task)
{
	Buffer &buf(m_rxBuffers[static_cast<int>(callbackParam)]);

	buf.busy = 1;
	if(*(uint32_t*) (buf.packet.payload + RoundUpTo4((uint16_t)buf.packet.header.payloadLength)) != crc) {
		buf.error = 1;
		buf.errorInfo = crc;
		++m_stats.payloadErrror;
	}
	if(m_userCallback)
		buf.busy = !m_userCallback->PacketReceived(this, m_userCallbackParam, buf.packet);
}


void PacketUsart::CrcFailed(uintptr_t callbackParam, uint32_t crc, uint8_t task)
{
	Buffer &buf(m_rxBuffers[static_cast<int>(callbackParam)]);
	buf.busy = buf.error = true;
	buf.errorInfo = 0;
	++m_stats.payloadErrror;
	if(m_userCallback)
		buf.busy = !m_userCallback->PacketReceived(this, m_userCallbackParam, buf.packet);
}


}	//	namespace f4ll
