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

/** @file
 *  @brief Nordic Mesh light sample
 */
#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh/models.h>
#include <bluetooth/mesh/dk_prov.h>
#include <dk_buttons_and_leds.h>
#include "model_handler_cli.h"
#include <drivers/gpio.h>
#include <drivers/flash.h>
#include <logging/log.h>
#include <main.h>
#include <drivers/uart.h>
#include <stdio.h>

#include <etn_mesh_fota_blb.h>

#include "etn_mesh_fota.h"
#include "etn_mesh_fota_blb.h"

const char *gpio0port = "GPIO_0";
const char *uart1port = "UART_1";
const char *uart0port = "UART_0";
const char *m25qname = "W25Q";

/* device pointer to for uart 0 port */
const struct device *dev_uart1;
const struct device *dev_uart0;
const struct device *dev_m25q;

/* uart1 config structure */
struct uart_config cfg_uart1;
struct uart_config cfg_uart0;

LOG_MODULE_REGISTER(fota, CONFIG_LOG_DEFAULT_LEVEL);

static struct uart_data uart1_data;
static struct uart_data uart0_data;
static struct flash_file_info file_info;

static uint8_t flag=0;
static uint8_t cntr=0;

struct client_curr_values cli_curr_val;
struct fota_blb_data_flash flash_data;

static struct uart_data u_data;
extern struct blbcli_firmware_values firm_val;
extern enum blbcli_firmware_states blbcli_fw_state;

static void bt_ready(int err)
{
	if (err) {
		LOG_DBG("Bluetooth init failed (err %d)\n", err);
		return;
	}

	LOG_DBG("Bluetooth initialized\n");

	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		dk_leds_init();
		dk_buttons_init(NULL);
	}
	
	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		LOG_DBG( "BLE Mesh FOTA client\n" );
		
		bt_set_name("etn-fota-cli-d");

		err = bt_mesh_init(bt_mesh_dk_prov_init(), model_handler_cli_init());
		if (err) {
			LOG_DBG("Initializing mesh failed (err %d)\n", err);
			return;
		}
	}
	
	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	/* This will be a no-op if settings_load() loaded provisioning info */
	bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);

	LOG_DBG("Mesh initialized\n");
}

void main(void)
{
	int err;

	LOG_DBG("Initializing...\n");
	LOG_DBG(CONFIG_BOARD);
	LOG_DBG("\n");

	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		k_sleep( K_MSEC(2000) );
	}

	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		setup_m25q();
		k_sleep( K_MSEC(100) );

		file_info.index = 0;
		file_info.start_addr = (file_info.index * SERIAL_FLASH_PARTITION_SIZE) + SERIAL_FLASH_PARTITION_SIZE;

		//read_file_info(&file_info);
		//read_from_serial_flash(file_info.start_addr,file_info.file_size,0);

		//clear_serial_flash_storage(0x80000, 0x80000);
		//test_serial_flash(0x10000, 4096);
		//clear_serial_flash_storage(0x10000, 4096);
		//read_from_serial_flash(0x10000,128,0);
	}

	err = bt_enable(bt_ready);
	if (err) {
		LOG_DBG("Bluetooth init failed (err %d)\n", err);
	}
	
	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		LOG_DBG("setting up uart\n");
		setup_uart1();
		setup_uart0();
	}

	while(1)
	{
		client_led_button_process();
		client_state_machine_run();
		k_sleep( K_MSEC(cli_curr_val.process_delay_time));
	}
}

void client_led_button_process(void)
{
	if( cli_curr_val.led_blink == 1)
	{
		if( cli_curr_val.led_value )
		{
			dk_set_led_off(cli_curr_val.led_index);
			cli_curr_val.led_value = 0;
		}
		else
		{
			dk_set_led_on(cli_curr_val.led_index);
			cli_curr_val.led_value = 1;
		}
	}
	else if( cli_curr_val.led_blink == 2 )
	{
		if( cli_curr_val.led_value )
		{
			dk_set_led_off(0);
			dk_set_led_off(1);
			dk_set_led_off(2);
			dk_set_led_off(3);
			cli_curr_val.led_value = 0;
		}
		else
		{
			dk_set_led_on(0);
			dk_set_led_on(1);
			dk_set_led_on(2);
			dk_set_led_on(3);
			cli_curr_val.led_value = 1;
		}
	}
	else if( cli_curr_val.led_blink == 3 )
	{
		dk_set_led_on(0);
		dk_set_led_on(1);
		dk_set_led_on(2);
		dk_set_led_on(3);
	}
	else
	{
		dk_set_led_on(cli_curr_val.led_index);
	}
}

