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

#include <stdio.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh/models.h>
#include <dk_buttons_and_leds.h>
#include "model_handler_cli.h"
#include <logging/log.h>
#include <sys/printk.h>

#include <main.h>
#include "etn_mesh_fota.h"
#include "etn_mesh_fota_blb.h"
#include "etn_mesh_fota_blb_cli.h"

#include "etn_mesh_fota_dis.h"
#include "etn_mesh_fota_dis_cli.h"

#include "etn_mesh_fota_upd.h"
#include "etn_mesh_fota_upd_cli.h"

LOG_MODULE_DECLARE(model_handler_blb_cli);

const uint16_t table[256] =
{
0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU,

0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U,

0x1081U, 0x0108U, 0x3393U, 0x221aU, 0x56a5U, 0x472cU, 0x75b7U, 0x643eU,

0x9cc9U, 0x8d40U, 0xbfdbU, 0xae52U, 0xdaedU, 0xcb64U, 0xf9ffU, 0xe876U,

0x2102U, 0x308bU, 0x0210U, 0x1399U, 0x6726U, 0x76afU, 0x4434U, 0x55bdU,

0xad4aU, 0xbcc3U, 0x8e58U, 0x9fd1U, 0xeb6eU, 0xfae7U, 0xc87cU, 0xd9f5U,

0x3183U, 0x200aU, 0x1291U, 0x0318U, 0x77a7U, 0x662eU, 0x54b5U, 0x453cU,

0xbdcbU, 0xac42U, 0x9ed9U, 0x8f50U, 0xfbefU, 0xea66U, 0xd8fdU, 0xc974U,

0x4204U, 0x538dU, 0x6116U, 0x709fU, 0x0420U, 0x15a9U, 0x2732U, 0x36bbU,

0xce4cU, 0xdfc5U, 0xed5eU, 0xfcd7U, 0x8868U, 0x99e1U, 0xab7aU, 0xbaf3U,

0x5285U, 0x430cU, 0x7197U, 0x601eU, 0x14a1U, 0x0528U, 0x37b3U, 0x263aU,

0xdecdU, 0xcf44U, 0xfddfU, 0xec56U, 0x98e9U, 0x8960U, 0xbbfbU, 0xaa72U,

0x6306U, 0x728fU, 0x4014U, 0x519dU, 0x2522U, 0x34abU, 0x0630U, 0x17b9U,

0xef4eU, 0xfec7U, 0xcc5cU, 0xddd5U, 0xa96aU, 0xb8e3U, 0x8a78U, 0x9bf1U,

0x7387U, 0x620eU, 0x5095U, 0x411cU, 0x35a3U, 0x242aU, 0x16b1U, 0x0738U,

0xffcfU, 0xee46U, 0xdcddU, 0xcd54U, 0xb9ebU, 0xa862U, 0x9af9U, 0x8b70U,

0x8408U, 0x9581U, 0xa71aU, 0xb693U, 0xc22cU, 0xd3a5U, 0xe13eU, 0xf0b7U,

0x0840U, 0x19c9U, 0x2b52U, 0x3adbU, 0x4e64U, 0x5fedU, 0x6d76U, 0x7cffU,

0x9489U, 0x8500U, 0xb79bU, 0xa612U, 0xd2adU, 0xc324U, 0xf1bfU, 0xe036U,

0x18c1U, 0x0948U, 0x3bd3U, 0x2a5aU, 0x5ee5U, 0x4f6cU, 0x7df7U, 0x6c7eU,

0xa50aU, 0xb483U, 0x8618U, 0x9791U, 0xe32eU, 0xf2a7U, 0xc03cU, 0xd1b5U,

0x2942U, 0x38cbU, 0x0a50U, 0x1bd9U, 0x6f66U, 0x7eefU, 0x4c74U, 0x5dfdU,

0xb58bU, 0xa402U, 0x9699U, 0x8710U, 0xf3afU, 0xe226U, 0xd0bdU, 0xc134U,

0x39c3U, 0x284aU, 0x1ad1U, 0x0b58U, 0x7fe7U, 0x6e6eU, 0x5cf5U, 0x4d7cU,

0xc60cU, 0xd785U, 0xe51eU, 0xf497U, 0x8028U, 0x91a1U, 0xa33aU, 0xb2b3U,

0x4a44U, 0x5bcdU, 0x6956U, 0x78dfU, 0x0c60U, 0x1de9U, 0x2f72U, 0x3efbU,

0xd68dU, 0xc704U, 0xf59fU, 0xe416U, 0x90a9U, 0x8120U, 0xb3bbU, 0xa232U,

0x5ac5U, 0x4b4cU, 0x79d7U, 0x685eU, 0x1ce1U, 0x0d68U, 0x3ff3U, 0x2e7aU,

0xe70eU, 0xf687U, 0xc41cU, 0xd595U, 0xa12aU, 0xb0a3U, 0x8238U, 0x93b1U,

0x6b46U, 0x7acfU, 0x4854U, 0x59ddU, 0x2d62U, 0x3cebU, 0x0e70U, 0x1ff9U,

0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,

0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U

};

/******************************************************************************/
/*************************** Health server setup ******************************/
/******************************************************************************/
/* Set up a repeating delayed work to blink the DK's LEDs when attention is
 * requested.
 */
static struct k_work_delayable attention_blink_work_cli;
static struct k_work_delayable send_opcode_cli;

static bool attention_cli;

static int blb_trans_start_done = 0;
static int blb_chunk_ack=0;
static int blb_info_done=0;

static struct fota_dis_metadata		dis_cli_metatdata;
static uint64_t distribution_list = 0;

static struct fota_blb_trans_start 	blb_cli_trans_start;
static struct fota_blb_block_start 	blb_cli_block_start;
static struct fota_blb_block_status	blb_cli_block_status;

enum blbcli_firmware_states blbcli_fw_state = FW_STATE_NONE;
static uint16_t blbcli_wait_timeout = 0;

static bool discli_dfu_cmd_flag=0;
static bool discli_metadata_flag=0;
static bool blbcli_blob_trans_status_flag=0;
static bool blbcli_block_trans_status_flag=0;
static bool blbcli_chunk_partial_flag=0;
static bool blbcli_info_status_flag=0;
static bool updcli_progress_status_flag=0;

static uint8_t progress_percent=0;

static uint32_t firmware_total_bytes=0;
static uint32_t firmware_bytes_send=0;
static uint16_t chunk_number = 0;
static uint16_t flash_address=0;


static struct blbcli_firmware_values firm_val;

static struct fota_blb_data_flash flash_data_backup[MAX_CHUNK_IN_BLOCK];

static void attention_blink_cli(struct k_work *work)
{
	static int idx;
	const uint8_t pattern[] = {
		BIT(0) | BIT(1),
		BIT(1) | BIT(2),
		BIT(2) | BIT(3),
		BIT(3) | BIT(0),
	};

	if (attention_cli) {
		dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
		k_work_reschedule(&attention_blink_work_cli, K_MSEC(100));
	} else {
		dk_set_leds(DK_NO_LEDS_MSK);
	}
}

static void attention_on_cli(struct bt_mesh_model *mod)
{
	attention_cli = true;
	k_work_reschedule(&attention_blink_work_cli, K_NO_WAIT);
}

static void attention_off_cli(struct bt_mesh_model *mod)
{
	/* Will stop rescheduling blink timer */
	attention_cli = false;
}

static const struct bt_mesh_health_srv_cb health_srv_cb_cli = {
	.attn_on = attention_on_cli,
	.attn_off = attention_off_cli,
};

