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

#include <cloud_codec.h>
#include <zephyr/types.h>
#include <stdbool.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <lwm2m_resource_ids.h>
#include <zephyr/net/lwm2m.h>
#include <net/lwm2m_client_utils.h>
#include <net/lwm2m_client_utils_location.h>
#include <date_time.h>

#if defined(CONFIG_SUPL_CLIENT_LIB)
#include "cloud/lwm2m_integration/assistance.h"
#endif

#include "lwm2m_codec_defines.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(cloud_codec, CONFIG_CLOUD_CODEC_LOG_LEVEL);
#include "minilog.h"

typedef union {
	struct nrf_modem_gnss_agps_data_utc utc_data;
	struct nrf_modem_gnss_agps_data_ephemeris ephemeris;
	struct nrf_modem_gnss_agps_data_almanac almanac;
	struct nrf_modem_gnss_agps_data_klobuchar klobuchar;
	struct nrf_modem_gnss_agps_data_nequick nequick;
	struct nrf_modem_gnss_agps_data_system_time_and_sv_tow tows;
	struct nrf_modem_gnss_agps_data_location agps;
	struct nrf_modem_gnss_agps_data_location location;
	struct nrf_modem_gnss_agps_data_integrity integrity;
} supl_union;

/* Some resources does not have designated buffers. Therefore we define those in here. */
static uint8_t bearers[2] = { LTE_FDD_BEARER, NB_IOT_BEARER };
static int battery_voltage;
static time_t pressure_ts;
static time_t temperature_ts;
static time_t humidity_ts;
static time_t button_ts;
static int64_t current_time;

/* Minimum and maximum values for the BME680 present on the Thingy:91. */
static double temp_min_range_val = BME680_TEMP_MIN_RANGE_VALUE;
static double temp_max_range_val = BME680_TEMP_MAX_RANGE_VALUE;
static double humid_min_range_val = BME680_HUMID_MIN_RANGE_VALUE;
static double humid_max_range_val = BME680_HUMID_MAX_RANGE_VALUE;
static double pressure_min_range_val = BME680_PRESSURE_MIN_RANGE_VALUE;
static double pressure_max_range_val = BME680_PRESSURE_MAX_RANGE_VALUE;

static uint8_t* supl_buffer = NULL;
size_t supl_size, supl_downloaded = 0;

/* Module event handler.  */
static cloud_codec_evt_handler_t module_evt_handler;

/* Convenience API used to generate path lists with reference to objects.
 * Strings in the path array are expected to be NULL terminated.
 */
static int object_path_list_add(struct cloud_codec_data *output, const char * const path[],
				size_t path_size)
{
	bool path_added = false;
	uint8_t j = 0;

	if (output == NULL || path == NULL || path_size == 0) {
		return -EINVAL;
	}

	for (int i = 0; i < ARRAY_SIZE(output->paths); i++) {
		if (strcmp(output->paths[i], "\0") == 0) {

			if ((sizeof(output->paths[i]) - 1) < strlen(path[j])) {
				MLOG_ERR("Path entry is too long");
				return -EMSGSIZE;
			}

			strncpy(output->paths[i], path[j], sizeof(output->paths[i]) - 1);

			output->paths[i][sizeof(output->paths[i]) - 1] = '\0';
			output->valid_object_paths++;
			path_added = true;
			j++;

			if (j == path_size) {
				break;
			}
		}
	}

	/* This API can be called multiple times to populate the same path buffer. Due to this we
	 * also check if all entries in the incoming path[] list was added.
	 */
	if (!path_added || (j != path_size)) {
		MLOG_ERR("Current object path list is full, or not all entries in the incoming "
			"path[] list was added");
		return -ENOMEM;
	}

	return 0;
}

static void get_agps_data_type_string(char *type_string, uint16_t type)
{
	switch (type) {
	case NRF_MODEM_GNSS_AGPS_UTC_PARAMETERS:
		(void)strcpy(type_string, "utc");
		break;
	case NRF_MODEM_GNSS_AGPS_EPHEMERIDES:
		(void)strcpy(type_string, "ephe");
		break;
	case NRF_MODEM_GNSS_AGPS_ALMANAC:
		(void)strcpy(type_string, "alm");
		break;
	case NRF_MODEM_GNSS_AGPS_KLOBUCHAR_IONOSPHERIC_CORRECTION:
		(void)strcpy(type_string, "klob");
		break;
	case NRF_MODEM_GNSS_AGPS_NEQUICK_IONOSPHERIC_CORRECTION:
		(void)strcpy(type_string, "neq");
		break;
	case NRF_MODEM_GNSS_AGPS_GPS_SYSTEM_CLOCK_AND_TOWS:
		(void)strcpy(type_string, "time");
		break;
	case NRF_MODEM_GNSS_AGPS_LOCATION:
		(void)strcpy(type_string, "pos");
		break;
	case NRF_MODEM_GNSS_AGPS_INTEGRITY:
		(void)strcpy(type_string, "int");
		break;
	default:
		(void)strcpy(type_string, "UNKNOWN");
		break;
	}
}

