/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */

#ifdef NDEBUG
#undef NDEBUG
#endif

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <libmctp.h>
#include <libmctp-alloc.h>

#include "test-utils.h"

struct mctp_binding_bridge {
	struct mctp_binding binding;
	int rx_count;
	int tx_count;
	uint8_t last_pkt_data;
	uint8_t tx_storage[MCTP_PKTBUF_SIZE(MCTP_BTU)] PKTBUF_STORAGE_ALIGN;
};

struct test_ctx {
	struct mctp *mctp;
	struct mctp_binding_bridge *bindings[2];
};

static int mctp_binding_bridge_tx(struct mctp_binding *b,
				  struct mctp_pktbuf *pkt)
{
	struct mctp_binding_bridge *binding =
		container_of(b, struct mctp_binding_bridge, binding);

	binding->tx_count++;
	assert(mctp_pktbuf_size(pkt) == sizeof(struct mctp_hdr) + 1);
	binding->last_pkt_data = *(uint8_t *)mctp_pktbuf_data(pkt);

	return 0;
}

static void mctp_binding_bridge_rx(struct mctp_binding_bridge *binding,
				   uint8_t key)
{
	struct mctp_pktbuf *pkt;
	struct mctp_hdr *hdr;
	uint8_t *buf;

	pkt = mctp_pktbuf_alloc(&binding->binding, sizeof(struct mctp_hdr) + 1);
	assert(pkt);

	hdr = mctp_pktbuf_hdr(pkt);
	hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM;

	/* arbitrary src/dest, as we're bridging */
	hdr->src = 1;
	hdr->dest = 2;

	buf = mctp_pktbuf_data(pkt);
	*buf = key;

	binding->rx_count++;
	mctp_bus_rx(&binding->binding, pkt);
	mctp_pktbuf_free(pkt);
}

static struct mctp_binding_bridge *mctp_binding_bridge_init(char *name)
{
	struct mctp_binding_bridge *binding;

	binding = __mctp_alloc(sizeof(*binding));
	memset(binding, 0, sizeof(*binding));
	binding->binding.name = name;
	binding->binding.version = 1;
	binding->binding.tx = mctp_binding_bridge_tx;
	binding->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
	binding->binding.pkt_header = 0;
	binding->binding.pkt_trailer = 0;
	binding->binding.tx_storage = binding->tx_storage;
	return binding;
}

int main(void)
{
	struct test_ctx _ctx, *ctx = &_ctx;
	ctx->mctp = mctp_init();

	ctx->bindings[0] = mctp_binding_bridge_init("binding0");
	ctx->bindings[1] = mctp_binding_bridge_init("binding1");
	mctp_bridge_busses(ctx->mctp, &ctx->bindings[0]->binding,
			   &ctx->bindings[1]->binding);

	mctp_binding_set_tx_enabled(&ctx->bindings[0]->binding, true);
	mctp_binding_set_tx_enabled(&ctx->bindings[1]->binding, true);

	mctp_binding_bridge_rx(ctx->bindings[0], 0xaa);
	assert(ctx->bindings[0]->tx_count == 0);
	assert(ctx->bindings[1]->tx_count == 1);
	assert(ctx->bindings[1]->last_pkt_data == 0xaa);

	mctp_binding_bridge_rx(ctx->bindings[1], 0x55);
	assert(ctx->bindings[1]->tx_count == 1);
	assert(ctx->bindings[0]->tx_count == 1);
	assert(ctx->bindings[0]->last_pkt_data == 0x55);

	__mctp_free(ctx->bindings[1]);
	__mctp_free(ctx->bindings[0]);
	mctp_destroy(ctx->mctp);

	return EXIT_SUCCESS;
}