void client_state_machine_run(void)
{
	switch (cli_curr_val.state)
	{

	case CLIENT_STATE_NONE:
		client_state_none();
		break;

	case CLIENT_STATE_RUN_MODE:
		client_state_run_mode();
		break;

	case CLIENT_STATE_RUN_STORAGE_START:
		client_state_run_storage_start();
		break;

	case CLIENT_STATE_RUN_STORAGE_READ:
		client_state_run_storage_read();
		break;

	case CLIENT_STATE_RUN_STORAGE_COMPLETE:
		client_state_run_storage_completed();
		break;

	case CLIENT_STATE_RUN_TRANSFER_START:
		client_state_run_transfer_start();
		break;

	case CLIENT_STATE_RUN_TRANSFER_PROCESS:
		client_state_run_transfer_process();
		break;

	case CLIENT_STATE_RUN_TRANSFER_COMPLETE:
		client_state_run_transfer_completed();
		break;

	case CLIENT_STATE_IDEL:
		client_state_idel();
		break;

	default:
		cli_curr_val.state = CLIENT_STATE_NONE;
		break;
	}
}

void client_state_none(void)
{
	cli_curr_val.state = CLIENT_STATE_RUN_MODE;	
	cli_curr_val.process_delay_time = 1000;
	cli_curr_val.led_blink = 2;
	cli_curr_val.uart_wait_cntr = 0;
	k_sleep(K_MSEC(1000));
}

void client_state_run_mode(void)
{
	int err=0;
	int loop_cntr;
	uint8_t data_valid=0;
	int buf_index_inc=0;

	u_data.tx_cmd = UART_RUN_MODE;
	u_data.rx_done = 0;
	uart0_get_data(&u_data); // 300ms wrost delay

	if( u_data.rx_done )
	{
		u_data.rx_done = 0;
		
		if( u_data.response_code == UART_RUN_MODE )
		{
			if( u_data.rx_cmd == UART_ERR_NONE )
			{
				data_valid = 1;
				buf_index_inc = 4;

				cli_curr_val.server_id = u_data.buf[buf_index_inc++];
				cli_curr_val.server_id <<= 8;
				cli_curr_val.server_id |= u_data.buf[buf_index_inc++];

				cli_curr_val.miss_chunk = u_data.buf[buf_index_inc++];
				cli_curr_val.miss_chunk <<= 8;
				cli_curr_val.miss_chunk |= u_data.buf[buf_index_inc++];
				
				cli_curr_val.running_mode = u_data.buf[buf_index_inc++];
				cli_curr_val.running_mode <<= 8;
				cli_curr_val.running_mode |= u_data.buf[buf_index_inc++];
				
				if( cli_curr_val.running_mode == CLIENT_RUN_MODE_STORAGE )
				{
					cli_curr_val.firmware_index = u_data.buf[buf_index_inc++];
					cli_curr_val.firmware_index <<= 8;
					cli_curr_val.firmware_index |= u_data.buf[buf_index_inc++];

					file_info.index = cli_curr_val.firmware_index;

					client_led_set_index(file_info.index);
					
					/* clear memory partition index for starage */
					clear_memory_partition(&file_info);

					LOG_DBG("serverid:%d miss_chunk:%d run_mode:%d firm_index:%d", cli_curr_val.server_id, cli_curr_val.miss_chunk, cli_curr_val.running_mode, cli_curr_val.firmware_index);
					cli_curr_val.state = CLIENT_STATE_RUN_STORAGE_START;
				}
				else
				{
					cli_curr_val.state = CLIENT_STATE_RUN_TRANSFER_START;
					LOG_DBG("Transfer mode");
				}
			}
		}
		else
		{
			u_data.rx_cmd = UART_ERR_WRNG_RES;
		}
	}
	else
	{
		//LOG_DBG("no response from uart\n");
		u_data.rx_cmd = UART_ERR_RESPONSE;		
		cli_curr_val.uart_wait_cntr++;
		if(cli_curr_val.uart_wait_cntr > 10)
		{
			cli_curr_val.uart_wait_cntr = 0;
			cli_curr_val.state = CLIENT_STATE_RUN_TRANSFER_START;
			cli_curr_val.server_id = 3;
			cli_curr_val.miss_chunk = 0xFFFF;
			cli_curr_val.running_mode=0;
		}
		LOG_DBG(" no uart response ");
	}
}