static struct bt_mesh_health_srv health_srv_cli = {
	.cb = &health_srv_cb_cli,
};

BT_MESH_HEALTH_PUB_DEFINE(health_pub_cli, 0);

static void print_client_status_blb_cli(void);
void Print_opcode_errors_cli( struct etn_fota_error *err_report );

static void handle_blb_cli_start(struct bt_mesh_etn_fota_blb_cli *blbcli)
{
	print_client_status_blb_cli();
}

static void handle_blb_info_status(struct bt_mesh_etn_fota_blb_cli *blbcli, struct bt_mesh_msg_ctx *ctx, const struct fota_blb_info_status *info_status)
{
	//LOG_DBG( "fun:handle_blb_info_status" );
	blbcli_info_status_flag = 1;
}

static void handle_blb_transfer_status(struct bt_mesh_etn_fota_blb_cli *blbcli, struct bt_mesh_msg_ctx *ctx, const struct fota_blb_trans_status *trans_status)
{
	//LOG_DBG( "fun: handle_blb_transfer_status");
	//LOG_DBG("received response from server %d\n", firm_val.server_id);
	
	blbcli_blob_trans_status_flag = 1;

}

static void handle_blb_block_status(struct bt_mesh_etn_fota_blb_cli *blbcli, struct bt_mesh_msg_ctx *ctx, const struct fota_blb_block_status *block_status)
{
	//LOG_DBG( "fun: handle_blb_block_status");
	LOG_DBG("received response from server %d\n", firm_val.server_id);

	blb_cli_block_status.status = block_status->status;
	blb_cli_block_status.rfu = block_status->rfu;
	blb_cli_block_status.format = block_status->format;
	blb_cli_block_status.block_number = block_status->block_number; 
	blb_cli_block_status.chunk_size = block_status->chunk_size;
	blb_cli_block_status.missing_chunks = block_status->missing_chunks;
	blb_cli_block_status.encoded_missing_chunks = block_status->encoded_missing_chunks;

	/* yogesh : to do validation at cli driver level */

	blbcli_block_trans_status_flag = 1;
}

static void handle_blb_block_partial_report( struct bt_mesh_etn_fota_blb_cli *blbcli, struct bt_mesh_msg_ctx *ctx, const struct fota_blb_block_prtl_rpt *partial_rpt )
{
	//LOG_DBG("block partial report");
	LOG_DBG("partial_rpt.encoded_missing_chunks :%02X", partial_rpt->encoded_missing_chunks);
	blb_chunk_ack = 1;
	blbcli_chunk_partial_flag=1;
}

static void handle_dis_cli_start( struct bt_mesh_etn_fota_dis_cli* discli )
{
	LOG_DBG( "Fun: handle_dis_cli_start" ); 
	LOG_INF( "Firmware distribution client model started. \n" );
}

static void handle_dis_cli_dfu_start ( enum fota_dis_cmd_status *dfu_cmd_reply )
{
	LOG_DBG( "Fun: handle_dis_cli_dfu_start" );
	LOG_INF( "Firmware distribution client model received DFU command %d .\n", *dfu_cmd_reply );
	discli_dfu_cmd_flag=1;
}

static void handle_dis_cli_metadata( enum fota_dis_srv_metadata_status *metadata_reply )
{
	LOG_DBG( "Fun: handle_dis_cli_metadata: %d", *metadata_reply );
	discli_metadata_flag=1;
}

static void handle_dis_cli_distribution( enum fota_dis_list_status *dstrbn_list_reply )
{
	LOG_DBG( "Fun: handle_dis_cli_distribution %d", *dstrbn_list_reply );
}

static void handle_dis_cli_state( enum fota_dis_srv_state *model_state )
{
	LOG_DBG( "Fun: handle_dis_cli_state %d", *model_state );
}

static void handle_dis_cli_error_reporting( struct bt_mesh_etn_fota_dis_cli *discli, struct bt_mesh_msg_ctx *ctx, struct etn_fota_error *err_report )
{
	LOG_DBG("fun: handle_dis_cli_error_reporting");
	//LOG_DBG( "fota err report opcode:%u and err:%d", err_report->opcode, err_report->opcode );
	Print_opcode_errors_cli(err_report);
	/* yogesh: to do  */
}

static void handle_upd_cli_start( struct bt_mesh_etn_fota_upd_cli *updcli )
{
	LOG_DBG( "Fun: handle_upd_cli_start" );
}

static void handle_upd_cli_percentage_progress( uint8_t *progress_in_percent )
{
	/* yogesh remove this */
	//LOG_DBG( "Fun: handle_upd_cli_percentage_progress: %d", *progress_in_percent );
	updcli_progress_status_flag = 1;

	progress_percent = 	 *progress_in_percent;
}

static void handle_upd_cli_state_get( enum fota_upd_srv_state *server_state )
{
	LOG_DBG( "Fun: handle_upd_cli_state_get : %d", *server_state );
}

static void handle_upd_cli_error_reporting( struct bt_mesh_etn_fota_upd_cli *updcli, struct bt_mesh_msg_ctx *ctx, struct etn_fota_error *err_report )
{
	LOG_DBG("fun: handle_upd_cli_error_reporting");
	//LOG_DBG( "fota err report opcode:%u and err:%d", err_report->opcode, err_report->opcode );
	Print_opcode_errors_cli(err_report);

	/* yogesh: to do  */
}

static void handle_blb_error_reporting( struct bt_mesh_etn_fota_blb_cli *blbcli, struct bt_mesh_msg_ctx *ctx, struct etn_fota_error *err_report )
{
	LOG_DBG("fun: handle_blb_error_reporting");
	//LOG_DBG( "fota err report opcode:%u and err:%d", err_report->opcode, err_report->opcode );
	Print_opcode_errors_cli(err_report);

	/* yogesh: to do  */
}

static const struct bt_mesh_etn_fota_dis_cli_handlers discli_handlers = {
	.start = handle_dis_cli_start,
	.dfu_start = handle_dis_cli_dfu_start,
	.metadata_get = handle_dis_cli_metadata,
	.distribn_get = handle_dis_cli_distribution,
	.server_state_get = handle_dis_cli_state,
	.error_reporting = handle_dis_cli_error_reporting,
};

static const struct bt_mesh_etn_fota_blb_cli_handlers blbcli_handlers = {
	.start = handle_blb_cli_start,
	.blb_info_status = handle_blb_info_status,
	.blb_transfer_status = handle_blb_transfer_status,
	.blb_block_status = handle_blb_block_status,
	.blb_block_partial_report = handle_blb_block_partial_report,
	.error_reporting = handle_blb_error_reporting,
};

static const struct bt_mesh_etn_fota_upd_cli_handlers updcli_handlers = {
	.start = handle_upd_cli_start,
	.image_progress_get = handle_upd_cli_percentage_progress,
	.server_state_get = handle_upd_cli_state_get,
	.error_reporting = handle_upd_cli_error_reporting,
};

static struct bt_mesh_etn_fota_dis_cli discli = {
	.handlers = &discli_handlers,
};


static struct bt_mesh_etn_fota_blb_cli blbcli = {
	.handlers = &blbcli_handlers,
};

static struct bt_mesh_etn_fota_upd_cli updcli = {
	.handlers = &updcli_handlers,
};