static void print_supl_data(supl_union *data, size_t agps_size, uint16_t type, char *type_string)
{
	// uint8_t *s = malloc(agps_size * 2 + 1);
	// for (size_t i = 0; i < agps_size; i++)
	// 	sprintf(s + i * 2, "%02x", data[i]);

	MLOG_INF("A-GPS data, type: %s, size: %d, array: [%s]", type_string, agps_size, "");
	//free(s);
	switch (type) {
	case NRF_MODEM_GNSS_AGPS_UTC_PARAMETERS: {
		MLOG_INF("a1=%d,a0=%d,tot=%d,wn_t=%d,delta_tls=%d,wn_lsf=%d,dn=%d,delta_tlsf=%d",
			 data->utc_data.a1, data->utc_data.a0, data->utc_data.tot, data->utc_data.wn_t,
			 data->utc_data.delta_tls, data->utc_data.wn_lsf, data->utc_data.dn,
			 data->utc_data.delta_tlsf);
		break;
	}
	case NRF_MODEM_GNSS_AGPS_EPHEMERIDES:
		MLOG_INF(
			"sv_id=%d,health=%d,iodc=%d,toc=%d,af2=%d,af1=%d,af0=%d,tgd=%d,ura=%d,fit_int=%d,toe=%d,w=%d,delta_n=%d,m0=%d,omega_dot=%d,e=%d,idot=%d,sqrt_a=%d,i0=%d,omega=%d,crs=%d,cis=%d,cus=%d,crc=%d,cic=%d,cuc=%d",
			data->ephemeris.sv_id, data->ephemeris.health, data->ephemeris.iodc,
			data->ephemeris.toc, data->ephemeris.af2, data->ephemeris.af1,
			data->ephemeris.af0, data->ephemeris.tgd, data->ephemeris.ura,
			data->ephemeris.fit_int, data->ephemeris.toe, data->ephemeris.w,
			data->ephemeris.delta_n, data->ephemeris.m0, data->ephemeris.omega_dot,
			data->ephemeris.e, data->ephemeris.idot, data->ephemeris.sqrt_a,
			data->ephemeris.i0, data->ephemeris.omega0, data->ephemeris.crs,
			data->ephemeris.cis, data->ephemeris.cus, data->ephemeris.crc,
			data->ephemeris.cic, data->ephemeris.cuc);
		break;
	case NRF_MODEM_GNSS_AGPS_ALMANAC:
		MLOG_INF(
			"sv_id=%d,wn=%d,toa=%d,ioda=%d,e=%d,delta_i=%d,omega_dot=%d,sv_health=%d,sqrt_a=%d,omega0=%d,w=%d,m0=%d,af0=%d,af1=%d",
			data->almanac.sv_id, data->almanac.wn, data->almanac.toa, data->almanac.ioda,
			data->almanac.e, data->almanac.delta_i, data->almanac.omega_dot,
			data->almanac.sv_health, data->almanac.sqrt_a, data->almanac.omega0,
			data->almanac.w, data->almanac.m0, data->almanac.af0, data->almanac.af1);
		break;
	case NRF_MODEM_GNSS_AGPS_KLOBUCHAR_IONOSPHERIC_CORRECTION:
		MLOG_INF(
			"alpha0=%d,alpha1=%d,alpha2=%d,alpha3=%d,beta0=%d,beta1=%d,beta2=%d,beta3=%d,",
			data->klobuchar.alpha0, data->klobuchar.alpha1, data->klobuchar.alpha2,
			data->klobuchar.alpha3, data->klobuchar.beta0, data->klobuchar.beta1,
			data->klobuchar.beta2, data->klobuchar.beta3);
		break;
	case NRF_MODEM_GNSS_AGPS_NEQUICK_IONOSPHERIC_CORRECTION:
		MLOG_INF("ai0=%d,ai1=%d,ai2=%d,storm_cond=%d,storm_valid=%d", data->nequick.ai0,
			 data->nequick.ai1, data->nequick.ai2, data->nequick.storm_cond,
			 data->nequick.storm_valid);
		break;
	case NRF_MODEM_GNSS_AGPS_GPS_SYSTEM_CLOCK_AND_TOWS:
		MLOG_INF("date_day=%d,time_full_s=%d,time_frac_ms=%d,sv_mask=%d",
			 data->tows.date_day, data->tows.time_full_s, data->tows.time_frac_ms,
			 data->tows.sv_mask);
		for (int i = 0; i < 32; i++)
			MLOG_INF("\ttlm=%x,flags=%x", data->tows.sv_tow[i].tlm,
				 data->tows.sv_tow[i].flags);
		break;
	case NRF_MODEM_GNSS_AGPS_LOCATION:
		MLOG_INF(
			"latitude=%d,longitude=%d,altitude=%d,semimajor=%d,semiminor=%d,orientation_major=%d,unc_altitude=%d,confidence=%d",
			data->agps.latitude, data->agps.longitude, data->agps.altitude,
			data->agps.unc_semimajor, data->agps.unc_semiminor,
			data->agps.orientation_major, data->agps.unc_altitude, data->agps.confidence);
		break;
	case NRF_MODEM_GNSS_AGPS_INTEGRITY:
		MLOG_INF("integrity=%d", data->integrity.integrity_mask);
		break;
	default:
		break;
	}

}