void client_state_run_storage_start(void)
{
	int err=0;
	int loop_cntr;
	uint8_t data_valid=0;
	int buf_index_inc=0;

	u_data.tx_cmd = UART_READ_FILE_SIZE;
	u_data.rx_done = 0;
	uart0_get_data(&u_data); // 300ms wrost delay

	if( u_data.rx_done )
	{
		u_data.rx_done = 0;
		
		if( u_data.response_code == UART_READ_FILE_SIZE )
		{
			if( u_data.rx_cmd == UART_ERR_NONE )
			{
				data_valid = 1;
				buf_index_inc = 4;

				buf_index_inc=4;
				cli_curr_val.file_size = u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];
				cli_curr_val.file_size <<= 8;
				cli_curr_val.file_size |= u_data.buf[buf_index_inc++];

				file_info.index = cli_curr_val.firmware_index;
				file_info.file_size = cli_curr_val.file_size;	

				store_file_info( &file_info);
				
				cli_curr_val.chunk_number = 0;
				cli_curr_val.flash_addr = 0;
				cli_curr_val.flash_length = 128;
				
				cli_curr_val.state = CLIENT_STATE_RUN_STORAGE_READ;
				cli_curr_val.process_delay_time = 100;
				LOG_DBG("filesize:%llu", cli_curr_val.file_size);
			}
		}
		else
		{
			u_data.rx_cmd = UART_ERR_WRNG_RES;
		}
	}
	else
	{
		//LOG_DBG("no response from uart\n");
		u_data.rx_cmd = UART_ERR_RESPONSE;
	}
}

void client_state_run_storage_read(void)
{
	if( flash_data.address < cli_curr_val.file_size )
	{
		client_store_bytes_to_flash();	
	}
	else
	{
		cli_curr_val.state = CLIENT_STATE_RUN_STORAGE_COMPLETE;
		cli_curr_val.process_delay_time = 100;
		cli_curr_val.led_blink = 0;
	}
}

void client_store_bytes_to_flash(void)
{
	int err=0;
	int loop_cntr;
	uint8_t data_valid=0;
	int buf_index_inc=0;
	int flash_status;
	uint8_t buf[300];
	int len=300;

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

	u_data.tx_cmd = UART_READ_BYTES;
	u_data.addr = flash_data.address;
	u_data.rx_done = 0;
	uart0_get_data(&u_data); // 300ms wrost delay

	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;
	}

	
	for( cntr=0; cntr< 128; cntr++ )
	{
		
	}

	if( data_valid )
	{
		buf_index_inc = 4;
		memset(buf,'0',300);
		snprintf(buf, len,"data: from:%llu ", cli_curr_val.firm_start_index);
		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];
			snprintf(buf + strlen(buf), len - strlen(buf), "%02X ", flash_data.data[loop_cntr]);
			buf_index_inc++;	
		}

		LOG_DBG("%s", buf);
		
		//flash_status = flash_write(dev_m25q, cli_curr_val.firm_start_index ,&flash_data.data, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE);
		flash_status = store_to_serial_flash(cli_curr_val.firm_start_index, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE, &flash_data.data);

		if( flash_status == 0)
		{
			LOG_DBG("serial flash write data to address %llu successful", cli_curr_val.firm_start_index);
			cli_curr_val.chunk_number++;
			cli_curr_val.flash_addr += BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
			cli_curr_val.firm_start_index += BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
		}
		else
		{
			LOG_DBG("serial flash write data to address %llu failed", cli_curr_val.firm_start_index);
		}
	}
}

