/**
 *****************************************************************************************
 *	@file etn_mesh_fota_dis_cli.c
 *
 *	@brief eaton ble mesh fota firmware distribution client model source file
 *
 *	@details this custom model from eaton, used for firmware distribution client model
 *
 *	@version C Style Guide Version 1.0
 *
 *	@copyright 2022 Eaton Corporation. All Rights Reserved.
 *
 *****************************************************************************************
 */

/*
 *****************************************************************************************
 *		Headers
 *****************************************************************************************
 */
#include <zephyr.h>
#include <string.h>
#include <zephyr.h>
#include <etn_mesh_fota.h>
#include <etn_mesh_fota_dis.h>
#include <etn_mesh_fota_dis_cli.h>
#include "model_utils.h"
#include <logging/log.h>

LOG_MODULE_REGISTER( etn_dis_cli, LOG_LEVEL_DBG );

static struct etn_fota_error   	dis_cli_err_report;

/**
 * @brief	static instance of Firmware distribution server model state
 */
static enum fota_dis_srv_state state;
/**
 * @brief	static instance of DFU command reception status
 */
static enum fota_dis_cmd_status dfu_cmd_reply;
/**
 * @brief	static instance of metadata reception status
 */
static enum fota_dis_srv_metadata_status metadata_status;
/**
 * @brief	static instance of distribution list reception status
 */
static enum fota_dis_list_status dstrbn_list_status;
/**
 * @brief	static instance of distribution list data
 */
static uint64_t distribution_list = 0;
/**
 * @todo: to add number mesh devices to be updated uint16_t num_of_updatable_mesh_devices;
 */
/**
 * @brief	static instance of firmware image metadata
 */
static struct fota_dis_metadata *metadata;

static void encode_state( struct net_buf_simple* buf, enum fota_dis_srv_state discli_state )
{
	bt_mesh_model_msg_init( buf, BT_MESH_ETN_FOTA_DIS_STATE_GET_OP );
	net_buf_simple_add_u8( buf, discli_state );
}

static const uint8_t* extract_msg( struct net_buf_simple* buf )
{
	buf->data[buf->len - 1] = '\0';
	return ( net_buf_simple_pull_mem( buf, buf->len ) );
}

static int bt_mesh_fota_dis_cli_update_handler( struct bt_mesh_model* model )
{
	/* Continue publishing current state. */
	encode_state( model->pub->msg, state );

	return ( 0 );
}

static int bt_mesh_fota_dis_cli_init( struct bt_mesh_model* model )
{
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;

	discli->model = model;

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

	return ( 0 );
}

static int bt_mesh_fota_dis_cli_start( struct bt_mesh_model* model )
{
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;

	state = BT_MESH_DEV_AVLBL_TO_FW_UPDATE;

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

	return ( 0 );
}

static void bt_mesh_fota_dis_cli_reset( struct bt_mesh_model* model )
{
	state = BT_MESH_DEV_AVLBL_TO_FW_UPDATE;
	
    struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;
	
    discli->model = model;

	net_buf_simple_init_with_data( &discli->pub_msg, discli->buf,
								   sizeof( discli->buf ) );
	discli->pub.msg = &discli->pub_msg;
}

const struct bt_mesh_model_cb _bt_mesh_etn_fota_dis_cli_cb =
{
	.init  = bt_mesh_fota_dis_cli_init,
	.start = bt_mesh_fota_dis_cli_start,
	.reset = bt_mesh_fota_dis_cli_reset,
};

int fota_dis_cli_extract_dfu_cmd(struct net_buf_simple *buf, uint8_t *dfu_cmd_reply)
{
	int status=0;
	int len=0;

	len = buf->len;

	if(len == 1 )
	{
		*dfu_cmd_reply = net_buf_simple_pull_u8(buf); 
		status = 0;
	}
	else
	{
		LOG_ERR( "Err in dfu start number of bytes, bytes len:%d",len );
		status = -1;
	}

	return status;
}