static size_t copy_supl_data(uint8_t *data, supl_union *supl, uint8_t data_type)
{
	switch (data_type) {
	case NRF_MODEM_GNSS_AGPS_UTC_PARAMETERS:
		supl->utc_data.a1 = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
		supl->utc_data.a0 = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
		supl->utc_data.tot = data[8];
		supl->utc_data.wn_t = data[9];
		supl->utc_data.delta_tls = data[10];
		supl->utc_data.wn_lsf = data[11];
		supl->utc_data.dn = data[12];
		supl->utc_data.delta_tlsf = data[13];
		return sizeof(supl->utc_data);

	case NRF_MODEM_GNSS_AGPS_EPHEMERIDES:
		supl->ephemeris.sv_id = data[0];
		supl->ephemeris.health = data[1];
		supl->ephemeris.iodc = (data[2] << 8) | data[3];
		supl->ephemeris.toc = (data[4] << 8) | data[5];
		supl->ephemeris.af2 = data[6];
		supl->ephemeris.af1 = (data[7] << 8) | data[8];
		supl->ephemeris.af0 =
			(data[9] << 24) | (data[10] << 16) | (data[11] << 8) | data[12];
		supl->ephemeris.tgd = data[13];
		supl->ephemeris.ura = data[14];
		supl->ephemeris.fit_int = data[15];
		supl->ephemeris.toe = (data[16] << 8) | data[17];
		supl->ephemeris.w =
			(data[18] << 24) | (data[19] << 16) | (data[20] << 8) | data[21];
		supl->ephemeris.delta_n = (data[22] << 8) | data[23];
		supl->ephemeris.m0 =
			(data[24] << 24) | (data[25] << 16) | (data[26] << 8) | data[27];
		supl->ephemeris.omega_dot =
			(data[28] << 24) | (data[29] << 16) | (data[30] << 8) | data[31];
		supl->ephemeris.e = (data[32] << 24) | (data[33] << 16) | (data[34] << 8) | data[35];
		supl->ephemeris.idot = (data[36] << 8) | data[37];
		supl->ephemeris.sqrt_a =
			(data[38] << 24) | (data[39] << 16) | (data[40] << 8) | data[41];
		supl->ephemeris.i0 =
			(data[42] << 24) | (data[43] << 16) | (data[44] << 8) | data[45];
		supl->ephemeris.omega0 =
			(data[46] << 24) | (data[47] << 16) | (data[48] << 8) | data[49];
		supl->ephemeris.crs = (data[50] << 8) | data[51];
		supl->ephemeris.cis = (data[52] << 8) | data[53];
		supl->ephemeris.cus = (data[54] << 8) | data[55];
		supl->ephemeris.crc = (data[56] << 8) | data[57];
		supl->ephemeris.cic = (data[58] << 8) | data[59];
		supl->ephemeris.cuc = (data[60] << 8) | data[61];
		return sizeof(supl->ephemeris);

	case NRF_MODEM_GNSS_AGPS_KLOBUCHAR_IONOSPHERIC_CORRECTION:
		supl->klobuchar.alpha0 = data[0];
		supl->klobuchar.alpha1 = data[1];
		supl->klobuchar.alpha2 = data[2];
		supl->klobuchar.alpha3 = data[3];
		supl->klobuchar.beta0 = data[4];
		supl->klobuchar.beta1 = data[5];
		supl->klobuchar.beta2 = data[6];
		supl->klobuchar.beta3 = data[7];
		return sizeof(supl->klobuchar);

	case NRF_MODEM_GNSS_AGPS_GPS_SYSTEM_CLOCK_AND_TOWS:

		return 0;//sizeof(supl->tows);

	case NRF_MODEM_GNSS_AGPS_LOCATION:
		supl->location.latitude = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
		supl->location.longitude = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
		supl->location.altitude = (data[8] << 8) | data[9];
		supl->location.unc_semimajor = data[10];
		supl->location.unc_semiminor = data[11];
		supl->location.orientation_major = data[12];
		supl->location.unc_altitude = data[13];
		supl->location.confidence = data[14];
		return sizeof(supl->location);
	default:
		return 0;
	}
}

static int handle_supl_data() {
	char type_string[16];
	size_t pointer = 0;
	supl_union data;

	MLOG_INF("Handling SUPL data: supl_size=%d", supl_size);
	while (pointer < supl_size) {
		MLOG_INF("SUPL loop: pointer=%d", pointer);
		uint8_t type = supl_buffer[pointer];
		MLOG_INF("SUPL loop: type=%d", type);
		get_agps_data_type_string(type_string, type);
		uint32_t size = (supl_buffer[pointer + 1] << 24) |
				(supl_buffer[pointer + 2] << 16) | (supl_buffer[pointer + 3] << 8) |
				supl_buffer[pointer + 4];
		MLOG_INF("SUPL loop: size=%d", size);
		size_t sz = copy_supl_data(supl_buffer + pointer + 5, &data, type);
		MLOG_INF("SUPL loop: sz=%d", size);
		if (sz) {
			int retval = nrf_modem_gnss_agps_write(&data, sz, type);
			if (retval != 0) {
				MLOG_ERR("Failed to write A-GPS data, type: %s (errno: %d)",
					 type_string, errno);
			}
			print_supl_data(&data, size, type, type_string);
		}
		pointer += size + 5;
	}

	return 0;
}

/* Function that is called whenever the SUPL object is written to. */
static int supl_write_cb(uint16_t obj_inst_id, uint16_t res_id, uint16_t res_inst_id,
			    uint8_t *data, uint16_t data_len, bool last_block, size_t total_size)
{
	MLOG_DBG("SUPL %d/%d, received %d/%d bytes of data, last block is %s", obj_inst_id, res_id, data_len, total_size, last_block ? "true" : "false");
	client_acknowledge();
	if (!supl_buffer) {
		MLOG_DBG("Allocating new buffer of %d bytes for SUPL object", total_size);

		supl_buffer = malloc(total_size);
		supl_size = total_size;
		supl_downloaded = 0;
	}

	if ((supl_size !=
	     total_size)) { // if total size is different we are dealing with a new buffer
		MLOG_ERR("Wrong total size. supl_size=%d, total_size=%d", supl_size, total_size);
		free(supl_buffer);
		supl_buffer = NULL;
		return -EAGAIN;
	}
	if (supl_size < (supl_downloaded +
			 data_len)) // if there is not enough buffer left, something has gone wrong
	{
		MLOG_ERR("Not enough buffer left. supl_size=%d, supl_downloaded=%d, data_len=%d. Disposing the buffer.",
			supl_size, supl_downloaded, data_len);
		free(supl_buffer);
		supl_buffer = NULL;
		return -EAGAIN;
	}

	memcpy(supl_buffer + supl_downloaded, data, data_len);
	supl_downloaded += data_len;

	if (last_block) {
		MLOG_DBG("Last SUPL block received.");
		int ret = handle_supl_data();
		free(supl_buffer);
		supl_buffer = NULL;
		return ret;
	}

	return 0;
}