static struct bt_mesh_elem elements_cli[] = {
	BT_MESH_ELEM(
		1,
		BT_MESH_MODEL_LIST(
			BT_MESH_MODEL_CFG_SRV,
			BT_MESH_MODEL_HEALTH_SRV(&health_srv_cli, &health_pub_cli)),
		BT_MESH_MODEL_LIST( BT_MESH_MODEL_ETN_FOTA_DIS_CLI(&discli),
							BT_MESH_MODEL_ETN_FOTA_BLB_CLI(&blbcli),
							BT_MESH_MODEL_ETN_FOTA_UPD_CLI(&updcli))),
	
};

static void blbcli_opcode_dfu_cmd_send(void)
{
	int err=0;
	uint8_t data_valid=0;

	data_valid = 0;
	if( get_server_id() != 0)
	{
		firm_val.server_id = get_server_id();
		firm_val.miss_these_chunks = get_miss_chunks();
		data_valid = 1;
	}
	else
	{
		LOG_DBG(" wrong server id:%d", get_server_id());
	}

	if(data_valid )
	{
		/* send dfu command to server */
		err = bt_mesh_etn_fota_dis_cli_update_start_send( &discli, firm_val.server_id );
		if (err) 
		{
			LOG_DBG("send dfu cmd to server failed: %d", err);
		}
		else
		{
			blbcli_wait_timeout = 0;
			blbcli_fw_state = FW_STATE_DFU_CMD_WAIT;
		}
	}
}

static void blbcli_opcode_dfu_cmd_wait(void)
{
	/* dis cli dfu cmd status */
	if( discli_dfu_cmd_flag ) 
	{
		discli_dfu_cmd_flag =0;
		blbcli_fw_state = FW_STATE_DFU_CMD_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive dfu cmd from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_DFU_CMD_SEND;
		}
	}
}

static void blbcli_opcode_prepare_metadata(void)
{

	LOG_DBG("fun:blbcli_opcode_prepare_metadata");

	if(get_firmware_size() != 0)
	{
		dis_cli_metatdata.sz_of_fw_image = get_firmware_size();
		//cal number of blocks
		dis_cli_metatdata.num_of_blocks = get_firmware_size() / MAX_BLOCK_SIZE_IN_BYTES;
		if( get_firmware_size() % MAX_BLOCK_SIZE_IN_BYTES )
		{
			dis_cli_metatdata.num_of_blocks++;
		}
		dis_cli_metatdata.image_version_dec = FW_VER_MINOR;
		dis_cli_metatdata.image_version_int = FW_VER_MAJOR;
		dis_cli_metatdata.user_id = firm_val.server_id;
		dis_cli_metatdata.TTL = 5;
		dis_cli_metatdata.timeout = 255;

		blbcli_fw_state = FW_STATE_METADATA_SEND;
	}
	else
	{
		LOG_DBG("Firmware size is zero");
	}
}

static void blbcli_opcode_metadata_send(void)
{
	int err;

	err = bt_mesh_etn_fota_dis_cli_metadata_send( &discli, firm_val.server_id, &dis_cli_metatdata );
	if (err) 
	{
		LOG_ERR("metadata send failed: %d", err);
	}
	else
	{
		blbcli_wait_timeout = 0;
		blbcli_fw_state = FW_STATE_METADATA_WAIT;
	}
}

static void blbcli_opcode_metadata_wait(void)
{
	/* dis cli metadata status */
	if( discli_metadata_flag ) 
	{
		discli_metadata_flag =0;
		blbcli_fw_state = FW_STATE_METADATA_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive metadata from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_METADATA_SEND;
		}
	}
}

static void blbcli_prepare_data_for_blb_tarnsfer(void)
{
	uint32_t value;

	firm_val.file_size_in_bytes = dis_cli_metatdata.sz_of_fw_image;

	firm_val.num_of_blobs = firm_val.file_size_in_bytes / MAX_BLOB_SIZE_IN_BYTES;
	value = firm_val.file_size_in_bytes % MAX_BLOB_SIZE_IN_BYTES;

	firm_val.num_of_blocks_remaining = value / MAX_BLOCK_SIZE_IN_BYTES;
	value = value % MAX_BLOCK_SIZE_IN_BYTES;

	firm_val.num_of_chunks_remaining = value / BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
	firm_val.num_of_byte_remaining = value % BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;

	firm_val.blob_counter = 0;
	firm_val.block_counter = 0;
	firm_val.block_max_counter=0;
	firm_val.chunk_max_counter=0;
	firm_val.chunk_number = 0;
	firm_val.flash_addr =0;
	firm_val.flash_length= BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;

	firm_val.blob_main_comp_flag = 0;
    firm_val.block_main_comp_flag = 0;
    firm_val.chunk_main_comp_flag = 0;

	LOG_DBG( "fun: blbcli_prepare_data_for_blb_tarnsfer\n");
	LOG_DBG("size:%u bytes num of blobs:%d ",firm_val.file_size_in_bytes,firm_val.num_of_blobs);
	LOG_DBG("num of blocks remain:%d num of chunks remain:%d num of bytes remain:%d\n", firm_val.num_of_blocks_remaining,firm_val.num_of_chunks_remaining,firm_val.num_of_byte_remaining);
}


static void blbcli_opcode_transfer_start(void)
{
	int err;

	blb_cli_trans_start.rfu = 0;
	blb_cli_trans_start.trans_mode = TRANSFER_MODE_PUSH;
	blb_cli_trans_start.blob_id = firm_val.blob_counter;

	/* calculate blob size */
	if( firm_val.blob_counter > ( firm_val.num_of_blobs - 1 ) )
	{
		blb_cli_trans_start.blob_size = 0;
		blb_cli_trans_start.blob_size += ( firm_val.num_of_blocks_remaining * MAX_BLOCK_SIZE_IN_BYTES );
		blb_cli_trans_start.blob_size += ( firm_val.num_of_chunks_remaining * BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE );
		blb_cli_trans_start.blob_size += firm_val.num_of_byte_remaining;
	}
	else
	{
		blb_cli_trans_start.blob_size = MAX_BLOB_SIZE_IN_BYTES;
	}
	
	blb_cli_trans_start.block_size_log = 11; //log2(MAX_BLOCK_SIZE_IN_BYTES)
	blb_cli_trans_start.cli_mtu_size = BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_MSG_MINLEN;

	LOG_DBG( "current blob transfer mode:%d id:%llu size:%u\n", blb_cli_trans_start.trans_mode, blb_cli_trans_start.blob_id, blb_cli_trans_start.blob_size);
	err = bt_mesh_etn_fota_blb_cli_blb_trans_start_send( &blbcli, firm_val.server_id, &blb_cli_trans_start );
	if (err) 
	{
		LOG_DBG("send test opcode blob transfer start failed: %d", err);
	}
	else
	{
		blbcli_fw_state = FW_STATE_BLOB_TRANS_START_WAIT;
		blbcli_wait_timeout = 0;
	}
}

static void blb_cli_opcode_blob_start_wait( void )
{
	/* blob server blob transfer status */
	if( blbcli_blob_trans_status_flag ) 
	{
		blbcli_blob_trans_status_flag =0;
		blbcli_fw_state = FW_STATE_BLOB_TRANS_START_DONE;
	}
	else
	{
		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive chunk receive response from %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
		}
	}
}

static void blb_cli_opcode_cal_block_send(void)
{

	firm_val.block_max_counter=0;

	if( blb_cli_trans_start.blob_size ==  MAX_BLOB_SIZE_IN_BYTES )
	{
		firm_val.block_to_send_in_blob = MAX_BLOCKS_IN_BLOB;
	}
	else
	{
		firm_val.block_to_send_in_blob = 0;
		firm_val.block_to_send_in_blob += firm_val.num_of_blocks_remaining;
		if( firm_val.num_of_chunks_remaining || firm_val.num_of_byte_remaining )
		{
			firm_val.block_to_send_in_blob += 1;
		}
	}

	LOG_DBG("number block to send for current blob:%d\n", firm_val.block_to_send_in_blob);
}


