/**
 *****************************************************************************************
 *	@file etn_mesh_fota_blb_trans_cli.c
 *
 *	@brief eaton ble mesh fota blob transfer client model source file
 *
 *	@details this custom model from eaton, used to transfer binary large object from client to server. 
 *
 *	@version C Style Guide Version 1.0
 *
 *	@copyright 2022 Eaton Corporation. All Rights Reserved.
 *
 *****************************************************************************************
 */

/*
 *****************************************************************************************
 *		Headers
 *****************************************************************************************
 */
#include <zephyr.h>
#include <etn_mesh_fota.h>
#include <etn_mesh_fota_blb.h>
#include <etn_mesh_fota_blb_cli.h>
#include <logging/log.h>

LOG_MODULE_REGISTER( etn_blb_cli, LOG_LEVEL_DBG );

/*
 *****************************************************************************************
 *		Defines
 *****************************************************************************************
 */


/*
 *****************************************************************************************
 *		Constants
 *****************************************************************************************
 */

/*
 *****************************************************************************************
 *		Variables
 *****************************************************************************************PLAT
 */

/*
 *****************************************************************************************
 *		Typedefs
 *****************************************************************************************
 */


/*
 *****************************************************************************************
 * See header file for function definition.
 *****************************************************************************************
 */
/*
 *****************************************************************************************
 *		handlers for eaton mesh fota blob transer server
 *****************************************************************************************
 */

static struct fota_blb_trans_status blb_trans_status;
static struct fota_blb_info_status blb_info_status;
static struct fota_blb_trans_start blb_trans_start;
static struct fota_blb_block_prtl_rpt prtl_report;

int fota_blb_cli_extract_blob_info_status(struct net_buf_simple *buf)
{
	int status=0;
	int len=0;

	len = buf->len;

	if( len == BT_MESH_ETN_FOTA_BLB_INFO_STATUS_MSG_MINLEN )
	{
		blb_info_status.min_block_size_log = net_buf_simple_pull_u8(buf);
		blb_info_status.max_block_size_log = net_buf_simple_pull_u8(buf);
		blb_info_status.max_total_chunks = net_buf_simple_pull_le16(buf);
		blb_info_status.max_chunk_size = net_buf_simple_pull_le16(buf);
		blb_info_status.max_blob_size = net_buf_simple_pull_le32(buf);
		blb_info_status.server_mtu_size = net_buf_simple_pull_le16(buf);
		blb_info_status.supported_trans_mode = net_buf_simple_pull_u8(buf);
		status=0;
	}
	else
	{
		LOG_ERR( "Err in information status opcode number of bytes received, bytes len:%d",len );
		status = -1;
	}

	return status;
}

int fota_blb_cli_extract_blob_block_status(struct net_buf_simple *buf, struct fota_blb_block_status *block_status)
{
	int status=0;
	int len=0;

	len = buf->len;

	if( len == BT_MESH_ETN_FOTA_BLB_BLOCK_STATUS_MSG_MINLEN )
	{
		block_status->status = net_buf_simple_pull_u8(buf);
		block_status->rfu = net_buf_simple_pull_u8(buf);
		block_status->format = net_buf_simple_pull_u8(buf);
		block_status->block_number = net_buf_simple_pull_le16(buf);
		block_status->chunk_size = net_buf_simple_pull_le16(buf);
		block_status->missing_chunks = net_buf_simple_pull_le16(buf);
		block_status->encoded_missing_chunks = net_buf_simple_pull_le16(buf);
		status=0;
	}
	else
	{
		LOG_ERR( "Err in block status opcode number of bytes received, bytes len:%d",len );
		status = -1;
	}

	return status;
}

int fota_blb_cli_extract_blob_transfer_status(struct net_buf_simple *buf, struct fota_blb_trans_status *trans_status)
{
	int status=0;
	int len=0;

	len = buf->len;

	if( len == BT_MESH_ETN_FOTA_BLB_TRANS_STATUS_MSG_MINLEN )
	{
		trans_status->status = net_buf_simple_pull_u8(buf);	
		trans_status->rfu = net_buf_simple_pull_u8(buf);
		trans_status->trans_mode = net_buf_simple_pull_u8(buf);
		trans_status->trans_phase = net_buf_simple_pull_u8(buf);
		trans_status->blob_id = net_buf_simple_pull_le64(buf);
		trans_status->blob_size = net_buf_simple_pull_le32(buf);
		trans_status->block_size_log = net_buf_simple_pull_u8(buf);
		trans_status->trans_mtu_size = net_buf_simple_pull_le16(buf);
		trans_status->block_not_recvd = net_buf_simple_pull_le16(buf);
		status=0;
	}
	else
	{
		LOG_ERR( "Err in transfer status opcode number of bytes received, bytes len:%d",len );
		status = -1;
	}
	
	return status;
}

int fota_blb_cli_extract_block_partial_report(struct net_buf_simple *buf)
{
	int status=0;
	int len=0;

	len = buf->len;

	if( len == BT_MESH_ETN_FOTA_BLB_BLOCK_PRL_RPT_MSG_MINLEN )
	{
		prtl_report.encoded_missing_chunks = net_buf_simple_pull_le16(buf);	
		status=0;
	}
	else
	{
		LOG_ERR( "Err in block partial opcode number of bytes received, bytes len:%d",len );
		status = -1;
	}
	
	return status;
}