/* Function that is called whenever the configuration object is written to. */
static int config_update_cb(uint16_t obj_inst_id, uint16_t res_id, uint16_t res_inst_id,
			    uint8_t *data, uint16_t data_len, bool last_block, size_t total_size)
{
	/* Because we are dependent on providing all configurations in the
	 * CLOUD_CODEC_EVT_CONFIG_UPDATE event, all configuration is retrieved whenever the
	 * configuration object changes.
	 */
	ARG_UNUSED(obj_inst_id);
	ARG_UNUSED(res_id);
	ARG_UNUSED(res_inst_id);
	ARG_UNUSED(data);
	ARG_UNUSED(data_len);
	ARG_UNUSED(last_block);
	ARG_UNUSED(total_size);

	int err;
	struct cloud_data_cfg cfg = { 0 };
	struct cloud_codec_evt evt = {
		.type = CLOUD_CODEC_EVT_CONFIG_UPDATE
	};

	/* There has been a configuration update. Send callback to application with the latest
	 * state of the configuration.
	 */
	err = lwm2m_engine_get_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, LOCATION_TIMEOUT_RID),
				   &cfg.location_timeout);
	if (err) {
		MLOG_ERR("Failed getting GNSS resource value.");
	}

	err = lwm2m_engine_get_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, ACTIVE_WAIT_TIMEOUT_RID),
				   &cfg.active_wait_timeout);
	if (err) {
		MLOG_ERR("Failed getting Active wait timeout resource value.");
	}

	err = lwm2m_engine_get_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, MOVEMENT_RESOLUTION_RID),
				   &cfg.movement_resolution);
	if (err) {
		MLOG_ERR("Failed getting Movement resolution resource value.");
	}

	err = lwm2m_engine_get_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, MOVEMENT_TIMEOUT_RID),
				   &cfg.movement_timeout);
	if (err) {
		MLOG_ERR("Failed getting Movement timeout resource value.");
	}

	err = lwm2m_engine_get_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
				     ACCELEROMETER_ACT_THRESHOLD_RID),
				     &cfg.accelerometer_activity_threshold);
	if (err) {
		MLOG_ERR("Failed getting Accelerometer activity threshold resource value.");
	}

	err = lwm2m_engine_get_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
				     ACCELEROMETER_INACT_THRESHOLD_RID),
				     &cfg.accelerometer_inactivity_threshold);
	if (err) {
		MLOG_ERR("Failed getting Accelerometer inactivity threshold resource value.");
	}

	err = lwm2m_engine_get_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
				     ACCELEROMETER_INACT_TIMEOUT_RID),
				     &cfg.accelerometer_inactivity_timeout);
	if (err) {
		MLOG_ERR("Failed getting Accelerometer inactivity timeout resource value.");
	}

	/* If the GNSS and neighbor cell entry in the No data structure is set, its disabled in the
	 * corresponding object.
	 */
	bool gnss_enable_temp;
	bool ncell_enable_temp;
	bool passive_mode_temp;

	err = lwm2m_engine_get_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID,
					       0,
					       PASSIVE_MODE_RID),
					       &passive_mode_temp);
	if (err) {
		MLOG_ERR("Failed getting Passive mode resource value.");
	}

	cfg.active_mode = (passive_mode_temp == true) ? false : true;

	err = lwm2m_engine_get_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID,
					       0,
					       GNSS_ENABLE_RID),
					       &gnss_enable_temp);
	if (err) {
		MLOG_ERR("Failed getting GNSS enable resource value.");
	}

	cfg.no_data.gnss = (gnss_enable_temp == true) ? false : true;

	err = lwm2m_engine_get_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID,
					       0,
					       NEIGHBOR_CELL_ENABLE_RID),
					       &ncell_enable_temp);
	if (err) {
		MLOG_ERR("Failed getting Neighbor cell enable resource value.");
	}

	cfg.no_data.neighbor_cell = (ncell_enable_temp == true) ? false : true;

	evt.config_update = cfg;
	module_evt_handler(&evt);

	return 0;
}