void client_state_run_storage_completed(void)
{
	uint64_t addr = (cli_curr_val.firmware_index * SERIAL_FLASH_PARTITION_SIZE) + SERIAL_FLASH_PARTITION_SIZE;
	read_from_serial_flash( addr, cli_curr_val.file_size, 0 );
	LOG_DBG("Addr::%llu filesize:%llu", addr, cli_curr_val.file_size);
	cli_curr_val.state = CLIENT_STATE_IDEL;
	LOG_DBG("storing to flash completed");
}

void client_state_run_transfer_start(void)
{
	uint8_t btn_read;
	bool flag=0;

	cli_curr_val.process_delay_time = 100;
	cli_curr_val.led_blink = 2;

	btn_read = dk_get_buttons();
	if(btn_read == 1)
	{
		file_info.index = 0;
		flag = 1;
	}
	else if(btn_read == 2)
	{
		file_info.index = 1;
		flag = 1;
	}
	else if(btn_read == 4)
	{
		file_info.index = 2;
		flag = 1;
	}
	else if(btn_read == 8)
	{
		file_info.index = 3;
		flag = 1;
	}
	else
	{
		LOG_DBG("Waiting for btn press to start firmware transfer");
	}

	if( flag )
	{
		LOG_DBG("btn pressed and reading file info of index %d ", file_info.index);
		read_file_info(&file_info);
		
		client_led_set_index(file_info.index);
		cli_curr_val.led_blink = 1;

		start_firmware_transfer_state_machine_in_handler();

		cli_curr_val.state = CLIENT_STATE_RUN_TRANSFER_PROCESS;

		cli_curr_val.start_time = k_uptime_get();
	}
}

void client_state_run_transfer_process(void)
{

}

void client_state_run_transfer_completed(void)
{
	LOG_DBG("file transfer completed");
	LOG_DBG("time taken for firmware transfer:%u", (k_uptime_get() - cli_curr_val.start_time));
	cli_curr_val.led_blink = 3;
	cli_curr_val.state = CLIENT_STATE_IDEL;
}

void client_state_idel(void)
{
	LOG_DBG("Device entered idel state in main");
	cli_curr_val.process_delay_time = 1000;
}

void client_led_set_index(uint8_t index)
{
	switch (index)
	{
	case 0:
		cli_curr_val.led_index = index;
		dk_set_led_off(1);
		dk_set_led_off(2);
		dk_set_led_off(3);

		break;
	case 1:
		cli_curr_val.led_index = index;
		dk_set_led_off(0);
		dk_set_led_off(2);
		dk_set_led_off(3);
		break;
	case 2:
		cli_curr_val.led_index = index;
		dk_set_led_off(0);
		dk_set_led_off(1);
		dk_set_led_off(3);
		break;
	case 3:
		cli_curr_val.led_index = index;
		dk_set_led_off(0);
		dk_set_led_off(1);
		dk_set_led_off(2);
		break;
	default:
		cli_curr_val.led_index = index;
		dk_set_led_off(1);
		dk_set_led_off(2);
		dk_set_led_off(3);
		break;
	}
}

void clear_memory_partition( struct flash_file_info *info )
{
	int flash_status;

	if( info->index < 4 )
	{
		cli_curr_val.firm_start_index = ( info->index * SERIAL_FLASH_PARTITION_SIZE ) + SERIAL_FLASH_PARTITION_SIZE;
		info->start_addr = cli_curr_val.firm_start_index;

		LOG_DBG("mem start index:%llu", cli_curr_val.firm_start_index);
		flash_status = flash_erase( dev_m25q, cli_curr_val.firm_start_index, SERIAL_FLASH_PARTITION_SIZE );
		if( flash_status == 0)
		{
			LOG_DBG("serial flash erase index %d successful", info->index);
		}
		else
		{
			LOG_DBG("serial flash erase index %d failed", info->index);
		}
	}
}