static void blbcli_opcode_block_start(void)
{
	int err;

	blb_cli_block_start.block_number = firm_val.block_counter;
	blb_cli_block_start.chunk_size = BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_MSG_MINLEN;
	LOG_DBG("block transfer block:%d chunk size:%d\n", blb_cli_block_start.block_number, blb_cli_block_start.chunk_size);
	err = bt_mesh_etn_fota_blb_block_start_send( &blbcli, firm_val.server_id, &blb_cli_block_start );
	if (err) 
	{
		LOG_DBG("send test opcode blob block start failed: %d", err);
	}
	else
	{
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_START_WAIT;
		blbcli_wait_timeout = 0;
	}
}

static void blbcli_opcode_block_start_wait( void )
{
	/* blob server blob transfer status */
	if( blbcli_block_trans_status_flag ) 
	{
		blbcli_block_trans_status_flag =0;
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_START_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive block status from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_BLOCK_TRANS_START_SEND;
		}
	}
}

static void blb_cli_opcode_cal_chunk_send(void)
{
	firm_val.chunk_max_counter = 0;
	firm_val.send_chunk_shift = 1;
	firm_val.send_chunks_bits = 0;

	/* if main block is not completed then block size and chunk is max */
	if( blb_cli_trans_start.blob_size ==  MAX_BLOB_SIZE_IN_BYTES )
	{
		firm_val.curr_block_size = MAX_CHUNK_IN_BLOCK * BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
		firm_val.chunk_to_send_in_block = MAX_CHUNK_IN_BLOCK;
	}
	else
	{
		if( firm_val.block_main_comp_flag == 0 )
		{
			firm_val.chunk_to_send_in_block = MAX_CHUNK_IN_BLOCK;
		}
		else
		{
			firm_val.curr_block_size = 0;
			firm_val.curr_block_size += firm_val.num_of_chunks_remaining * BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
			firm_val.curr_block_size += firm_val.num_of_byte_remaining;

			firm_val.chunk_to_send_in_block = 0;
			firm_val.chunk_to_send_in_block = firm_val.num_of_chunks_remaining;
			if( firm_val.num_of_byte_remaining )
			{
				firm_val.chunk_to_send_in_block++;
			}
		}
		
	}
}

static void blbcli_opcode_all_chunk_transfer(uint16_t miss_few_chunks)
{
	int err=0;
	int loop_cntr;
	struct fota_blb_data_flash flash_data;
	uint8_t data_valid=0;
	int buf_index_inc=0;
	uint8_t whileloopcntr=0;
	uint32_t timenow;

	LOG_DBG("here:%d\n",loop_cntr);

	while(whileloopcntr < MAX_CHUNK_IN_BLOCK)
	{
		timenow = k_uptime_get_32();
		/*u_data.tx_cmd = UART_READ_BYTES;
		u_data.addr = firm_val.flash_addr;
		u_data.rx_done = 0;
		data_valid = 0;

		LOG_DBG("cntr:%d\n", whileloopcntr);
		
		uart1_get_data(&u_data);
		
		if( u_data.rx_done )
		{
			u_data.rx_done = 0;
			
			if(u_data.response_code == UART_READ_BYTES )
			{
				if( u_data.rx_cmd == UART_ERR_NONE )
				{
					data_valid = 1;
				}	
			}
			else
			{
				u_data.rx_cmd = UART_ERR_WRNG_RES;
			}
		}
		else
		{
			//LOG_DBG("no response from uart\n");
			u_data.rx_cmd = UART_ERR_RESPONSE;
		}*/

		if( data_valid )
		{
			flash_data.chunk_number = firm_val.chunk_number;
			flash_data.address = firm_val.flash_addr;
			flash_data.length = firm_val.flash_length;
			
			buf_index_inc = 4;
			for( loop_cntr=0; loop_cntr < BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE; loop_cntr++ )
			{
				//flash_data.data[loop_cntr] = u_data.buf[buf_index_inc];
				//flash_data_backup[firm_val.chunk_max_counter].data[loop_cntr] = u_data.buf[buf_index_inc];
				buf_index_inc++;	
			}

			/* flash data backup */
			flash_data_backup[firm_val.chunk_max_counter].chunk_number = flash_data.chunk_number;
			flash_data_backup[firm_val.chunk_max_counter].address = flash_data.address;
			flash_data_backup[firm_val.chunk_max_counter].length = flash_data.length;


			/* yogesh missing chunk logic remove later */
			if( miss_few_chunks & ( 1 << firm_val.chunk_max_counter ) )
			{
				err = bt_mesh_etn_fota_blb_cli_chunk_data_send(&blbcli, firm_val.server_id, &flash_data);
				if( err )
				{
					LOG_ERR("blob chunk transfer failed err: %d chunk number:%d\n", err, flash_data.chunk_number);
				}
				else
				{
					firm_val.chunk_max_counter++;
					firm_val.chunk_number++;
					firm_val.transferd_bytes = firm_val.flash_addr + BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
					firm_val.flash_addr += BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
					firm_val.send_chunks_bits |= firm_val.send_chunk_shift;
					firm_val.send_chunk_shift <<=1;
					LOG_DBG("sucess:%d", whileloopcntr);
					whileloopcntr++;
				}
			}
			else
			{
				LOG_DBG("miss chunk:%d\n", firm_val.chunk_max_counter);
			}
		}

		LOG_DBG("time:%d\n", (k_uptime_get_32() - timenow ));
	}

}

static void blbcli_opcode_chunk_transfer( uint16_t miss_few_chunks )
{
	int err=0;
	int loop_cntr;
	struct fota_blb_data_flash flash_data;
	uint8_t data_valid=0;
	uint16_t crc;

	flash_data.chunk_number = firm_val.chunk_number;
	flash_data.address = firm_val.flash_addr;
	flash_data.length = firm_val.flash_length;

	if( get_firmware_data_from_external_flash(&flash_data) == 0)
	{
		data_valid = 1;
	}

	if( data_valid )
	{
		flash_data.chunk_number = firm_val.chunk_number;
		flash_data.address = firm_val.flash_addr;
		
		crc = cal_crc16(&flash_data.data,BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE, 0);
		//printk("CRC:%02X\n", crc);
		flash_data.length = firm_val.flash_length;
		flash_data.length <<= 16;
		flash_data.length |= crc;

		for( loop_cntr=0; loop_cntr < BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE; loop_cntr++ )
		{
			flash_data_backup[firm_val.chunk_max_counter].data[loop_cntr] = flash_data.data[loop_cntr];
		}

		/* flash data backup */
		flash_data_backup[firm_val.chunk_max_counter].chunk_number = flash_data.chunk_number;
		flash_data_backup[firm_val.chunk_max_counter].address = flash_data.address;
		flash_data_backup[firm_val.chunk_max_counter].length = flash_data.length;


		/* yogesh missing chunk logic remove later */
		if( miss_few_chunks & ( 1 << firm_val.chunk_max_counter ) )
		{
			err = bt_mesh_etn_fota_blb_cli_chunk_data_send(&blbcli, firm_val.server_id, &flash_data);
			if( err )
			{
				LOG_ERR("blob chunk transfer failed err: %d chunk number:%d\n", err, flash_data.chunk_number);
			}
			else
			{
				firm_val.chunk_max_counter++;
				firm_val.chunk_number++;
				firm_val.transferd_bytes = firm_val.flash_addr + BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
				firm_val.flash_addr += BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
				firm_val.send_chunks_bits |= firm_val.send_chunk_shift;
				firm_val.send_chunk_shift <<=1;
			}
		}
		else
		{
			LOG_DBG("miss chunk:%d\n", firm_val.chunk_max_counter);
			firm_val.chunk_max_counter++;
			firm_val.chunk_number++;
			firm_val.transferd_bytes = firm_val.flash_addr + BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
			firm_val.flash_addr += BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
			firm_val.send_chunks_bits |= firm_val.send_chunk_shift;
			firm_val.send_chunk_shift <<=1;
		}
	}
}