static int opcode_handler_fota_blb_cli_blob_transfer_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	struct fota_blb_trans_status trans_status;
	int parse_status;

	LOG_DBG( "fun: opcode_handler_fota_blb_cli_blob_transfer_status" );

	parse_status = fota_blb_cli_extract_blob_transfer_status(buf,&trans_status);

	if( parse_status == 0 )
	{
		if (blbcli->handlers->blb_transfer_status) {
			blbcli->handlers->blb_transfer_status(blbcli, ctx, &trans_status);
		}
	}
	else
	{
		LOG_DBG( "error in data parsing" );
	}

	return parse_status;
}

static int opcode_handler_fota_blb_cli_info_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	int parse_status;

	LOG_DBG( "fun: opcode_handler_fota_blb_cli_info_status" );

	parse_status = fota_blb_cli_extract_blob_info_status(buf);
	
	if( parse_status == 0 )
	{
		if (blbcli->handlers->blb_info_status) {
			blbcli->handlers->blb_info_status(blbcli, ctx, &blb_info_status);
		}
	}
	else
	{
		LOG_DBG( "error in data parsing" );
	}

	return parse_status;
}

static int opcode_handler_fota_blb_cli_blob_block_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	struct fota_blb_block_status block_status;
	int parse_status;

	LOG_DBG( "fun: opcode_handler_fota_blb_cli_blob_block_status" );

	parse_status = fota_blb_cli_extract_blob_block_status(buf,&block_status);
	
	if( parse_status == 0 )
	{
		if (blbcli->handlers->blb_block_status) {
			blbcli->handlers->blb_block_status(blbcli, ctx, &block_status);
		}
	}
	else
	{
		LOG_DBG( "error in data parsing" );
	}

	return parse_status;
}

static int opcode_handler_fota_blb_cli_block_partial_report(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	int parse_status;

	LOG_DBG( "fun: opcode_handler_fota_blb_cli_block_partial_report" );

	parse_status = fota_blb_cli_extract_block_partial_report(buf);

	/* yogesh : to do error reporting */
	if( parse_status == 0 )
	{
		if (blbcli->handlers->blb_block_partial_report) {
			blbcli->handlers->blb_block_partial_report(blbcli, ctx, &prtl_report);
		}
	}
	else
	{
		LOG_DBG( "error in data parsing" );
	}

	return parse_status;
}

static int opcode_handler_fota_blb_srv_reboot_request(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
{
	LOG_DBG( "fun: opcode_handler_fota_blb_srv_reboot_request" );	
	return 0;
}

const struct bt_mesh_model_op _bt_mesh_etn_fota_blb_cli_op[] = {
	
	/* 
		Blob transfer messages 
			1. blob transfer get
			2. blob transfer start
			3. blob transfer cancel
			4. blob transfer status
	*/
	
	{
		BT_MESH_ETN_FOTA_BLB_BLOCK_PRL_RPT_OP,
		BT_MESH_LEN_MIN( BT_MESH_ETN_FOTA_BLB_BLOCK_PRL_RPT_MSG_MINLEN ),
		opcode_handler_fota_blb_cli_block_partial_report
	},

	{
		BT_MESH_ETN_FOTA_BLB_TRANS_STATUS_OP,
		BT_MESH_LEN_MIN( BT_MESH_ETN_FOTA_BLB_TRANS_STATUS_MSG_MINLEN ),
		opcode_handler_fota_blb_cli_blob_transfer_status
	},

	{
		BT_MESH_ETN_FOTA_BLB_BLOCK_STATUS_OP,
		BT_MESH_LEN_MIN( BT_MESH_ETN_FOTA_BLB_BLOCK_STATUS_MSG_MINLEN ),
		opcode_handler_fota_blb_cli_blob_block_status
	},


	/* blob information status */
	{
		BT_MESH_ETN_FOTA_BLB_INFO_STATUS_OP,
		BT_MESH_LEN_MIN( BT_MESH_ETN_FOTA_BLB_INFO_STATUS_MSG_MINLEN ),
		opcode_handler_fota_blb_cli_info_status
	},

	/* blob reboot */
	{
		BT_MESH_ETN_FOTA_BLB_REBOOT_REQ_OP,
		BT_MESH_LEN_MIN( BT_MESH_ETN_FOTA_BLB_REBOOT_REQ_MSG_MINLEN ),
		opcode_handler_fota_blb_srv_reboot_request
	},

	BT_MESH_MODEL_OP_END,
};

static int bt_mesh_etn_fota_blb_cli_update_handler(struct bt_mesh_model *model)
{
	//struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	LOG_DBG( "fun: bt_mesh_etn_fota_blb_cli_update_handler" );
	return 0;
}

#ifdef CONFIG_BT_SETTINGS
static int bt_mesh_etn_fota_blb_cli_settings_set(struct bt_mesh_model *model, const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg)
{
	//struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;
	LOG_DBG( "fun: bt_mesh_etn_fota_blb_cli_settings_set" );

	return 0;
}
#endif

static int bt_mesh_etn_fota_blb_cli_init(struct bt_mesh_model *model)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;

	LOG_DBG( "fun: bt_mesh_etn_fota_blb_cli_init" );

	blbcli->model = model;

	net_buf_simple_init_with_data(&blbcli->pub_msg, blbcli->buf,sizeof(blbcli->buf));
	blbcli->pub.msg = &blbcli->pub_msg;
	blbcli->pub.update = bt_mesh_etn_fota_blb_cli_update_handler;

	return 0;
}