void store_file_info( struct flash_file_info *info )
{
	uint64_t addr=0;
	int flash_status;
	uint8_t data[8];

	if( info->index < 4)
	{
		addr = SERIAL_FLASH_FILE_INFO_ADDR + ( info->index * SERIAL_MIN_ERASE_SIZE);
		flash_status = flash_erase( dev_m25q, addr, SERIAL_MIN_ERASE_SIZE );
		if( flash_status == 0)
		{
			LOG_DBG("serial flash info erase index %d successful", info->index);
			data[0] = cli_curr_val.file_size >> 56;
			data[1] = cli_curr_val.file_size >> 48;
			data[2] = cli_curr_val.file_size >> 40;
			data[3] = cli_curr_val.file_size >> 32;
			data[4] = cli_curr_val.file_size >> 24;
			data[5] = cli_curr_val.file_size >> 16;
			data[6] = cli_curr_val.file_size >> 8;
			data[7] = cli_curr_val.file_size;

			flash_status = flash_write(dev_m25q, addr ,data,8);
			if( flash_status == 0)
			{
				LOG_DBG("serial flash write info index %d successful", info->index);
			}
			else
			{
				LOG_DBG("serial flash write info index %d failed", info->index);
			}
		}
		else
		{
			LOG_DBG("serial flash info erase index %d failed", info->index);
		}
	}
}

void read_file_info( struct flash_file_info *info )
{
	uint64_t addr=0;
	int flash_status;
	uint8_t data[8];

	if( info->index < 4)
	{
		
		addr = SERIAL_FLASH_FILE_INFO_ADDR + ( info->index * SERIAL_MIN_ERASE_SIZE);
		
		flash_status = flash_read(dev_m25q, addr, data, 8);

		if( flash_status == 0)
		{
			cli_curr_val.file_size = 0;
			cli_curr_val.file_size = data[0];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[1];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[2];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[3];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[4];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[5];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[6];
			cli_curr_val.file_size <<= 8;
			cli_curr_val.file_size |= data[7];

			info->file_size = cli_curr_val.file_size;
			info->start_addr = (file_info.index * SERIAL_FLASH_PARTITION_SIZE) + SERIAL_FLASH_PARTITION_SIZE;
			info->length = cli_curr_val.file_size;

			LOG_DBG("serial flash info read file info index %d successful file_size:%llu bytes", info->index, cli_curr_val.file_size);
		}
		else
		{
			LOG_DBG("serial flash info erase index %d failed", info->index);
		}
	}
}

void setup_uart1(void)
{
	/* get uart1 device binding */
	dev_uart1 = device_get_binding(uart1port);

	if(dev_uart1 == NULL)
	{
		LOG_ERR("device binding to port uart1 failed");
		return;
	}
	else
	{
		LOG_INF("device binding to port uart1 success");
	}

	/* uart configuration */
	cfg_uart1.baudrate = 115200;
	cfg_uart1.data_bits = UART_CFG_DATA_BITS_8;
	cfg_uart1.flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
	cfg_uart1.parity = UART_CFG_PARITY_NONE;
	cfg_uart1.stop_bits = UART_CFG_STOP_BITS_1;

	/* configure uart1 */
	int ret = uart_configure(dev_uart1, &cfg_uart1);
	if(ret != 0)
	{
		LOG_ERR("uart1 configuration failed");
		return;		
	}
	else
	{
		LOG_INF("uart1 configuration success");
	} 

	int uart_data = 43;
    uart_irq_callback_user_data_set(dev_uart1, uart1_cb, &uart_data);
	uart_irq_rx_enable(dev_uart1);
}