static void blbcli_opcode_chunk_send( uint16_t value )
{
	blbcli_opcode_chunk_transfer(value);
	//blbcli_opcode_all_chunk_transfer(value);

	if( firm_val.chunk_max_counter < firm_val.chunk_to_send_in_block )
	{
		blbcli_fw_state = FW_STATE_BLOCK_CHUNK_TRANS_SEND;
	}
	else
	{
		blbcli_fw_state = FW_STATE_BLOCK_CHUNK_TRANS_DONE;
		
		if( firm_val.block_main_comp_flag )
		{
			if( firm_val.chunk_max_counter > (firm_val.num_of_chunks_remaining - 1) )
			{
				LOG_DBG("remaining chunks completed\n");
			}
		}
	}
}
static void blbcli_opcode_block_get(void)
{
	int err;

	err = bt_mesh_etn_fota_blb_blb_block_get_send( &blbcli, firm_val.server_id );
	if (err) 
	{
		LOG_DBG("send test opcode blob block get failed: %d\n", err);
	}
	else
	{
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_GET_WAIT;
		blbcli_wait_timeout = 0;
	}
}

static void blbcli_opcode_block_get_wait(void)
{
	/* blob server blob transfer status */
	if( blbcli_block_trans_status_flag ) 
	{
		blbcli_block_trans_status_flag =0;
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_GET_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive block status from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_BLOCK_TRANS_GET_SEND;
		}
	}
}

static uint16_t blbcli_check_missing_chunks(void)
{
	return blb_cli_block_status.missing_chunks;
}

static void blbcli_send_missing_chunk(uint16_t value)
{
	uint8_t counter=0;
	int err;

	while( value )
	{
		value >>= 1;
		counter++;
	}

	counter = counter - 1;
	
	err = bt_mesh_etn_fota_blb_cli_chunk_data_send(&blbcli, firm_val.server_id, &flash_data_backup[counter]);
	if( err )
	{
		LOG_ERR("blob chunk transfer failed err: %d chunk number:%x\n", err, flash_data_backup[counter].chunk_number);
	}
}

static void blbcli_opcode_check_missing_chunks(void)
{

	firm_val.missing_chunks = blbcli_check_missing_chunks();
	firm_val.missing_chunks &= firm_val.send_chunks_bits;

	LOG_DBG("checking missing chunks missing:%x bits:%x\n", firm_val.missing_chunks, firm_val.send_chunks_bits);

	if( firm_val.missing_chunks )
	{
		blbcli_fw_state = FW_STATE_CHUNK_TRANS_MISS_SEND;
		firm_val.chunk_miss_mask = 1;
	}
	else
	{
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_COMPLETE;
	}
}

static void blbcli_opcode_send_missing_chunks(void)
{

	if( firm_val.chunk_miss_mask & blb_cli_block_status.missing_chunks )
	{
		blbcli_send_missing_chunk( firm_val.chunk_miss_mask & blb_cli_block_status.missing_chunks );
		firm_val.chunk_miss_mask <<= 1;
	}

	if( firm_val.chunk_miss_mask != firm_val.send_chunk_shift )
	{
		while( (firm_val.chunk_miss_mask != firm_val.send_chunk_shift) & ((firm_val.chunk_miss_mask & blb_cli_block_status.missing_chunks) == 0) )
		{
			firm_val.chunk_miss_mask <<= 1;
		}
	}
	else
	{
		blbcli_fw_state = FW_STATE_CHUNK_TRANS_MISS_DONE;
	}
}

static void blbcli_opcode_block_transfer_complete(void)
{
	firm_val.block_counter++;
	firm_val.block_max_counter++;
	firm_val.chunk_max_counter = 0;
	
	/* if main blob is completed then check for remaining blocks */
	if(firm_val.blob_main_comp_flag )
	{
		if( firm_val.block_max_counter > (firm_val.num_of_blocks_remaining - 1) )
		{
			firm_val.block_main_comp_flag = 1;
			LOG_DBG("remaining blocks completed\n");
		}
	}
	
	if( firm_val.block_max_counter < firm_val.block_to_send_in_blob )
	{
		blbcli_fw_state = FW_STATE_BLOCK_TRANS_START_SEND;
	}
	else
	{
		LOG_DBG("blob transfer completed\n");
		firm_val.block_to_send_in_blob = 0;

		if(firm_val.blob_main_comp_flag == 0 )
		{	
			firm_val.block_max_counter = 0;
			firm_val.blob_counter++;

			// check for number of blobs 
			if( firm_val.blob_counter > ( firm_val.num_of_blobs - 1 ) )
			{
				firm_val.blob_main_comp_flag = 1;
				LOG_DBG("main blobs completed\n");
				if( firm_val.num_of_blocks_remaining == 0 )
				{
					firm_val.block_main_comp_flag = 1;
					LOG_DBG("no remaining blocks so flag marked completed\n");
				}
				if( firm_val.num_of_chunks_remaining == 0 )
				{
					firm_val.chunk_main_comp_flag = 1;
					LOG_DBG("no remaining chunks so flag marked completed\n");
				}
			}
			blbcli_fw_state = FW_STATE_BLOB_TRANS_COMPLETE;
		}
		else
		{
			blbcli_fw_state = FW_STATE_FIRMWARE_TRANS_COMPLETED;
			LOG_DBG("firmware transfer completed \n");
		}	
	}
}

static void blbcli_opcode_blb_trans_complete(void)
{
	LOG_DBG("fun:blbcli_opcode_blb_trans_complete\n");
	if(firm_val.blob_main_comp_flag == 0 )
	{
		blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
	}
	else
	{
		if( firm_val.block_main_comp_flag == 0)
		{
			if( firm_val.num_of_blocks_remaining || firm_val.num_of_chunks_remaining || firm_val.num_of_byte_remaining )
			{
				LOG_DBG("few more data to be sent \n");
				blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
			}
			else
			{
				LOG_DBG("no more remaing chunks or bytes all blb completd\n");
				blbcli_fw_state = FW_STATE_FIRMWARE_TRANS_COMPLETED;
				LOG_DBG("firmware transfer completed \n");
			}	
		}
		else
		{
			if( firm_val.chunk_main_comp_flag == 0)
			{
				if( firm_val.num_of_chunks_remaining || firm_val.num_of_byte_remaining )
				{
					LOG_DBG("few more data to be sent \n");
					blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
				}
				else
				{
					LOG_DBG("no more remaing chunks or bytes all blb completd\n");
					blbcli_fw_state = FW_STATE_FIRMWARE_TRANS_COMPLETED;
					LOG_DBG("firmware transfer completed \n");
				}
			}
			else
			{
				/* yogesh if( firm_val.num_of_byte_remaining )
				{
					LOG_DBG("few more data to be sent \n");
					blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
				}
				else
				{
					LOG_DBG("no more remaing chunks or bytes all blb completd\n");
					blbcli_fw_state = FW_STATE_HOLD_FOR_DEBUG;
					LOG_DBG("hold for debug\n");
				}*/

				LOG_DBG("no more remaing chunks or bytes all blb completd\n");
				blbcli_fw_state = FW_STATE_FIRMWARE_TRANS_COMPLETED;
				LOG_DBG("firmware transfer completed \n");
			}
		}
	}
}