static int opcode_handler_fota_dis_cli_dfu_cmd( struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf )
{	
	int parse_status=0;
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;

	LOG_DBG(" fun: opcode_handler_fota_dis_cli_dfu_cmd ");
	
	parse_status = fota_dis_cli_extract_dfu_cmd(buf, &dfu_cmd_reply);

	if( parse_status == 0 )
	{
		/* yogesh : to do validation */
		if ( discli->handlers->dfu_start )
		{
			discli->handlers->dfu_start( &dfu_cmd_reply );
		}
	}
	else
	{
		LOG_DBG( "data parse error" );
		dis_cli_err_report.opcode = BT_MESH_ETN_FOTA_DIS_UPDATE_START_OP;
		dis_cli_err_report.err_code = FOTA_ERROR_DATA_PARSE;
		
		if (discli->handlers->error_reporting)
		{
			discli->handlers->error_reporting(discli, ctx, &dis_cli_err_report);
		}
	}

	return ( 0 );
}

int fota_dis_srv_extract_receive_list_reply(struct net_buf_simple *buf, uint8_t *dstrbn_list_status)
{
	int status=0;
	int len=0;

	len = buf->len;

	if(len == 1 )
	{
		*dstrbn_list_status = net_buf_simple_pull_u8(buf); 
		status = 0;
	}
	else
	{
		LOG_ERR( "Err in receive list reply number of bytes, bytes len:%d",len );
		status = -1;
	}

	return status;
}

static int opcode_handler_fota_dis_cli_receive_list( struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf )
{
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;
	int parse_status=0;

	parse_status = fota_dis_srv_extract_receive_list_reply(buf, &dstrbn_list_status);

	if( parse_status == 0 )
	{
		/* yogesh: to do  validation */
		if ( discli->handlers->distribn_get )
		{
			discli->handlers->distribn_get( &dstrbn_list_status );
		}
	}
	else
	{
		LOG_DBG( "data parse error" );
		dis_cli_err_report.opcode = BT_MESH_ETN_FOTA_DIS_RECV_LIST_GET_OP;
		dis_cli_err_report.err_code = FOTA_ERROR_DATA_PARSE;
		
		if (discli->handlers->error_reporting)
		{
			discli->handlers->error_reporting(discli, ctx, &dis_cli_err_report);
		}
	}

	

	return ( 0 );
}

int fota_dis_srv_extract_metadata_reply(struct net_buf_simple *buf, uint8_t *metadata_status)
{
	int status=0;
	int len=0;

	len = buf->len;

	if(len == 1 )
	{
		*metadata_status = net_buf_simple_pull_u8(buf); 
		status = 0;
	}
	else
	{
		LOG_ERR( "Err in metadata reply number of bytes, bytes len:%d",len );
		status = -1;
	}

	return status;
}

static int opcode_handler_fota_dis_cli_receive_metadata_replay( struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf )
{
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;
	const uint8_t *msg;
	int l_Status = 0;

	/* yogesh : to do error reporting */
	
	int parse_status=0;

	/* yogesh : to do error reporting */
	
	parse_status = fota_dis_srv_extract_metadata_reply(buf, &metadata_status);

	if( parse_status == 0 )
	{
		if ( discli->handlers->metadata_get )
		{
			discli->handlers->metadata_get( &metadata_status );
		}
	}
	else
	{
		LOG_DBG( "data parse error" );
		dis_cli_err_report.opcode = BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_OP;
		dis_cli_err_report.err_code = FOTA_ERROR_DATA_PARSE;
		
		if (discli->handlers->error_reporting)
		{
			discli->handlers->error_reporting(discli, ctx, &dis_cli_err_report);
		}
	}

	return ( 0 );
}

int fota_dis_srv_extract_state_get(struct net_buf_simple *buf, uint8_t *state)
{
	int status=0;
	int len=0;

	len = buf->len;

	if(len == 1 )
	{
		*state = net_buf_simple_pull_u8(buf); 
		status = 0;
	}
	else
	{
		LOG_ERR( "Err in state get number of bytes, bytes len:%d",len );
		status = -1;
	}

	return status;
}