void uart1_cb(const struct device *dev, void *user_data)
{
	uint8_t byte;

	uart_irq_update(dev);
	while(uart_irq_rx_ready(dev)) {
		uart_fifo_read(dev, &byte, 1);
		uart1_data.buf[uart1_data.rx_counter++] = byte;
		if(uart1_data.rx_counter < RX_BUFFER_LENGTH)
		{
			if(uart1_data.rx_counter > 5)
			{
				if((uart1_data.buf[uart1_data.rx_counter-1] == 0x0A) && (uart1_data.buf[uart1_data.rx_counter-2] == 0x0D) && (uart1_data.buf[uart1_data.rx_counter-3] == 0x0A) && (uart1_data.buf[uart1_data.rx_counter-4] == 0x0D))
				{
					uart1_data.len = uart1_data.rx_counter - 5;
					uart1_data.rx_done = 1;
				}
			}
		}
		else
		{
			uart1_data.rx_counter = 0;
			uart1_data.rx_done = 0;
		}
	}
}

void uart1_write_data(char array[], uint8_t len)
{
    
    for(uint8_t i=0; i < len;i++)
    {
        uart_poll_out(dev_uart1,array[i]);
    }
}

bool uart1_check_if_data_received(struct uart_data *data)
{
	data->rx_done = 0;

	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		if(uart1_data.rx_done)
		{

			data->rx_done = 1;
			data->rx_counter = uart1_data.rx_counter;
			data->len = uart1_data.len;
			memcpy(data->buf, uart1_data.buf, uart1_data.rx_counter);

			uart1_data.rx_done = 0;
			uart1_data.rx_counter = 0;
			//write_to_uart1(uart1_data.buf, uart1_data.len);
			memset(uart1_data.buf,'\0',sizeof(uart1_data.buf));
		}
	}

	return data->rx_done ;
}

void uart1_get_data(struct uart_data *data)
{
	int buf_index_inc=0;
	int wait_counter=0;

	data->tx_buf[0] = 0xAA;
	data->tx_buf[1] = 0x55;
	data->tx_buf[2] = data->tx_cmd;
	data->tx_buf[3] = data->addr>>24;
	data->tx_buf[4] = data->addr>>16;
	data->tx_buf[5] = data->addr>>8;
	data->tx_buf[6] = data->addr;
	data->tx_buf[7] = 0;
	data->tx_buf[8] = '\r';
	data->tx_buf[9] = '\n';

	uart1_write_data(data->tx_buf, TX_BUFFER_LENGTH);

	wait_counter=0;
	while( wait_counter < 300 )
	{
		k_sleep( K_MSEC(10));
		wait_counter += 10 ;
		if( uart1_data.rx_done )
		{
			wait_counter = 310;
		}	
	}
	
	if( uart1_check_if_data_received(data))
	{
		data->response_code = data->buf[buf_index_inc++];
		data->response_code <<= 8;
		data->response_code |= data->buf[buf_index_inc++];

		data->rx_cmd = data->buf[buf_index_inc++];
		data->rx_cmd <<= 8;
		data->rx_cmd |= data->buf[buf_index_inc++];
	}

	uart1_data.rx_done = 0;
}

void setup_uart0(void)
{
	/* get uart0 device binding */
	dev_uart0 = device_get_binding(uart0port);

	if(dev_uart0 == NULL)
	{
		LOG_ERR("device binding to port uart0 failed");
		return;
	}
	else
	{
		LOG_INF("device binding to port uart0 success");
	}

	/* uart configuration */
	cfg_uart0.baudrate = 115200;
	cfg_uart0.data_bits = UART_CFG_DATA_BITS_8;
	cfg_uart0.flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
	cfg_uart0.parity = UART_CFG_PARITY_NONE;
	cfg_uart0.stop_bits = UART_CFG_STOP_BITS_1;

	/* configure uart1 */
	int ret = uart_configure(dev_uart0, &cfg_uart0);
	if(ret != 0)
	{
		LOG_ERR("uart0 configuration failed");
		return;		
	}
	else
	{
		LOG_INF("uart0 configuration success");
	} 

	int uart_data = 43;
    uart_irq_callback_user_data_set(dev_uart0, uart0_cb, &uart_data);
	uart_irq_rx_enable(dev_uart0);
}