static void blbcli_opcode_progress_get_send(void)
{
	int err=0;
	
	/* send progress get command to server */
	err = bt_mesh_etn_fota_upd_cli_progress_get_send( &updcli, firm_val.server_id );
	if (err) 
	{
		LOG_DBG("send progress get command to server failed: %d", err);
	}
	else
	{
		blbcli_wait_timeout = 0;
		blbcli_fw_state = FW_STATE_PROGRESS_WAIT_RES;
	}
	
}

static void blbcli_opcode_progress_request_wait(void)
{
	/* progress status flag */
	if( updcli_progress_status_flag ) 
	{
		updcli_progress_status_flag =0;
		blbcli_fw_state = FW_STATE_PROGRESS_GET_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive progress get from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_PROGRESS_REQUEST;
		}
	}
}

static int blbcli_opcode_info_get_send( void )
{
	int err;
	
	LOG_DBG("fun:blbcli_test_opcode_info_get");

	err = bt_mesh_etn_fota_blb_info_status_get_send( &blbcli, firm_val.server_id );
	if (err) 
	{
		LOG_ERR("blob info get failed: %d", err);
	}
	else
	{
		blbcli_wait_timeout = 0;
		blbcli_fw_state = FW_STATE_BLOB_INFO_WAIT;
	}

	return 0;
}

static void blbcli_opcode_info_get_wait(void)
{
	/* info get flag flag */
	if( blbcli_info_status_flag ) 
	{
		blbcli_info_status_flag =0;
		blbcli_fw_state = FW_STATE_BLOB_INFO_DONE;
	}
	else
	{
		/* yogesh: to do "handle max fail to count for failure " */

		/*timeout logic */
		blbcli_wait_timeout = blbcli_wait_timeout + BLB_CLI_SEND_OPCODE_WORK_DELAY;
		if( blbcli_wait_timeout > CLI_WAIT_TIMEOUT )
		{
			LOG_DBG( "failed to receive info get from addr %d\n", firm_val.server_id );
			blbcli_fw_state = FW_STATE_BLOB_INFO_SEND;
		}
	}
}

static int blbcli_opcode_reboot_request_send( void )
{
	int err;
	
	LOG_DBG("fun:blbcli_opcode_reboot_request_send");

	err = bt_mesh_etn_fota_blb_cli_blob_reboot_request_send( &blbcli, firm_val.server_id, 1 );
	if (err) 
	{
		LOG_ERR("device reboot request failed: %d", err);
	}
	else
	{
		blbcli_fw_state = FW_STATE_HOLD_FOR_DEBUG;
		client_state_set(CLIENT_STATE_RUN_TRANSFER_COMPLETE);
	}

	return 0;
}

void start_firmware_transfer_state_machine_in_handler(void)
{
	LOG_DBG("Firmware state machine started\n");
	blbcli_fw_state = FW_STATE_DFU_CMD_SEND;
}

static void blbcli_send_firmware_with_state_machine( void )
{
	switch ( blbcli_fw_state )
	{
		case FW_STATE_NONE:
			//LOG_DBG("\nfirmware transfer with state machine\n");
			//blbcli_fw_state = FW_STATE_DFU_CMD_SEND;
			break;

		case FW_STATE_DFU_CMD_SEND:
			blbcli_opcode_dfu_cmd_send();
			break;

		case FW_STATE_DFU_CMD_WAIT:
			blbcli_opcode_dfu_cmd_wait();
			break;

		case FW_STATE_DFU_CMD_DONE:
			blbcli_opcode_prepare_metadata();
			break;

		case FW_STATE_METADATA_SEND:
			blbcli_opcode_metadata_send();
			break;

		case FW_STATE_METADATA_WAIT:
			blbcli_opcode_metadata_wait();
			break;

		case FW_STATE_METADATA_DONE:
			blbcli_fw_state = FW_STATE_PREPARE_DATA;
			break;

		case FW_STATE_PREPARE_DATA:
			blbcli_prepare_data_for_blb_tarnsfer();
			blbcli_fw_state = FW_STATE_BLOB_TRANS_START_SEND;
			break;

		case FW_STATE_BLOB_TRANS_START_SEND:
			blbcli_opcode_transfer_start();
			break;

		case FW_STATE_BLOB_TRANS_START_WAIT:
			blb_cli_opcode_blob_start_wait();
			break;

		case FW_STATE_BLOB_TRANS_START_DONE:
			blb_cli_opcode_cal_block_send();
			blbcli_fw_state = FW_STATE_BLOCK_TRANS_START_SEND;
			break;

		case FW_STATE_BLOCK_TRANS_START_SEND:
			blbcli_opcode_block_start();
			break;

		case FW_STATE_BLOCK_TRANS_START_WAIT:
			blbcli_opcode_block_start_wait();
			break;
		
		case FW_STATE_BLOCK_TRANS_START_DONE:
			blb_cli_opcode_cal_chunk_send();
			blbcli_fw_state = FW_STATE_BLOCK_CHUNK_TRANS_SEND;
			break;

		case FW_STATE_BLOCK_CHUNK_TRANS_SEND:
			blbcli_opcode_chunk_send(firm_val.miss_these_chunks); //value is for stimulate missing chunks
			break;

		case FW_STATE_BLOCK_CHUNK_TRANS_DONE:
			blbcli_fw_state = FW_STATE_BLOCK_TRANS_GET_SEND;
			break;
		
		case FW_STATE_BLOCK_TRANS_GET_SEND:
			blbcli_opcode_block_get();
			break;

		case FW_STATE_BLOCK_TRANS_GET_WAIT:
			blbcli_opcode_block_get_wait();
			break;

		case FW_STATE_BLOCK_TRANS_GET_DONE:
			blbcli_opcode_check_missing_chunks();	
			break;

		case FW_STATE_CHUNK_TRANS_MISS_SEND:
			blbcli_opcode_send_missing_chunks();
			break;

		case FW_STATE_CHUNK_TRANS_MISS_DONE:
			blbcli_fw_state = FW_STATE_BLOCK_TRANS_GET_SEND;
			break;

		case FW_STATE_BLOCK_TRANS_COMPLETE:
			blbcli_opcode_block_transfer_complete();
			break;

		case FW_STATE_BLOB_TRANS_COMPLETE:
			blbcli_opcode_blb_trans_complete();
			break;

		case FW_STATE_FIRMWARE_TRANS_COMPLETED:
			blbcli_fw_state = FW_STATE_PROGRESS_REQUEST;
			break;

		case FW_STATE_PROGRESS_REQUEST:
			blbcli_opcode_progress_get_send();
			break;

		case FW_STATE_PROGRESS_WAIT_RES:
			blbcli_opcode_progress_request_wait();
			break;

		case FW_STATE_PROGRESS_GET_DONE:
			if( progress_percent == 100 )
			{
				LOG_DBG("firmware transfer success go for reset the device\n");
				blbcli_fw_state = FW_STATE_REBOOT_SEND;
			}
			else
			{
				LOG_DBG("firmware file trasfer is not 100 percent\n");
				blbcli_fw_state = FW_STATE_HOLD_FOR_DEBUG;
			}
			break;

		case FW_STATE_BLOB_INFO_SEND:
			LOG_DBG("BLOB_INFO_SEND\n");
			blbcli_opcode_info_get_send();
			break;

		case FW_STATE_BLOB_INFO_WAIT:
			LOG_DBG("BLOB_INFO_WAIT\n");
			blbcli_opcode_info_get_wait();
			break;

		case FW_STATE_BLOB_INFO_DONE:
			LOG_DBG("BLOB_INFO_DONE\n");
			blbcli_fw_state = FW_STATE_HOLD_FOR_DEBUG;
			break;

		case FW_STATE_REBOOT_SEND:
			LOG_DBG("reboot request\n");
			blbcli_opcode_reboot_request_send();
			break;

		case FW_STATE_HOLD_FOR_DEBUG:
			break;

		default:
			blbcli_fw_state = FW_STATE_NONE;
			break;
	}
}