int cloud_codec_init(struct cloud_data_cfg *cfg, cloud_codec_evt_handler_t event_handler)
{
	int err;

	err = lwm2m_engine_create_res_inst(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      AVAIL_NETWORK_BEARER_ID, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_res_inst(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      AVAIL_NETWORK_BEARER_ID, 1));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_res_inst(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      IP_ADDRESSES, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_res_inst(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      APN, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_obj_inst(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_obj_inst(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_obj_inst(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0));
	if (err) {
		return err;
	}

	err = lwm2m_engine_create_res_inst(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
						      POWER_SOURCE_VOLTAGE_RID, 0));
	if (err) {
		return err;
	}

	/* Device current time. */
	err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
						  CURRENT_TIME_RID),
				       &current_time, sizeof(current_time), sizeof(current_time),
				       LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	/* Battery voltage buffer. */
	err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
						  POWER_SOURCE_VOLTAGE_RID),
				       &battery_voltage, sizeof(battery_voltage),
				       sizeof(battery_voltage), LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	/* Sensor timestamps. */
	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, TIMESTAMP_RID),
				       &pressure_ts, sizeof(pressure_ts),
				       sizeof(pressure_ts), LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, TIMESTAMP_RID),
				       &temperature_ts, sizeof(temperature_ts),
				       sizeof(temperature_ts), LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
						  TIMESTAMP_RID),
				       &humidity_ts, sizeof(humidity_ts),
				       sizeof(humidity_ts), LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	/* Temperature object. */
	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, MIN_RANGE_VALUE_RID),
				     &temp_min_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, MAX_RANGE_VALUE_RID),
				     &temp_max_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, SENSOR_UNITS_RID),
				       BME680_TEMP_UNIT, sizeof(BME680_TEMP_UNIT),
				       sizeof(BME680_TEMP_UNIT), LWM2M_RES_DATA_FLAG_RO);
	if (err) {
		return err;
	}

	/* Humidity object. */
	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
						MIN_RANGE_VALUE_RID),
				     &humid_min_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
						MAX_RANGE_VALUE_RID),
				     &humid_max_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
						  SENSOR_UNITS_RID),
				       BME680_HUMID_UNIT, sizeof(BME680_HUMID_UNIT),
				       sizeof(BME680_HUMID_UNIT), LWM2M_RES_DATA_FLAG_RO);
	if (err) {
		return err;
	}

	/* Pressure object. */
	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, MIN_RANGE_VALUE_RID),
				     &pressure_min_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, MAX_RANGE_VALUE_RID),
				     &pressure_max_range_val);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, SENSOR_UNITS_RID),
				       BME680_PRESSURE_UNIT, sizeof(BME680_PRESSURE_UNIT),
				       sizeof(BME680_PRESSURE_UNIT), LWM2M_RES_DATA_FLAG_RO);
	if (err) {
		return err;
	}

	/* Current configuration. */
	err = lwm2m_engine_set_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, PASSIVE_MODE_RID),
				    !cfg->active_mode);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, LOCATION_TIMEOUT_RID),
				   cfg->location_timeout);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, ACTIVE_WAIT_TIMEOUT_RID),
				   cfg->active_wait_timeout);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, MOVEMENT_RESOLUTION_RID),
				   cfg->movement_resolution);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_s32(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0, MOVEMENT_TIMEOUT_RID),
				   cfg->movement_timeout);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
						ACCELEROMETER_ACT_THRESHOLD_RID),
				     &cfg->accelerometer_activity_threshold);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
						ACCELEROMETER_INACT_THRESHOLD_RID),
				     &cfg->accelerometer_inactivity_threshold);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_float(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
						ACCELEROMETER_INACT_TIMEOUT_RID),
				     &cfg->accelerometer_inactivity_timeout);
	if (err) {
		return err;
	}

	/* If the GNSS and Neighbor cell entry in the No data structure is set, its disabled in the
	 * corresponding object.
	 */
	err = lwm2m_engine_set_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
					       GNSS_ENABLE_RID),
				    !cfg->no_data.gnss);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_bool(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
					       NEIGHBOR_CELL_ENABLE_RID),
				    !cfg->no_data.neighbor_cell);
	if (err) {
		return err;
	}

	/* Register callback for SUPL updates. */
	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(SUPL_OBJECT_ID, 0,
								   SUPL_DATA_RID),
							supl_write_cb);
	if (err) {
		return err;
	}

	/* Register callback for configuration changes. */
	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   PASSIVE_MODE_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   LOCATION_TIMEOUT_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   ACTIVE_WAIT_TIMEOUT_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   MOVEMENT_RESOLUTION_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   MOVEMENT_TIMEOUT_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(
		LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
			   ACCELEROMETER_ACT_THRESHOLD_RID),
		config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(
		LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
			   ACCELEROMETER_INACT_THRESHOLD_RID),
		config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(
		LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
			   ACCELEROMETER_INACT_TIMEOUT_RID),
		config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   GNSS_ENABLE_RID),
							config_update_cb);
	if (err) {
		return err;
	}

	err = lwm2m_engine_register_post_write_callback(LWM2M_PATH(CONFIGURATION_OBJECT_ID, 0,
								   NEIGHBOR_CELL_ENABLE_RID),
							config_update_cb);
	if (err) {
		return err;
	}


	/* Create Button 1 object instance. */
	err = lwm2m_engine_create_obj_inst(LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID,
						      BUTTON1_OBJ_INST_ID));
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(
		LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID, BUTTON1_OBJ_INST_ID, APPLICATION_TYPE_RID),
		BUTTON1_APP_NAME, sizeof(BUTTON1_APP_NAME), sizeof(BUTTON1_APP_NAME),
		LWM2M_RES_DATA_FLAG_RO);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_res_buf(
		LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID, BUTTON1_OBJ_INST_ID, TIMESTAMP_RID),
		&button_ts, sizeof(button_ts), sizeof(button_ts), LWM2M_RES_DATA_FLAG_RW);
	if (err) {
		return err;
	}

	/* Create Button 2 object instance. */
	if (CONFIG_LWM2M_IPSO_PUSH_BUTTON_INSTANCE_COUNT == 2) {
		err = lwm2m_engine_create_obj_inst(LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID,
						   BUTTON2_OBJ_INST_ID));
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID,
							  BUTTON2_OBJ_INST_ID,
							  APPLICATION_TYPE_RID),
					       BUTTON2_APP_NAME, sizeof(BUTTON2_APP_NAME),
					       sizeof(BUTTON2_APP_NAME), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID,
							  BUTTON2_OBJ_INST_ID, TIMESTAMP_RID),
					       &button_ts,
					       sizeof(button_ts),
					       sizeof(button_ts),
					       LWM2M_RES_DATA_FLAG_RW);
		if (err) {
			return err;
		}
	}

	module_evt_handler = event_handler;

	return 0;
}