void uart0_cb(const struct device *dev, void *user_data)
{
	uint8_t byte;

	uart_irq_update(dev);
	while(uart_irq_rx_ready(dev)) {
		uart_fifo_read(dev, &byte, 1);
		uart0_data.buf[uart0_data.rx_counter++] = byte;
		if(uart0_data.rx_counter < RX_BUFFER_LENGTH)
		{
			if(uart0_data.rx_counter > 5)
			{
				if((uart0_data.buf[uart0_data.rx_counter-1] == 0x0A) && (uart0_data.buf[uart0_data.rx_counter-2] == 0x0D) && (uart0_data.buf[uart0_data.rx_counter-3] == 0x0A) && (uart0_data.buf[uart0_data.rx_counter-4] == 0x0D))
				{
					uart0_data.len = uart0_data.rx_counter - 5;
					uart0_data.rx_done = 1;
				}
			}
		}
		else
		{
			uart0_data.rx_counter = 0;
			uart0_data.rx_done = 0;
		}
	}
}

void uart0_write_data(char array[], uint8_t len)
{
    
    for(uint8_t i=0; i < len;i++)
    {
        uart_poll_out(dev_uart0,array[i]);
    }
}

bool uart0_check_if_data_received(struct uart_data *data)
{
	data->rx_done = 0;

	if( strcmp( CONFIG_BOARD , "nrf52840dk_nrf52840") == 0 )
	{
		if(uart0_data.rx_done)
		{

			data->rx_done = 1;
			data->rx_counter = uart0_data.rx_counter;
			data->len = uart0_data.len;
			memcpy(data->buf, uart0_data.buf, uart0_data.rx_counter);

			uart0_data.rx_done = 0;
			uart0_data.rx_counter = 0;
			//write_to_uart1(uart1_data.buf, uart1_data.len);
			memset(uart0_data.buf,'\0',sizeof(uart0_data.buf));
		}
	}

	return data->rx_done ;
}

void uart0_get_data(struct uart_data *data)
{
	int buf_index_inc=0;
	int wait_counter=0;

	data->tx_buf[0] = 0xAA;
	data->tx_buf[1] = 0x55;
	data->tx_buf[2] = data->tx_cmd;
	data->tx_buf[3] = data->addr>>24;
	data->tx_buf[4] = data->addr>>16;
	data->tx_buf[5] = data->addr>>8;
	data->tx_buf[6] = data->addr;
	data->tx_buf[7] = 0;
	data->tx_buf[8] = '\r';
	data->tx_buf[9] = '\n';

	uart0_write_data(data->tx_buf, TX_BUFFER_LENGTH);

	wait_counter=0;
	while( wait_counter < 300 )
	{
		k_sleep( K_MSEC(10));
		wait_counter += 10 ;
		if( uart0_data.rx_done )
		{
			wait_counter = 310;
		}	
	}
	
	if( uart0_check_if_data_received(data))
	{
		data->response_code = data->buf[buf_index_inc++];
		data->response_code <<= 8;
		data->response_code |= data->buf[buf_index_inc++];

		data->rx_cmd = data->buf[buf_index_inc++];
		data->rx_cmd <<= 8;
		data->rx_cmd |= data->buf[buf_index_inc++];
	}

	uart0_data.rx_done = 0;
}

void setup_m25q(void)
{
	/* get device binding to m25q */
	dev_m25q = device_get_binding(m25qname);

	if (dev_m25q == NULL)
	{
		LOG_DBG("device binding to m25q failed\n");
		return;
	}
	else
	{
		LOG_DBG("device binding to m25q success\n");
	}
}

int store_to_serial_flash(uint64_t address, uint64_t length, const void * data)
{
	int flash_status=0;

	flash_status = flash_write(dev_m25q, address,data,length);

	return flash_status;
}

void clear_serial_flash_storage(uint64_t start_address, uint64_t size)
{
	int flash_status=0;

	flash_status = flash_erase( dev_m25q, start_address, size );
	if( flash_status == 0)
	{
		LOG_DBG("serial flash erase successful start addr:%llu size:%llu",start_address, size );
	}
	else
	{
		LOG_DBG("serial flash erase failed start addr:%llu size:%llu",start_address, size);
	}
}