static void fota_blb_srv_update_firmware_percentage_complete(void)
{
	if( firm_val.file_size_in_bytes == 0 )
	{
		firm_val.percent_complete = 0;
		//LOG_DBG( "expected fw size is zero" );
	}
	else
	{
		firm_val.percent_complete = (float)((float)firm_val.transferd_bytes / (float)firm_val.file_size_in_bytes) * (float)100;
	}

	if( firm_val.percent_complete > 100 )
	{
		firm_val.percent_complete = 100;
	}

}

static void blbcli_print_curr_cli_status(void)
{	
	uint8_t len= 250;
	char buf[len];

	fota_blb_srv_update_firmware_percentage_complete();

	//LOG_DBG("file size:%u blocks:%04d server:%02d miss_chunk:%02X ", firm_val.file_size_in_bytes,dis_cli_metatdata.num_of_blocks,dis_cli_metatdata.user_id, firm_val.miss_these_chunks);
	snprintf(buf, len,"file size:%u blocks:%04d server:%02d miss_chunk:%02X ", firm_val.file_size_in_bytes,dis_cli_metatdata.num_of_blocks,dis_cli_metatdata.user_id, firm_val.miss_these_chunks);
	//LOG_DBG("blb:%02d block:%04d chunk:%04d curr block:%04d sent:%u", firm_val.blob_counter, firm_val.block_counter, firm_val.chunk_number, firm_val.block_max_counter, firm_val.transferd_bytes);
	snprintf(buf + strlen(buf), len - strlen(buf), "blb:%02d block:%04d chunk:%04d curr block:%04d sent:%u", firm_val.blob_counter, firm_val.block_counter, firm_val.chunk_number, firm_val.block_max_counter, firm_val.transferd_bytes);
	//LOG_DBG(" completion:%02d%c", firm_val.percent_complete,'%');
	snprintf(buf + strlen(buf), len - strlen(buf), " completion:%02d%c", firm_val.percent_complete,'%');
	//LOG_DBG(" state:");
	snprintf(buf + strlen(buf), len - strlen(buf), " state:");

	switch ( blbcli_fw_state )
	{
		case FW_STATE_NONE:
			//LOG_DBG("NONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "NONE");
			break;

		case FW_STATE_DFU_CMD_SEND:
			//LOG_DBG("DFU_CMD_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "DFU_CMD_SEND");
			break;

		case FW_STATE_DFU_CMD_WAIT:
			//LOG_DBG("DFU_CMD_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "DFU_CMD_WAIT");
			break;

		case FW_STATE_DFU_CMD_DONE:
			//LOG_DBG("DFU_CMD_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "DFU_CMD_DONE");
			break;

		case FW_STATE_METADATA_SEND:
			//LOG_DBG("METADATA_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "METADATA_SEND");
			break;

		case FW_STATE_METADATA_WAIT:
			//LOG_DBG("METADATA_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "METADATA_WAIT");
			break;

		case FW_STATE_METADATA_DONE:
			//LOG_DBG("METADATA_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "METADATA_DONE");
			break;

		case FW_STATE_PREPARE_DATA:
			//LOG_DBG("PREPARE_DATA");
			snprintf(buf + strlen(buf), len - strlen(buf), "PREPARE_DATA");
			break;

		case FW_STATE_BLOB_TRANS_START_SEND:
			//LOG_DBG("BLOB_START_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_START_SEND");
			break;

		case FW_STATE_BLOB_TRANS_START_WAIT:
			//LOG_DBG("BLOB_START_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_START_WAIT");
			break;

		case FW_STATE_BLOB_TRANS_START_DONE:
			//LOG_DBG("BLOB_START_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_START_DONE");
			break;

		case FW_STATE_BLOCK_TRANS_START_SEND:
			//LOG_DBG("BLOCK_START_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_START_SEND");
			break;

		case FW_STATE_BLOCK_TRANS_START_WAIT:
			//LOG_DBG("BLOCK_START_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_START_WAIT");
			break;
		
		case FW_STATE_BLOCK_TRANS_START_DONE:
			//LOG_DBG("BLOCK_START_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_START_DONE");
			break;

		case FW_STATE_BLOCK_CHUNK_TRANS_SEND:
			//LOG_DBG("CHUNK_TRANS_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "CHUNK_TRANS_SEND");
			break;

		case FW_STATE_BLOCK_CHUNK_TRANS_DONE:
			//LOG_DBG("CHUNK_TRANS_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "CHUNK_TRANS_DONE");
			break;
		
		case FW_STATE_BLOCK_TRANS_GET_SEND:
			//LOG_DBG("BLOCK_TRANS_GET_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_TRANS_GET_SEND");
			break;

		case FW_STATE_BLOCK_TRANS_GET_WAIT:
			//LOG_DBG("BLOCK_TRANS_GET_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_TRANS_GET_WAIT");
			break;

		case FW_STATE_BLOCK_TRANS_GET_DONE:
			//LOG_DBG("BLOCK_TRANS_GET_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_TRANS_GET_DONE");
			break;

		case FW_STATE_CHUNK_TRANS_MISS_SEND:
			//LOG_DBG("CHUNK_TRANS_MISS_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "CHUNK_TRANS_MISS_SEND");
			break;

		case FW_STATE_CHUNK_TRANS_MISS_DONE:
			//LOG_DBG("CHUNK_TRANS_MISS_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "CHUNK_TRANS_MISS_DONE");
			break;

		case FW_STATE_BLOCK_TRANS_COMPLETE:
			//LOG_DBG("BLOCK_TRANS_COMPLETE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOCK_TRANS_COMPLETE");
			break;

		case FW_STATE_BLOB_TRANS_COMPLETE:
			//LOG_DBG("BLOB_TRANS_COMPLETE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_TRANS_COMPLETE");
			break;

		case FW_STATE_FIRMWARE_TRANS_COMPLETED:
			//LOG_DBG("FIRMWARE_COMPLETE");
			snprintf(buf + strlen(buf), len - strlen(buf), "FIRMWARE_COMPLETE");
			break;

		case FW_STATE_PROGRESS_REQUEST:
			//LOG_DBG("PROGRESS_REQUEST");
			snprintf(buf + strlen(buf), len - strlen(buf), "PROGRESS_REQUEST");
			break;

		case FW_STATE_PROGRESS_WAIT_RES:
			//LOG_DBG("PROGRESS_WAIT_RES");
			snprintf(buf + strlen(buf), len - strlen(buf), "PROGRESS_WAIT_RES");
			break;

		case FW_STATE_PROGRESS_GET_DONE:
			//LOG_DBG("PROGRESS_GET_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "PROGRESS_GET_DONE");
			break;

		case FW_STATE_BLOB_INFO_SEND:
			//LOG_DBG("BLOB_INFO_SEND");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_INFO_SEND");
			break;

		case FW_STATE_BLOB_INFO_WAIT:
			//LOG_DBG("BLOB_INFO_WAIT");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_INFO_WAIT");
			break;

		case FW_STATE_BLOB_INFO_DONE:
			//LOG_DBG("BLOB_INFO_DONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "BLOB_INFO_DONE");
			break;

		case FW_STATE_REBOOT_SEND:
			//LOG_DBG("REBOOT_REQUEST");
			snprintf(buf + strlen(buf), len - strlen(buf), "REBOOT_REQUEST");
			break;

		case FW_STATE_HOLD_FOR_DEBUG:
			//LOG_DBG("HOLD_FOR_DEBUG");
			snprintf(buf + strlen(buf), len - strlen(buf), "HOLD_FOR_DEBUG");
			break;

		default:
			//LOG_DBG("NONE");
			snprintf(buf + strlen(buf), len - strlen(buf), "NONE");
			break;
	}
    
	//LOG_DBG(" uart:");
	snprintf(buf + strlen(buf), len - strlen(buf), " uart:");
	//u_data.rx_cmd
	int test=0;
	switch (test)
	{
		case UART_ERR_NONE:
			//LOG_DBG("ok");
			snprintf(buf + strlen(buf), len - strlen(buf), "ok");
			break;
		case UART_ERR_ADDR:
			//LOG_DBG("err addr");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_addr");
			break;
		case UART_ERR_LEN:
			//LOG_DBG("err len");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_len");
			break;
		case UART_ERR_FILESIZE:
			//LOG_DBG("err_filesize");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_filesize");
			break;
		case UART_ERR_RESPONSE:
			//LOG_DBG("err_noRes");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_noRes");
			break;
		case UART_ERR_WRNG_RES:
			//LOG_DBG("err_wrongRes");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_wrongRes");
			break;
		default:
			//LOG_DBG("err_unknow");
			snprintf(buf + strlen(buf), len - strlen(buf), "err_unknow");
			break;
	}

	LOG_DBG("%s",buf);
}