int cloud_codec_encode_neighbor_cells(struct cloud_codec_data *output,
				      struct cloud_data_neighbor_cells *neighbor_cells)
{
	int err;
	struct lte_lc_cells_info *cells = &neighbor_cells->cell_data;

	cells->neighbor_cells = neighbor_cells->neighbor_cells;
	cells->ncells_count = neighbor_cells->cell_data.ncells_count;

	if (!neighbor_cells->queued) {
		return -ENODATA;
	}

#if defined(CONFIG_LWM2M_CLIENT_UTILS_LOCATION_ASSIST_AGPS)
	err = lwm2m_update_signal_meas_objects((const struct lte_lc_cells_info *)cells);
	if (err == -ENODATA) {
		MLOG_DBG("No neighboring cells available");
	} else if (err) {
		MLOG_ERR("lwm2m_update_signal_meas_objects, error: %d", err);
		return err;
	}
#endif

	err = lwm2m_engine_set_s8(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					     RADIO_SIGNAL_STRENGTH),
				  neighbor_cells->cell_data.current_cell.rsrp);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u32(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      CELLID),
				   neighbor_cells->cell_data.current_cell.id);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      SMNC),
				   neighbor_cells->cell_data.current_cell.mnc);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      SMCC),
				   neighbor_cells->cell_data.current_cell.mcc);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      LAC),
				   neighbor_cells->cell_data.current_cell.tac);
	if (err) {
		return err;
	}

	neighbor_cells->queued = false;
	return 0;
}

int cloud_codec_encode_agps_request(struct cloud_codec_data *output,
				    struct cloud_data_agps_request *agps_request)
{
#if defined(CONFIG_SUPL_CLIENT_LIB)
       /* SUPL doesn't usually provide satellite real time integrity information. If GNSS asks
        * only for satellite integrity, the request should be ignored.
        */
       if (agps_request->request.sv_mask_ephe == 0 &&
           agps_request->request.sv_mask_alm == 0 &&
           agps_request->request.data_flags == NRF_MODEM_GNSS_AGPS_INTEGRITY_REQUEST) {
               MLOG_INF(" Ignoring assistance request for only satellite integrity");
       } else {
               MLOG_INF(" Assistance data needed, ephe 0x%08x, alm 0x%08x, flags 0x%02x",
                       agps_request->request.sv_mask_ephe,
                       agps_request->request.sv_mask_alm,
                       agps_request->request.data_flags);
               assistance_request(&agps_request->request);
       }
       return -ENOTSUP;
#elif defined(CONFIG_LWM2M_CLIENT_UTILS_LOCATION_ASSIST_AGPS)
	ARG_UNUSED(output);

	int err;

	err = location_assistance_agps_set_mask(&agps_request->request);
	if (err) {
		MLOG_ERR("location_assistance_agps_set_mask, error: %d", err);
		return err;
	}

	/* Disable filtered A-GPS. */
	location_assist_agps_set_elevation_mask(-1);

	err = lwm2m_engine_set_u32(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      CELLID),
				   agps_request->cell);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      SMNC),
				   agps_request->mnc);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      SMCC),
				   agps_request->mcc);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
					      LAC),
				   agps_request->area);
	if (err) {
		return err;
	}

	agps_request->queued = false;
	return 0;
#else
	return -ENOTSUP;
#endif
}

int cloud_codec_encode_pgps_request(struct cloud_codec_data *output,
				    struct cloud_data_pgps_request *pgps_request)
{
	ARG_UNUSED(output);

	int err;

	err = location_assist_pgps_set_prediction_count(pgps_request->count);
	if (err) {
		MLOG_ERR("location_assist_pgps_set_prediction_count, error: %d", err);
		return err;
	}

	err = location_assist_pgps_set_prediction_interval(pgps_request->interval);
	if (err) {
		MLOG_ERR("location_assist_pgps_set_prediction_interval, error: %d", err);
		return err;
	}

	err = location_assist_pgps_set_start_time(pgps_request->time);
	if (err) {
		MLOG_ERR("location_assist_pgps_set_start_time, error: %d", err);
		return err;
	}

	location_assist_pgps_set_start_gps_day(pgps_request->day);

	pgps_request->queued = false;
	return 0;
}

int cloud_codec_decode_config(const char *input, size_t input_len,
			      struct cloud_data_cfg *cfg)
{
	return -ENOTSUP;
}

int cloud_codec_encode_config(struct cloud_codec_data *output,
			      struct cloud_data_cfg *data)
{
	return -ENOTSUP;
}

int cloud_codec_encode_data(struct cloud_codec_data *output,
			    struct cloud_data_gnss *gnss_buf,
			    struct cloud_data_sensors *sensor_buf,
			    struct cloud_data_modem_static *modem_stat_buf,
			    struct cloud_data_modem_dynamic *modem_dyn_buf,
			    struct cloud_data_ui *ui_buf,
			    struct cloud_data_impact *impact_buf,
			    struct cloud_data_battery *bat_buf)
{
	ARG_UNUSED(ui_buf);

	int err;
	bool objects_written = false;
	double alt, acc, spd;