void read_from_serial_flash(uint64_t address, uint64_t length, uint8_t type)
{
	int flash_status=0;
	uint8_t data[BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE];
	uint64_t len=0;
	uint8_t loop_cntr=0;
	uint16_t buflen= 350;
	char buf[buflen];
	uint64_t start_addr;

	start_addr = address;

	LOG_DBG("\nreading %llu bytes from address %llu\n", length, address);
	while( len <= length )
	{
		flash_status = flash_read( dev_m25q, address, data, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE );
		if( flash_status == 0)
		{
			memset(buf,'\0',buflen);
			snprintf(buf, buflen,"addr:%llu: ", address);
			if(type == 0 )
			{
				for(loop_cntr=0;loop_cntr<BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;loop_cntr++)
				{
					snprintf(buf + strlen(buf), buflen - strlen(buf), "%02X ", data[loop_cntr]);
				}
				LOG_DBG("%s", buf);
			}
			else
			{
				for(loop_cntr=0;loop_cntr<BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;loop_cntr++)
				{
					snprintf(buf + strlen(buf), len - strlen(buf), "%c ", data[loop_cntr]);
				}
				LOG_DBG("%s", buf);
			}
		}
		else
		{
			LOG_DBG("serial flash read failed at %llu\n", address);
		}
		address = address + BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
		len = len + BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE;
	}

	LOG_DBG("serial flash reading completed");

}

void test_serial_flash(uint64_t start_address, uint64_t size)
{
	uint8_t data[128];
	uint8_t cntr=0;
	uint16_t len= 300;
	char buf[len];
	int flash_status;

	LOG_DBG("testing serial flash");

	clear_serial_flash_storage(start_address, size);

	LOG_DBG("writing 128 bytes to end address");
	snprintf(buf, len,"data:");
	for( cntr=0; cntr< 128; cntr++ )
	{
		data[cntr] = cntr;
		snprintf(buf + strlen(buf), len - strlen(buf), "%02X ", data[cntr]);
	}
	LOG_DBG("%s", buf);
	//flash_status = store_to_serial_flash(start_address, 128, &data);
	flash_status = store_to_serial_flash(start_address, 128, &data);
	if( flash_status !=0  )
	{
		LOG_DBG("flash write error");
	}

	for( cntr=0; cntr< 128; cntr++ )
	{
		data[cntr] = 0;
	}

	LOG_DBG("reading 128 bytes to end address");
	//flash_status = flash_read( dev_m25q, (SERIAL_FLASH_END_ADDRESS - 128), data, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE );
	flash_status = flash_read( dev_m25q, start_address, data, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE );
	if( flash_status !=0  )
	{
		LOG_DBG("flash read error");
	}
	memset(buf,'0',300);
	snprintf(buf, len,"data:");
	for( cntr=0; cntr< 128; cntr++ )
	{
		snprintf(buf + strlen(buf), len - strlen(buf), "%02X ", data[cntr]);
	}
	LOG_DBG("%s", buf);
}


uint16_t get_server_id(void)
{
	return cli_curr_val.server_id;
}

uint16_t get_miss_chunks(void)
{
	return cli_curr_val.miss_chunk;
}

uint64_t get_firmware_size(void)
{
	return file_info.file_size;
}

int get_firmware_data_from_external_flash( struct fota_blb_data_flash *flash_data)
{
	uint64_t addr;
	uint64_t len;
	uint64_t base_addr;
	int flash_status;

	//LOG_DBG(" addr:%u base:%llu", flash_data->address, addr);
	addr = flash_data->address + file_info.start_addr;
	flash_status = flash_read( dev_m25q, addr, flash_data->data, BT_MESH_ETN_FOTA_BLB_FLASH_MAX_DATA_SIZE );
	if( flash_status != 0)
	{
		LOG_DBG(" read err from external flash addr:%llu actual external flash addr:%llu",flash_data->address, addr );
	}

	return flash_status;
}	

void client_state_set(uint8_t state)
{
	cli_curr_val.state = state;
}