/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * KISS protocol encoder and decoder implementation.
 */

#include "kiss.h"
#include <errno.h>
#include <string.h>

int kiss_encode_cmd(uint8_t cmd, const uint8_t *data, size_t data_len,
		    uint8_t *out_buf, size_t out_size)
{
	size_t out_idx = 0;

	/* Worst-case output: FEND + CMD + (2 * data_len) + FEND */
	if (out_size < (2 + 2 * data_len + 1)) {
		return -ENOMEM;
	}

	out_buf[out_idx++] = KISS_FEND;
	out_buf[out_idx++] = cmd;

	for (size_t i = 0; i < data_len; i++) {
		if (data[i] == KISS_FEND) {
			out_buf[out_idx++] = KISS_FESC;
			out_buf[out_idx++] = KISS_TFEND;
		} else if (data[i] == KISS_FESC) {
			out_buf[out_idx++] = KISS_FESC;
			out_buf[out_idx++] = KISS_TFESC;
		} else {
			out_buf[out_idx++] = data[i];
		}
	}

	out_buf[out_idx++] = KISS_FEND;
	return (int)out_idx;
}

int kiss_encode(const uint8_t *data, size_t data_len,
		uint8_t *out_buf, size_t out_size)
{
	return kiss_encode_cmd(KISS_CMD_DATA, data, data_len, out_buf, out_size);
}

void kiss_decoder_init(struct kiss_decoder_ctx *ctx,
		       uint8_t *buf, size_t size)
{
	ctx->frame_buf      = buf;
	ctx->frame_buf_size = size;
	ctx->frame_len      = 0;
	ctx->in_frame       = false;
	ctx->in_escape      = false;
	ctx->cmd_received   = false;
	ctx->cmd            = 0;
}

bool kiss_decoder_feed(struct kiss_decoder_ctx *ctx, uint8_t byte)
{
	if (byte == KISS_FEND) {
		if (ctx->in_frame && ctx->frame_len > 0) {
			/* Closing delimiter — frame complete */
			ctx->in_frame     = false;
			ctx->in_escape    = false;
			ctx->cmd_received = false;
			return true;
		}
		/* Opening delimiter (or spurious FEND) — start fresh */
		ctx->in_frame     = true;
		ctx->in_escape    = false;
		ctx->cmd_received = false;
		ctx->frame_len    = 0;
		return false;
	}

	if (!ctx->in_frame) {
		return false;
	}

	/* Handle escape sequences */
	if (byte == KISS_FESC) {
		ctx->in_escape = true;
		return false;
	}

	if (ctx->in_escape) {
		ctx->in_escape = false;
		if (byte == KISS_TFEND) {
			byte = KISS_FEND;
		} else if (byte == KISS_TFESC) {
			byte = KISS_FESC;
		}
		/* Unknown escape sequences: discard the escape, keep the byte */
	}

	/* First unescaped byte after opening FEND is the CMD byte */
	if (!ctx->cmd_received) {
		ctx->cmd          = byte;
		ctx->cmd_received = true;
		return false;
	}

	/* Append to payload buffer (silently drop if full) */
	if (ctx->frame_len < ctx->frame_buf_size) {
		ctx->frame_buf[ctx->frame_len++] = byte;
	}

	return false;
}