	/* GPS PVT */
	if (gnss_buf->queued) {
		/*
		 * The LwM2M engine expect double pointers, so floats have to be casted to double
		 * first. Then we can pass pointers to the double values.
		 */
		alt = (double) gnss_buf->pvt.alt;
		acc = (double) gnss_buf->pvt.acc;
		spd = (double) gnss_buf->pvt.spd;

		err = date_time_uptime_to_unix_time_ms(&gnss_buf->gnss_ts);
		if (err) {
			MLOG_ERR("date_time_uptime_to_unix_time_ms, error: %d", err);
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0, LATITUDE_RID),
					     &gnss_buf->pvt.lat);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0, LONGITUDE_RID),
					     &gnss_buf->pvt.longi);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0, ALTITUDE_RID),
					     &alt);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0, RADIUS_RID),
					     &acc);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0, SPEED_RID),
					     &spd);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_s32(LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID, 0,
						      LOCATION_TIMESTAMP_RID),
					   (int32_t)(gnss_buf->gnss_ts / MSEC_PER_SEC));
		if (err) {
			return err;
		}

		static const char * const path_list[] = {
			LWM2M_PATH(LWM2M_OBJECT_LOCATION_ID)
		};

		err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
		if (err) {
			MLOG_ERR("Failed populating object path list, error: %d", err);
			return err;
		}

		gnss_buf->queued = false;
		objects_written = true;
	}

	/* Modem dynamic */
	if (modem_dyn_buf->queued) {
		if (modem_dyn_buf->nw_mode == LTE_LC_LTE_MODE_LTEM) {
			err = lwm2m_engine_set_u8(LWM2M_PATH(
							LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
							NETWORK_BEARER_ID),
						  LTE_FDD_BEARER);
			if (err) {
				return err;
			}
		} else if (modem_dyn_buf->nw_mode == LTE_LC_LTE_MODE_NBIOT) {
			err = lwm2m_engine_set_u8(LWM2M_PATH(
							LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
							NETWORK_BEARER_ID),
						  NB_IOT_BEARER);
			if (err) {
				return err;
			}
		} else {
			MLOG_WRN("No network bearer set");
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID,
							  0, AVAIL_NETWORK_BEARER_ID, 0),
					       &bearers[0], sizeof(bearers[0]), sizeof(bearers[0]),
					       LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID,
							   0, AVAIL_NETWORK_BEARER_ID, 1),
					       &bearers[1], sizeof(bearers[1]), sizeof(bearers[1]),
					       LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID,
							   0, IP_ADDRESSES, 0),
					       modem_dyn_buf->ip, sizeof(modem_dyn_buf->ip),
					       sizeof(modem_dyn_buf->ip), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID,
							   0, APN, 0),
					       modem_dyn_buf->apn, sizeof(modem_dyn_buf->apn),
					       sizeof(modem_dyn_buf->apn), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_s8(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      RADIO_SIGNAL_STRENGTH),
					  modem_dyn_buf->rsrp);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_u32(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      CELLID),
					   modem_dyn_buf->cell);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      SMNC),
					   modem_dyn_buf->mnc);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      SMCC),
					   modem_dyn_buf->mcc);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_u16(LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 0,
						      LAC),
					   modem_dyn_buf->area);
		if (err) {
			return err;
		}

		err = date_time_now(&current_time);
		if (err) {
			MLOG_WRN("Failed getting current time, error: %d", err);
			return err;
		}

		err = lwm2m_engine_set_s32(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, CURRENT_TIME_RID),
					   (int32_t)(current_time / MSEC_PER_SEC));
		if (err) {
			return err;
		}

		static const char * const path_list[] = {
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, CURRENT_TIME_RID),
			LWM2M_PATH(LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID)
		};

		err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
		if (err) {
			MLOG_ERR("Failed populating object path list, error: %d", err);
			return err;
		}

		modem_dyn_buf->queued = false;
		objects_written = true;
	}

	/* Modem static */
	if (modem_stat_buf->queued) {

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
							  MODEL_NUMBER_RID),
					       CONFIG_BOARD,
					       sizeof(CONFIG_BOARD),
					       sizeof(CONFIG_BOARD),
					       LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
							  HARDWARE_VERSION_RID),
					       CONFIG_SOC,
					       sizeof(CONFIG_SOC),
					       sizeof(CONFIG_SOC),
					       LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
							  MANUFACTURER_RID),
					       CONFIG_CLOUD_CODEC_MANUFACTURER,
					       sizeof(CONFIG_CLOUD_CODEC_MANUFACTURER),
					       sizeof(CONFIG_CLOUD_CODEC_MANUFACTURER),
					       LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, FIRMWARE_VERSION_RID),
			modem_stat_buf->appv, sizeof(modem_stat_buf->appv),
			sizeof(modem_stat_buf->appv), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, SOFTWARE_VERSION_RID),
			modem_stat_buf->fw, sizeof(modem_stat_buf->fw),
			sizeof(modem_stat_buf->fw), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_res_buf(
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_SERIAL_NUMBER_ID),
			modem_stat_buf->imei, sizeof(modem_stat_buf->imei),
			sizeof(modem_stat_buf->imei), LWM2M_RES_DATA_FLAG_RO);
		if (err) {
			return err;
		}

		static const char * const path_list[] = {
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, MANUFACTURER_RID),
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, MODEL_NUMBER_RID),
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, SOFTWARE_VERSION_RID),
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, FIRMWARE_VERSION_RID),
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_SERIAL_NUMBER_ID)
		};

		err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
		if (err) {
			MLOG_ERR("Failed populating object path list, error: %d", err);
			return err;
		}

		modem_stat_buf->queued = false;
		objects_written = true;
	}

	if (bat_buf->queued) {

		err = lwm2m_engine_set_s32(LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0,
						      POWER_SOURCE_VOLTAGE_RID),
					   bat_buf->bat);
		if (err) {
			return err;
		}

		static const char * const path_list[] = {
			LWM2M_PATH(LWM2M_OBJECT_DEVICE_ID, 0, POWER_SOURCE_VOLTAGE_RID)
		};

		err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
		if (err) {
			MLOG_ERR("Failed populating object path list, error: %d", err);
			return err;
		}

		bat_buf->queued = false;
		objects_written = true;
	}

	/* Environmental sensor data */
	if (sensor_buf->queued) {

		err = date_time_uptime_to_unix_time_ms(&sensor_buf->env_ts);
		if (err) {
			MLOG_ERR("date_time_uptime_to_unix_time_ms, error: %d", err);
			return err;
		}

		err = lwm2m_engine_set_s32(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0,
						      TIMESTAMP_RID),
					   (int32_t)(sensor_buf->env_ts / MSEC_PER_SEC));
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_s32(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
						      TIMESTAMP_RID),
					   (int32_t)(sensor_buf->env_ts / MSEC_PER_SEC));
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_s32(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0,
						      TIMESTAMP_RID),
					   (int32_t)(sensor_buf->env_ts / MSEC_PER_SEC));
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0,
							SENSOR_VALUE_RID),
					     &sensor_buf->temperature);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0,
							SENSOR_VALUE_RID),
					     &sensor_buf->humidity);
		if (err) {
			return err;
		}

		err = lwm2m_engine_set_float(LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0,
							SENSOR_VALUE_RID),
					     &sensor_buf->pressure);
		if (err) {
			return err;
		}

		static const char * const path_list[] = {
			LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, TIMESTAMP_RID),
			LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0, TIMESTAMP_RID),
			LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, TIMESTAMP_RID),
			LWM2M_PATH(IPSO_OBJECT_TEMP_SENSOR_ID, 0, SENSOR_VALUE_RID),
			LWM2M_PATH(IPSO_OBJECT_HUMIDITY_SENSOR_ID, 0, SENSOR_VALUE_RID),
			LWM2M_PATH(IPSO_OBJECT_PRESSURE_ID, 0, SENSOR_VALUE_RID)
		};

		err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
		if (err) {
			MLOG_ERR("Failed populating object path list, error: %d", err);
			return err;
		}

		sensor_buf->queued = false;
		objects_written = true;
	}

	if (!objects_written) {
		return -ENODATA;
	}

	return 0;
}

