/*
 * Copyright (c) 2023 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/net/buf.h>
#include <zephyr/kernel.h>
#include <string.h>
#include <zephyr/mgmt/mcumgr/smp/smp_client.h>
#include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
#include <zephyr/sys/byteorder.h>
#include <mgmt/mcumgr/transport/smp_internal.h>
#include "smp_stub.h"

K_THREAD_STACK_DEFINE(smp_stub_work_queue_stack, CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_STACK_SIZE);

static mcmgr_client_data_check_fn rx_verify_cb;
static int send_client_failure;
static struct net_buf *response_buf;
static struct smp_hdr res_hdr;
static struct smp_transport smpt_test;
static struct smp_client_transport_entry smp_client_transport;
static struct k_work_q smp_work_queue;
static struct k_work stub_work;

static const struct k_work_queue_config smp_work_queue_config = {
	.name = "mcumgr smp"
};

void smp_stub_set_rx_data_verify(mcmgr_client_data_check_fn cb)
{
	rx_verify_cb = cb;
}

void smp_client_send_status_stub(int status)
{
	send_client_failure = status;
}

struct net_buf *smp_response_buf_allocation(void)
{
	smp_client_response_buf_clean();

	response_buf = smp_packet_alloc();

	return response_buf;
}

void smp_client_response_buf_clean(void)
{
	if (response_buf) {
		smp_client_buf_free(response_buf);
		response_buf = NULL;
	}
}

void smp_transport_read_hdr(const struct net_buf *nb, struct smp_hdr *dst_hdr)
{
	memcpy(dst_hdr, nb->data, sizeof(*dst_hdr));
	dst_hdr->nh_len = sys_be16_to_cpu(dst_hdr->nh_len);
	dst_hdr->nh_group = sys_be16_to_cpu(dst_hdr->nh_group);
}


static uint16_t smp_uart_get_mtu(const struct net_buf *nb)
{
	return 256;
}

static int smp_uart_tx_pkt(struct net_buf *nb)
{
	if (send_client_failure) {
		/* Test Send cmd fail */
		return send_client_failure;
	}

	memcpy(&res_hdr, nb->data, sizeof(res_hdr));
	res_hdr.nh_len = sys_be16_to_cpu(res_hdr.nh_len);
	res_hdr.nh_group = sys_be16_to_cpu(res_hdr.nh_group);
	res_hdr.nh_op += 1; /* Request to response */

	/* Validate Input data if callback is configured */
	if (rx_verify_cb) {
		rx_verify_cb(nb);
	}

	/* Free tx buf */
	net_buf_unref(nb);

	if (response_buf) {
		k_work_submit_to_queue(&smp_work_queue, &stub_work);
	}

	return 0;
}

static void smp_client_handle_reqs(struct k_work *work)
{
	if (response_buf) {
		smp_client_single_response(response_buf, &res_hdr);
	}
}

void stub_smp_client_transport_register(void)
{

	smpt_test.functions.output = smp_uart_tx_pkt;
	smpt_test.functions.get_mtu = smp_uart_get_mtu;

	smp_transport_init(&smpt_test);
	smp_client_transport.smpt = &smpt_test;
	smp_client_transport.smpt_type = SMP_SERIAL_TRANSPORT;
	smp_client_transport_register(&smp_client_transport);


	k_work_queue_init(&smp_work_queue);

	k_work_queue_start(&smp_work_queue, smp_stub_work_queue_stack,
			   K_THREAD_STACK_SIZEOF(smp_stub_work_queue_stack),
			   CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_THREAD_PRIO, &smp_work_queue_config);

	k_work_init(&stub_work, smp_client_handle_reqs);
}