static int bt_mesh_etn_fota_blb_cli_start(struct bt_mesh_model *model)
{
	struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;

	LOG_DBG( "fun: bt_mesh_etn_fota_blb_cli_start" );

	if (blbcli->handlers->start) {
		blbcli->handlers->start(blbcli);
	}

	return 0;
}

static void bt_mesh_etn_fota_blb_cli_reset(struct bt_mesh_model *model)
{
	//struct bt_mesh_etn_fota_blb_cli *blbcli = model->user_data;

	LOG_DBG( "fun: bt_mesh_etn_fota_blb_cli_reset" );

	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
		(void) bt_mesh_model_data_store(model, true, NULL, NULL, 0);
	}
}

/* .. include_startingpoint_chat_cli_rst_7 */
const struct bt_mesh_model_cb _bt_mesh_etn_fota_blb_cli_cb = {
	.init  = bt_mesh_etn_fota_blb_cli_init,
    .start = bt_mesh_etn_fota_blb_cli_start,
	#ifdef CONFIG_BT_SETTINGS
		.settings_set = bt_mesh_etn_fota_blb_cli_settings_set,
	#endif
	.reset = bt_mesh_etn_fota_blb_cli_reset,
};

static void bt_mesh_etn_fota_blb_cli_send_cb_start(uint16_t duration, int err, void *data)
{
	//printk("start send_cb data:%d err:%d\n", duration, err);
}

static void bt_mesh_etn_fota_blb_cli_send_cb_end(int err, void *cb_data)
{
	//printk("end send_cb err:%d\n", err);
}

const struct bt_mesh_send_cb _bt_mesh_etn_fota_blb_cli_send_cb =
{
	.start = bt_mesh_etn_fota_blb_cli_send_cb_start,
	.end = bt_mesh_etn_fota_blb_cli_send_cb_end,
};

/* model functions to access externally */

int bt_mesh_etn_fota_blb_blb_transfer_get_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_TRANS_GET_OP, BT_MESH_ETN_FOTA_BLB_TRANS_GET_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_TRANS_GET_OP);
	net_buf_simple_add_u8(&buf, 0x00);

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_blb_transfer_cancel_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_TRANS_CANCEL_OP, BT_MESH_ETN_FOTA_BLB_TRANS_CANCEL_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_TRANS_CANCEL_OP);
	net_buf_simple_add_u8(&buf, 0x00);

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_blb_block_get_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_BLOCK_GET_OP, BT_MESH_ETN_FOTA_BLB_BLOCK_GET_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_BLOCK_GET_OP);
	net_buf_simple_add_u8(&buf, 0x00);

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_info_status_get_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_INFO_GET_OP, BT_MESH_ETN_FOTA_BLB_INFO_GET_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_INFO_GET_OP);
	net_buf_simple_add_u8(&buf, 0x00);

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_cli_chunk_data_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr, const struct fota_blb_data_flash *flash_data)
{
	/* yogesh: to do default ttl issue */
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_OP, BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_OP);
	net_buf_simple_add_mem(&buf, flash_data, BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_MSG_MINLEN );

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, &_bt_mesh_etn_fota_blb_cli_send_cb, NULL);
}

int bt_mesh_etn_fota_blb_block_start_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr, const struct fota_blb_block_start *block_start)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_BLOCK_START_OP, BT_MESH_ETN_FOTA_BLB_BLOCK_START_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_BLOCK_START_OP);
	net_buf_simple_add_mem(&buf, block_start, BT_MESH_ETN_FOTA_BLB_BLOCK_START_MSG_MINLEN );

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_cli_blb_trans_start_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr, const struct fota_blb_trans_start *trans_start)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_TRANS_START_OP, BT_MESH_ETN_FOTA_BLB_TRANS_START_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_TRANS_START_OP);
	net_buf_simple_add_mem(&buf, trans_start, BT_MESH_ETN_FOTA_BLB_TRANS_START_MSG_MINLEN );

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}

int bt_mesh_etn_fota_blb_cli_blob_reboot_request_send(struct bt_mesh_etn_fota_blb_cli *blbcli, uint16_t addr, uint8_t value)
{
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = blbcli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_BLB_REBOOT_REQ_OP, BT_MESH_ETN_FOTA_BLB_REBOOT_REQ_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_BLB_REBOOT_REQ_OP);
	
	net_buf_simple_add_u8(&buf, value );

	return bt_mesh_model_send(blbcli->model, &ctx, &buf, NULL, NULL);
}