static void blbcli_send_opcode(struct k_work *work)
{	
	if (bt_mesh_is_provisioned())
	{	
		blbcli_send_firmware_with_state_machine();
		if((blbcli_fw_state != FW_STATE_HOLD_FOR_DEBUG) && (blbcli_fw_state != FW_STATE_NONE))
		{
			blbcli_print_curr_cli_status();
		}
		k_work_reschedule(&send_opcode_cli, K_MSEC(BLB_CLI_SEND_OPCODE_WORK_DELAY));
	}
	else
	{
		k_work_reschedule(&send_opcode_cli, K_MSEC(BLB_CLI_SEND_OPCODE_WORK_DELAY));
	}
}

static void print_client_status_blb_cli(void)
{
	if (!bt_mesh_is_provisioned()) {
		LOG_DBG( "The mesh node is not provisioned. Please provision the mesh node before using the chat.\n");
	} else {
		LOG_DBG("The mesh node is provisioned. The client address is 0x%04x.\n", bt_mesh_model_elem(blbcli.model)->addr);
	}
}

uint16_t cal_crc16( uint8_t const* data, uint32_t length, uint16_t init_crc )
{
	uint16_t temp_crc;

	temp_crc = init_crc;

    while ( length > 0U )
	{
        length--;
        temp_crc = ( ( temp_crc >> 8 ) ^ table[( (uint8_t)( temp_crc ) ^ *data ) & 0xFFU] );
        data++;
    }

    return ( temp_crc );
}

void Print_opcode_errors_cli( struct etn_fota_error *err_report )
{
	uint8_t len= 100;
	char buf[len];

	switch( err_report->opcode )
	{
		case BT_MESH_ETN_FOTA_BLB_TRANS_GET_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_TRANS_GET" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_TRANS_GET");
		break;

		case BT_MESH_ETN_FOTA_BLB_TRANS_START_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_TRANS_START" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_TRANS_START");
		break;

		case BT_MESH_ETN_FOTA_BLB_TRANS_CANCEL_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_TRANS_CANCEL" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_TRANS_CANCEL");
		break;

		case BT_MESH_ETN_FOTA_BLB_TRANS_STATUS_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_TRANS_STATUS" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_TRANS_STATUS");
		break;

		case BT_MESH_ETN_FOTA_BLB_BLOCK_START_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_BLOCK_START" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_BLOCK_START");
		break;

		case BT_MESH_ETN_FOTA_BLB_BLOCK_STATUS_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_BLOCK_STATUS" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_BLOCK_STATUS");
		break;

		case BT_MESH_ETN_FOTA_BLB_BLOCK_PRL_RPT_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_BLOCK_PRL_RPT" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_BLOCK_PRL_RPT");
		break;

		case BT_MESH_ETN_FOTA_BLB_CHUNK_TRANS_OP:
		//LOG_DBG( "Opcode err: opcode:%s","BLB_CHUNK_TRANS" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_CHUNK_TRANS");
		break;

		case BT_MESH_ETN_FOTA_BLB_BLOCK_GET_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_BLOCK_GET" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_BLOCK_GET");
		break;

		case BT_MESH_ETN_FOTA_BLB_INFO_GET_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_INFO_GET" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_INFO_GET");
		break;

		case BT_MESH_ETN_FOTA_BLB_INFO_STATUS_OP:
		//LOG_DBG( "Opcode err: opcode:%s", "BLB_INFO_STATUS" );
		snprintf(buf, len,"Opcode err: opcode:%s", "BLB_INFO_STATUS");
		break;
	}

	switch (err_report->err_code)
	{
	case FOTA_ERROR_NONE:
		//LOG_DBG(" err:%s\n", "NONE");
		snprintf(buf + strlen(buf), len - strlen(buf), " err:%s\n", "NONE");
		break;

	case FOTA_ERROR_DATA_PARSE:
		//LOG_DBG(" err:%s\n", "DATA_PARSE");
		snprintf(buf + strlen(buf), len - strlen(buf), " err:%s\n", "DATA_PARSE");
		break;

	case FOTA_ERROR_DATA_VALIDATION:
		//LOG_DBG(" err:%s\n", "DATA_VALIDATION");
		snprintf(buf + strlen(buf), len - strlen(buf), " err:%s\n", "DATA_VALIDATION");
		break;

	default:
		//LOG_DBG(" err:%s\n", "ERROR_NONE");
		snprintf(buf + strlen(buf), len - strlen(buf), " err:%s\n", "ERROR_NONE");
		break;
	}

	LOG_DBG("%s", buf);
}

static const struct bt_mesh_comp comp = {
	.cid = BT_MESH_COMPANY_ID_EATON,
	.elem = elements_cli,
	.elem_count = ARRAY_SIZE(elements_cli),
};

/******************************************************************************/
/******************************** Public API **********************************/
/******************************************************************************/
const struct bt_mesh_comp *model_handler_cli_init(void)
{
	k_work_init_delayable(&attention_blink_work_cli, attention_blink_cli);
	k_work_init_delayable(&send_opcode_cli, blbcli_send_opcode);
	k_work_reschedule(&send_opcode_cli, K_NO_WAIT);

	LOG_DBG(" transfering %d of bytes from client to server\n", FLASH_MAX_SEND_IN_BYTES);

	return &comp;
}