static int opcode_handler_fota_dis_cli_state_get( struct bt_mesh_model* model, struct bt_mesh_msg_ctx* ctx,
							 struct net_buf_simple* buf )
{
	struct bt_mesh_etn_fota_dis_cli *discli = model->user_data;
	int l_Status = 0;

	int parse_status=0;

	/* yogesh : to do error reporting */
	
	parse_status = fota_dis_srv_extract_state_get(buf, &state);

	if( parse_status == 0 )
	{
		if ( discli->handlers->server_state_get )
		{
			discli->handlers->server_state_get( &state );
		}
	}
	else
	{
		LOG_DBG( "data parse error" );
		dis_cli_err_report.opcode = BT_MESH_ETN_FOTA_DIS_STATE_GET_OP;
		dis_cli_err_report.err_code = FOTA_ERROR_DATA_PARSE;
		
		if (discli->handlers->error_reporting)
		{
			discli->handlers->error_reporting(discli, ctx, &dis_cli_err_report);
		}
	}

	return ( 0 );
}

const struct bt_mesh_model_op _bt_mesh_etn_fota_dis_cli_op[] =
{
	{
		BT_MESH_ETN_FOTA_DIS_UPDATE_START_OP,
		BT_MESH_LEN_EXACT( 0 ),
        opcode_handler_fota_dis_cli_dfu_cmd
	},
	{
		BT_MESH_ETN_FOTA_DIS_RECV_LIST_GET_OP,
		BT_MESH_LEN_EXACT( 1 ), //here we are receiving one byte only
		opcode_handler_fota_dis_cli_receive_list
	},
	{
		BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_OP,
		BT_MESH_LEN_MIN( 1 ), //here we are receiving only one byte
		opcode_handler_fota_dis_cli_receive_metadata_replay
	},
	{
		BT_MESH_ETN_FOTA_DIS_STATE_GET_OP,
		BT_MESH_LEN_EXACT( 1 ),//here we are receiving only one byte
		opcode_handler_fota_dis_cli_state_get
	},

	BT_MESH_MODEL_OP_END,
};

int bt_mesh_etn_fota_dis_cli_metadata_send(struct bt_mesh_etn_fota_dis_cli *discli, uint16_t addr, struct fota_dis_metadata *metadata )
{
    /* yogesh : to do ttl value */
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = discli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

    BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_OP, BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_OP);
	net_buf_simple_add_mem(&buf, metadata, BT_MESH_ETN_FOTA_DIS_IMAGE_METADATA_GET_MSG_MINLEN );

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

}

int bt_mesh_etn_fota_dis_cli_update_start_send(struct bt_mesh_etn_fota_dis_cli *discli, uint16_t addr)
{
	/* yogesh : to do ttl value */
	
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = discli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_DIS_UPDATE_START_OP, 0);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_DIS_UPDATE_START_OP);

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

int bt_mesh_etn_fota_dis_cli_receive_list_send(struct bt_mesh_etn_fota_dis_cli *discli, uint16_t addr, uint64_t *distribution_list)
{
	/* yogesh : to do ttl value */
	
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = discli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_DIS_RECV_LIST_GET_OP, BT_MESH_ETN_FOTA_DIS_RECV_LIST_GET_MSG_MINLEN);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_DIS_RECV_LIST_GET_OP);
	net_buf_simple_add_le64(&buf,*distribution_list);

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

int bt_mesh_etn_fota_dis_cli_state_get_send(struct bt_mesh_etn_fota_dis_cli *discli, uint16_t addr)
{
	/* yogesh : to do ttl value */
	
	struct bt_mesh_msg_ctx ctx = {
		.addr = addr,
		.app_idx = discli->model->keys[0],
		.send_ttl = BT_MESH_TTL_DEFAULT,
		.send_rel = true,
	};

	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_ETN_FOTA_DIS_STATE_GET_OP, 0);
	bt_mesh_model_msg_init(&buf, BT_MESH_ETN_FOTA_DIS_STATE_GET_OP);

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