int cloud_codec_encode_ui_data(struct cloud_codec_data *output,
			       struct cloud_data_ui *ui_buf)
{
	int err, len;

	if (!ui_buf->queued) {
		return -ENODATA;
	}

	/* Create object instance according to the button number. */
	char digital_input_path[12] = "\0", timestamp_path[12] = "\0";

	len = snprintk(digital_input_path,
		       sizeof(digital_input_path),
		       "%d/%d/%d",
		       IPSO_OBJECT_PUSH_BUTTON_ID,
		       (ui_buf->btn - 1),
		       DIGITAL_INPUT_STATE_RID);
	if ((len < 0) || (len >= sizeof(digital_input_path))) {
		return -ERANGE;
	}

	len = snprintk(timestamp_path,
		       sizeof(timestamp_path),
		       "%d/%d/%d",
		       IPSO_OBJECT_PUSH_BUTTON_ID,
		       (ui_buf->btn - 1),
		       TIMESTAMP_RID);
	if ((len < 0) || (len >= sizeof(timestamp_path))) {
		return -ERANGE;
	}

	err = date_time_uptime_to_unix_time_ms(&ui_buf->btn_ts);
	if (err) {
		MLOG_ERR("date_time_uptime_to_unix_time_ms, error: %d", err);
		return err;
	}

	/* Toggle digital input state of the toggled button to increment
	 * the digital input counter. This is done automatically in the push button object
	 * abstraction in the LwM2M Zephyr API.
	 */
	err = lwm2m_engine_set_bool(digital_input_path, true);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_bool(digital_input_path, false);
	if (err) {
		return err;
	}

	err = lwm2m_engine_set_s32(timestamp_path, (int32_t)(ui_buf->btn_ts / MSEC_PER_SEC));
	if (err) {
		return err;
	}

	static const char * const path_list[] = {
		LWM2M_PATH(IPSO_OBJECT_PUSH_BUTTON_ID),
	};

	err = object_path_list_add(output, path_list, ARRAY_SIZE(path_list));
	if (err) {
		MLOG_ERR("Failed populating object path list, error: %d", err);
		return err;
	}

	ui_buf->queued = false;
	return 0;
}

int cloud_codec_encode_impact_data(struct cloud_codec_data *output,
				   struct cloud_data_impact *impact_buf)
{
	return -ENOTSUP;
}

int cloud_codec_encode_batch_data(struct cloud_codec_data *output,
				  struct cloud_data_gnss *gnss_buf,
				  struct cloud_data_sensors *sensor_buf,
				  struct cloud_data_modem_static *modem_stat_buf,
				  struct cloud_data_modem_dynamic *modem_dyn_buf,
				  struct cloud_data_ui *ui_buf,
				  struct cloud_data_impact *impact_buf,
				  struct cloud_data_battery *bat_buf,
				  size_t gnss_buf_count,
				  size_t sensor_buf_count,
				  size_t modem_stat_buf_count,
				  size_t modem_dyn_buf_count,
				  size_t ui_buf_count,
				  size_t impact_buf_count,
				  size_t bat_buf_count)
{
	return -ENOTSUP;
}
