/*
 * BLE Mesh Smart Metter model HIKING DDS238-2 WIFI
 *
 * (C)2023-2025 St(u)dio of Computer Games
 */

#include <stdint.h>
#include <string.h>

/* HAL */
#include "boards.h"
#include "app_timer.h"

/* core */
#include "nrf_mesh_config_core.h"
#include "nrf_mesh_gatt.h"
#include "nrf_mesh_configure.h"
#include "nrf_mesh_events.h"
#include "nrf_mesh.h"
#include "mesh_stack.h"
#include "device_state_manager.h"
#include "access_config.h"
#include "proxy.h"
#include "bearer_event.h"

/* provisioning and configuration */
#include "mesh_provisionee_output_numeric.h"
#include "mesh_app_utils.h"

/* models */
#include "model_config_file.h"
//#include "scene_setup_server.h"
//#include "generic_onoff_server.h"

/* application specific includes */
#include "nordic_common.h"
#include "nrf_mesh_config_app.h"
#include "nrf.h"
#include "example_common.h"
#include "nrf_drv_pwm.h"
#include "nrf_mesh_config_examples.h"
#include "ble_softdevice_support.h"
//#include "app_onoff.h"
//#include "app_dtt.h"
//#include "app_scene.h"
#include "app_config.h"
//#include "sensor_utils.h"
//#include "app_sensor.h"
//#include "app_sensor_utils.h"

/* Tuya */
#include "tuya.h"

/* logging */
#include "log.h"


/*** Definitions ***/

#define SCHED_MAX_EVENT_DATA_SIZE                               (APP_TIMER_SCHED_EVENT_DATA_SIZE + sizeof(uint16_t))
#define SCHED_QUEUE_SIZE                                        (10)

/* Controls if the model instance should force all mesh messages to be segmented messages. */
#define APP_FORCE_SEGMENTATION                                  (false)
/* Controls the MIC size used by the model instance for sending the mesh messages. */
#define APP_MIC_SIZE                                            (NRF_MESH_TRANSMIC_SIZE_SMALL)
/** The maximum message size in bytes. */
#define APP_MAX_MESSAGE_BYTES                                   (256)

#define UNSPECIFIED                                             (0)
#define TYUA_UPDATE_PERIOD                                      (100)                   /* 30 sec */

#define APP_ONOFF_ELEMENT_INDEX                                 (0)
#define APP_SENSOR_ELEMENT_INDEX                                (0)

#define TUYA_START_INTERVAL                                     APP_TIMER_TICKS(3000)   /* 3 sec */
//#define TUYA_HEARTBEAT_INTERVAL                                 APP_TIMER_TICKS(15000)  /* 15 sec */
#define TUYA_HEARTBEAT_INTERVAL                                 APP_TIMER_TICKS(1000)  /* 1 sec */

#define TUYA_INITIALIZATION_MAX                                 3

//#define TUYA_DPID_SWITCH                                        1
//#define TUYA_DPID_CURRENT                                       18
//#define TUYA_DPID_POWER                                         19
//#define TUYA_DPID_VOLTAGE                                       20
//#define TUYA_DPID_POWER_USAGE                                   101

//#define NUM_DESCRIPTORS                                         SENSOR_SETUP_SERVER_STORED_STATES_MAX



/* Tuya module initialization state */
enum tuya_state_e {
    TS_START = 0,
    TS_HELLO,
    TS_QUERY_PRODUCT,
    TS_MCU_CONF,
    TS_HEARTBEAT
};

/* device states */
enum device_state_e {
    APP_UNPROVISIONED = 0,
    APP_OUTPUT_REQUEST,
    APP_PROVISIONED,
    APP_RESET_WARN,
    APP_TUYA_ERROR
};

/* On/Off states */
enum onoff_state_e {
    ONOFF_OFF = 0,
    ONOFF_OFF_TRANSITION,
    ONOFF_ON,
    ONOFF_ON_TRANSITION
};



/*** forward declarations ***/

/* control */
static void set_device_state(enum device_state_e state);

/* models behavior */
//static void app_onoff_server_set_cb(const app_onoff_server_t *p_server, bool onoff);
//static void app_onoff_server_get_cb(const app_onoff_server_t *p_server, bool *p_present_onoff);
//static void app_onoff_server_transition_cb(const app_onoff_server_t *p_server,
//                                           uint32_t transition_time_ms, bool target_onoff);

//#if SCENE_SETUP_SERVER_INSTANCES_MAX > 0
//static void app_onoff_scene_transition_cb(const app_scene_setup_server_t *p_app,
//                                          uint32_t transition_time_ms,
//                                          uint16_t target_scene);
//#endif

//static void app_sensor_get_cb(const app_sensor_server_t * p_server,
//                              uint16_t property_id,
//                              uint8_t * p_sensor_data,
//                              uint16_t * p_out_bytes);

//static void app_sensor_settings_get_cb(const app_sensor_server_t * p_server,
//                                       uint16_t property_id,
//                                       sensor_settings_status_msg_pkt_t * p_out,
//                                       uint16_t * p_out_bytes);

//static void app_sensor_setting_set_cb(const app_sensor_server_t * p_server,
//                                      uint16_t property_id,
//                                      uint16_t setting_property_id,
//                                      const sensor_setting_set_msg_pkt_t * p_in,
//                                      uint16_t in_len,
//                                      sensor_setting_status_msg_pkt_t * p_out,
//                                      uint16_t * p_out_bytes);

//static void app_sensor_setting_get_cb(const app_sensor_server_t * p_server,
//                                      uint16_t property_id,
//                                      uint16_t setting_property_id,
//                                      sensor_setting_status_msg_pkt_t * p_out,
//                                      uint16_t * p_out_bytes);

//static void app_sensor_column_get_cb(const app_sensor_server_t * p_server,
//                                     const sensor_column_get_msg_pkt_t * p_in,
//                                     uint16_t in_len,
//                                     sensor_column_status_msg_pkt_t * p_out,
//                                     uint16_t * p_out_len);

//static void app_sensor_series_get_cb(const app_sensor_server_t * p_server,
//                                     const sensor_series_get_msg_pkt_t * p_in,
//                                     uint16_t in_len,
//                                     sensor_series_status_msg_pkt_t * p_out,
//                                     uint16_t * p_out_len);


/* Tuya module */
static void tuya_module_init();
static void tuya_module_start();
static void tuya_set_on_off(bool val);
static void tuya_set_current(uint32_t val);
static void tuya_set_power(uint32_t val);
static void tuya_set_voltage(uint32_t val);
static void tuya_set_energy(uint32_t val);


/* proivisioning */
static void provisioning_output_request_cb(uint8_t action, uint8_t size, const uint8_t *p_data);
static void provisioning_complete_cb(void);
static void provisioning_abort_cb(void);

/* mesh basic */
static void mesh_events_handle(const nrf_mesh_evt_t *p_evt);
static void node_reset(void);



/*** static variables ***/
static bool m_device_provisioned;
//static enum device_state_e m_device_state;
static uint32_t m_pin_numeric;

//static enum onoff_state_e m_onoff_state;
//static chr_electric_current_t m_current_state;
//static chr_power_t m_power_state;
//static chr_voltage_t m_voltage_state;
//static chr_energy32_t m_energy_state;

//static bearer_event_flag_t m_on_off_status_flag;
//static bearer_event_flag_t m_sensor_power_status_flag;
//static bearer_event_flag_t m_sensor_current_status_flag;
//static bearer_event_flag_t m_sensor_voltage_status_flag;
//static bearer_event_flag_t m_sensor_energy_use_status_flag;

/* Generic Power OnOff Setup server structure definition and initialization */
//APP_ONOFF_SERVER_DEF(m_onoff_server,
//                     APP_FORCE_SEGMENTATION,
//                     APP_MIC_SIZE,
//                     app_onoff_server_set_cb,
//                     app_onoff_server_get_cb,
//                     app_onoff_server_transition_cb)

//#if SCENE_SETUP_SERVER_INSTANCES_MAX > 0

/* Defaut Transition Time server structure definition and initialization */
//APP_DTT_SERVER_DEF(m_dtt_server,
//                   APP_FORCE_SEGMENTATION,
//                   APP_MIC_SIZE,
//                   NULL)

/* Scene Setup server structure definition and initialization */
//APP_SCENE_SETUP_SERVER_DEF(m_scene_server,
//                           APP_FORCE_SEGMENTATION,
//                           APP_MIC_SIZE,
//                           app_onoff_scene_transition_cb,
//                           &m_dtt_server.server)
//#endif


/* Tuya module */
static enum tuya_state_e m_tuya_state;
static bool m_tuya_heartbeat_ack;
static int m_tuya_initialization_cnt;
APP_TIMER_DEF(m_tuya_timer_id);

/* Sensor server */
//static const sensor_descriptor_t m_power_metter_descriptor[NUM_DESCRIPTORS] =
//{
//    {
//        .property_id = SENSOR_PRESENT_DEVICE_INPUT_POWER_PROPERTY_ID,
//        .positive_tolerance = UNSPECIFIED,
//        .negative_tolerance = UNSPECIFIED,
//        .sampling_function = UNSPECIFIED,
//        .measurement_period = UNSPECIFIED,
//        .update_interval = TYUA_UPDATE_PERIOD
//    },
//    {
//        .property_id = SENSOR_PRESENT_INPUT_CURRENT_PROPERTY_ID,
//        .positive_tolerance = UNSPECIFIED,
//        .negative_tolerance = UNSPECIFIED,
//        .sampling_function = UNSPECIFIED,
//        .measurement_period = UNSPECIFIED,
//        .update_interval = TYUA_UPDATE_PERIOD
//    },
//    {
//        .property_id = SENSOR_PRESENT_INPUT_VOLTAGE_PROPERTY_ID,
//        .positive_tolerance = UNSPECIFIED,
//        .negative_tolerance = UNSPECIFIED,
//        .sampling_function = UNSPECIFIED,
//        .measurement_period = UNSPECIFIED,
//        .update_interval = TYUA_UPDATE_PERIOD
//    },
//    {
//        .property_id = SENSOR_PRECISE_TOTAL_DEVICE_ENERGY_USE_PROPERTY_ID,
//        .positive_tolerance = UNSPECIFIED,
//        .negative_tolerance = UNSPECIFIED,
//        .sampling_function = UNSPECIFIED,
//        .measurement_period = UNSPECIFIED,
//        .update_interval = TYUA_UPDATE_PERIOD
//    }
//};

//static uint16_t m_property_array[NUM_DESCRIPTORS + 1] = {
//    NUM_DESCRIPTORS,
//    SENSOR_PRESENT_DEVICE_INPUT_POWER_PROPERTY_ID,
//    SENSOR_PRESENT_INPUT_CURRENT_PROPERTY_ID,
//    SENSOR_PRESENT_INPUT_VOLTAGE_PROPERTY_ID,
//    SENSOR_PRECISE_TOTAL_DEVICE_ENERGY_USE_PROPERTY_ID
//};

//APP_TIMER_DEF(m_sensor_server_cadence_timer_0);
//APP_TIMER_DEF(m_sensor_server_cadence_timer_1);
//APP_TIMER_DEF(m_sensor_server_cadence_timer_2);
//APP_TIMER_DEF(m_sensor_server_cadence_timer_3);

//APP_TIMER_DEF(m_sensor_server_min_interval_timer_0);
//APP_TIMER_DEF(m_sensor_server_min_interval_timer_1);
//APP_TIMER_DEF(m_sensor_server_min_interval_timer_2);
//APP_TIMER_DEF(m_sensor_server_min_interval_timer_3);

//static app_timer_id_t m_cadence_timer_ids[NUM_DESCRIPTORS] =
//{
//    &m_sensor_server_cadence_timer_0_data,
//    &m_sensor_server_cadence_timer_1_data,
//    &m_sensor_server_cadence_timer_2_data,
//    &m_sensor_server_cadence_timer_3_data
//};

//static app_timer_id_t m_min_interval_timer_ids[NUM_DESCRIPTORS] =
//{
//    &m_sensor_server_min_interval_timer_0_data,
//    &m_sensor_server_min_interval_timer_1_data,
//    &m_sensor_server_min_interval_timer_2_data,
//    &m_sensor_server_min_interval_timer_3_data,
//};

//static uint8_t m_sensor_message_buffer[APP_MAX_MESSAGE_BYTES];

//APP_SENSOR_SERVER_DEF(m_sensor_server,
//                      APP_FORCE_SEGMENTATION,
//                      APP_MIC_SIZE,
//                      app_sensor_get_cb,
//                      app_sensor_settings_get_cb,
//                      app_sensor_setting_set_cb,
//                      app_sensor_setting_get_cb,
//                      app_sensor_column_get_cb,
//                      app_sensor_series_get_cb,
//                      m_property_array,
//                      m_cadence_timer_ids,
//                      m_min_interval_timer_ids,
//                      m_power_metter_descriptor,
//                      NUM_DESCRIPTORS,
//                      m_sensor_message_buffer,
//                      sizeof(m_sensor_message_buffer));

static nrf_mesh_evt_handler_t m_event_handler = {
    .evt_cb = mesh_events_handle,
};



/*** Helpers section ***/

static void unicast_address_print(void)
{
    dsm_local_unicast_address_t node_address;
    dsm_local_unicast_addresses_get(&node_address);
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Node Address: 0x%04x\n", node_address.address_start);
}



/*** Tuya section ***/

static bool tuya_module_update_state(const tuya_msg_t *p_msg)
{
//    switch (m_tuya_state) {
//    case TS_START:
//        __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "----- Start Tuya module -----\n");
//        m_tuya_state = TS_HELLO;
//        UNUSED_VARIABLE(tuya_send_heartbeat());
//        return true;

//    case TS_HELLO:
//        if (p_msg->cmd == TUYA_CMD_HEARTBEAT) {
//            __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "Receive answer on first HEARTBEAT\n");
//            m_tuya_state = TS_QUERY_PRODUCT;
//            UNUSED_VARIABLE(tuya_send_query_product());
//            return true;
//        }

//    case TS_QUERY_PRODUCT:
//        if (p_msg->cmd == TUYA_CMD_QUERY_PRODUCT) {
//#if NRF_MESH_LOG_ENABLE == 1
//            char str[256];
//            strncpy(str, (char *)p_msg->data, p_msg->size);
//            str[p_msg->size] = '\0';
//            __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "MCU Product: %s\n", str);
//#endif /* NRF_MESH_LOG_ENABLE */

//            m_tuya_state = TS_MCU_CONF;
//            UNUSED_VARIABLE(tuya_send_mcu_conf());
//            return true;
//        }
//        break;

//    case TS_MCU_CONF:
//        if (p_msg->cmd == TUYA_CMD_MCU_CONF) {
//#if NRF_MESH_LOG_ENABLE == 1
//            int val_gpio_led = p_msg->data[0];
//            int val_gpio_btn = p_msg->data[1];
//            __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "MCU Config: LED %d, button %d\n", val_gpio_led, val_gpio_btn);
//#endif /* NRF_MESH_LOG_ENABLE */

//            m_tuya_state = TS_HEARTBEAT;
//            UNUSED_VARIABLE(tuya_send_query_state());
//            m_tuya_heartbeat_ack = true;
//        }
//        return true;

//    case TS_HEARTBEAT:
        if (p_msg->cmd == TUYA_CMD_HEARTBEAT) {
            __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "Receive answer on HEARTBEAT\n");
            m_tuya_heartbeat_ack = true;
            return true;
        }
//        break;
//    }

    return false;
}


static void tuya_event_handler(tuya_evt_type_t evt, const tuya_msg_t *p_msg)
{
    switch (evt) {
    case TUYA_MSG:
        if (tuya_module_update_state(p_msg)) break;

//        if (p_msg->cmd == TUYA_CMD_STATE) {
//            tuya_msg_dp_t dp_msg;
//            if (tuya_parse_dp(p_msg->data, p_msg->size, &dp_msg) == NRF_SUCCESS) {
//                if (dp_msg.id == TUYA_DPID_SWITCH && dp_msg.type == TUYA_DP_TYPE_BOOL) {
//                    tuya_set_on_off(dp_msg.val.boolean ? true : false);
//                } else if (dp_msg.id == TUYA_DPID_CURRENT && dp_msg.type == TUYA_DP_TYPE_VALUE) {
//                    tuya_set_current(dp_msg.val.value);
//                } else if (dp_msg.id == TUYA_DPID_POWER && dp_msg.type == TUYA_DP_TYPE_VALUE) {
//                    tuya_set_power(dp_msg.val.value);
//                } else if (dp_msg.id == TUYA_DPID_VOLTAGE && dp_msg.type == TUYA_DP_TYPE_VALUE) {
//                    tuya_set_voltage(dp_msg.val.value);
//                } else if (dp_msg.id == TUYA_DPID_POWER_USAGE && dp_msg.type == TUYA_DP_TYPE_VALUE) {
//                    tuya_set_energy(dp_msg.val.value);
//                } else {
//#if NRF_MESH_LOG_ENABLE == 1
//                    switch (dp_msg.type) {
//                    case TUYA_DP_TYPE_BOOL:
//                        __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "Unsupported DP ID 0x%x, value %s\n",
//                                       dp_msg.id, dp_msg.val.boolean ? "true" : "false");
//                        break;
//                    case TUYA_DP_TYPE_VALUE:
//                        __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "Unsupported DP ID 0x%x, value %d\n",
//                              dp_msg.id, dp_msg.val.value);
//                        break;
//                    }
//#endif /* NRF_MESH_LOG_ENABLE */
//                }
//            } else {
//                __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "failed to parse Tuya message\n");
//            }
//        } else {
//#if NRF_MESH_LOG_ENABLE == 1
//            int i;
//            __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "Unsipported Tuya command 0x%02x, size %d\n",
//                  p_msg->cmd, p_msg->size);
//            for (i = 0; i < p_msg->size; i++) {
//                __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "%02x\n", p_msg->data[i]);
//            }
//#endif /* NRF_MESH_LOG_ENABLE */
//        }
//        break;

    case TUYA_INVALID_SIZE:
        __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "invalid size of Tuya message, size %d\n", p_msg->size);
        break;

    case TUYA_INVALID_CRC:
        __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "invalid CRC of Tuya message\n");
        break;
    }
}


static void tuya_heartbeat_timeout_handler(void * p_context)
{
//    if (m_tuya_state != TS_HEARTBEAT) {
//            if (m_tuya_initialization_cnt >= TUYA_INITIALIZATION_MAX) {
//                set_device_state(APP_TUYA_ERROR);
//                __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "Tuya module inital sequence failed\n");
//            } else {
//                m_tuya_initialization_cnt++;
//            }
//            tuya_module_start();
//    } else {
        if (!m_tuya_heartbeat_ack) {
            __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "no response received on HEARTBEAT\n");
//            set_device_state(APP_TUYA_ERROR);
//        } else if (m_device_state == APP_TUYA_ERROR) {
//            set_device_state(mesh_stack_is_device_provisioned() ? APP_PROVISIONED : APP_UNPROVISIONED);
        }
        __LOG(LOG_SRC_APP, LOG_LEVEL_DBG1, "send HEARTBEAT\n");
        m_tuya_heartbeat_ack = false;
        UNUSED_VARIABLE(tuya_send_heartbeat());
        APP_ERROR_CHECK(app_timer_start(m_tuya_timer_id, TUYA_HEARTBEAT_INTERVAL, NULL));
//    }
}


//static void tuya_set_on_off(bool val) {
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "TUYA: set switch to %s\n", val ? "on" : "off");
//    m_onoff_state = val ? ONOFF_ON : ONOFF_OFF;
//    bearer_event_flag_set(m_on_off_status_flag);
//}

//static void tuya_set_current(uint32_t val) {
//    m_current_state = (val + 5) / 10;
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "TUYA: set current %u (%u)\n", val, m_current_state);
//    bearer_event_flag_set(m_sensor_current_status_flag);
//}

//static void tuya_set_power(uint32_t val) {
//    m_power_state = val;
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "TUYA: set power %u (%d)\n", val, m_power_state);
//    bearer_event_flag_set(m_sensor_power_status_flag);
//}

//static void tuya_set_voltage(uint32_t val) {
//    m_voltage_state = val * 64 / 10;
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "TUYA: set voltage %u (%u)\n", val, m_voltage_state);
//    bearer_event_flag_set(m_sensor_voltage_status_flag);
//}

//static void tuya_set_energy(uint32_t val) {
//    m_energy_state = val * 10;
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "TUYA: set energy %u (%u)\n", val, m_energy_state);
//    bearer_event_flag_set(m_sensor_energy_use_status_flag);
//}


static void tuya_module_init()
{
    /* create Tuya heartbead timer */
    APP_ERROR_CHECK(app_timer_create(&m_tuya_timer_id,
                                     APP_TIMER_MODE_SINGLE_SHOT,
                                     tuya_heartbeat_timeout_handler));
//    m_tuya_initialization_cnt = 0;

    ERROR_CHECK(tuya_init(tuya_event_handler));
}

static void tuya_module_start()
{
    m_tuya_state = TS_START;
    UNUSED_VARIABLE(tuya_module_update_state(NULL));

    /* started timer for checking Tuya module start sequence */
    APP_ERROR_CHECK(app_timer_start(m_tuya_timer_id, TUYA_START_INTERVAL, NULL));
}



/*** Models behavior section ***/

/* on/off server */

/* Callback for updating the hardware state */
//static void app_onoff_server_set_cb(const app_onoff_server_t * p_server, bool onoff)
//{
    /* do nothing */
//}


/* Callback for reading the hardware state */
//static void app_onoff_server_get_cb(const app_onoff_server_t *p_server, bool *p_present_onoff)
//{
//    *p_present_onoff = (m_onoff_state == ONOFF_ON || m_onoff_state == ONOFF_ON_TRANSITION);
//}

/* Callback for updating the hardware state */
//static void app_onoff_server_transition_cb(const app_onoff_server_t *p_server,
//                                           uint32_t transition_time_ms, bool target_onoff)
//{
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Transition time: %d, Target OnOff: %d\n",
//          transition_time_ms, target_onoff);

//    if (transition_time_ms <= 0) {
//        tuya_send_state(TUYA_DPID_SWITCH, TUYA_DP_TYPE_BOOL, (uint8_t *)&target_onoff);
//    }
//}

//#if SCENE_SETUP_SERVER_INSTANCES_MAX > 0
//static void app_onoff_scene_transition_cb(const app_scene_setup_server_t * p_app,
//                                          uint32_t transition_time_ms,
//                                          uint16_t target_scene)
//{
    /* do nothing */
//}
//#endif


//static bool app_onoff_status_flag_cb(void)
//{
//    app_onoff_status_publish(&m_onoff_server);
//    return true;
//}



/* sensor server */

//static void app_sensor_get_cb(const app_sensor_server_t *p_server,
//                              uint16_t property_id,
//                              uint8_t *p_sensor_data,
//                              uint16_t *p_out_bytes)
//{
//    switch (property_id) {
//    case SENSOR_NO_PROPERTY_ID:
//        break;

//    case SENSOR_PRESENT_DEVICE_INPUT_POWER_PROPERTY_ID:
//        p_sensor_data[0] = (m_power_state >> 0) & 0xff;
//        p_sensor_data[1] = (m_power_state >> 8) & 0xff;
//        p_sensor_data[2] = (m_power_state >> 16) & 0xff;
//        *p_out_bytes = 3;
//        break;

//    case SENSOR_PRESENT_INPUT_CURRENT_PROPERTY_ID:
//        p_sensor_data[0] = (m_current_state >> 0) & 0xff;
//        p_sensor_data[1] = (m_current_state >> 8) & 0xff;
//        *p_out_bytes = 2;
//        break;

//    case SENSOR_PRESENT_INPUT_VOLTAGE_PROPERTY_ID:
//        p_sensor_data[0] = (m_voltage_state >> 0) & 0xff;
//        p_sensor_data[1] = (m_voltage_state >> 8) & 0xff;
//        *p_out_bytes = 2;
//        break;

//    case SENSOR_PRECISE_TOTAL_DEVICE_ENERGY_USE_PROPERTY_ID:
//        p_sensor_data[0] = (m_energy_state >> 0) & 0xff;
//        p_sensor_data[1] = (m_energy_state >> 8) & 0xff;
//        p_sensor_data[2] = (m_energy_state >> 16) & 0xff;
//        p_sensor_data[3] = (m_energy_state >> 24) & 0xff;
//        *p_out_bytes = 4;
//        break;

//    default:
//        __LOG(LOG_SRC_APP, LOG_LEVEL_ERROR, "unsupported property id 0x%04x\n", property_id);
//        p_out_bytes = 0;
//        return;
//    }
//}

//static void app_sensor_settings_get_cb(const app_sensor_server_t *p_server,
//                                       uint16_t property_id,
//                                       sensor_settings_status_msg_pkt_t *p_out,
//                                       uint16_t *p_out_bytes)
//{
    /* Sensor Settings Get is not implemented */
//    *p_out_bytes = sizeof(property_id);
//    p_out->property_id = property_id;
//}

//static void app_sensor_setting_set_cb(const app_sensor_server_t *p_server,
//                                      uint16_t property_id,
//                                      uint16_t setting_property_id,
//                                      const sensor_setting_set_msg_pkt_t *p_in,
//                                      uint16_t in_len,
//                                      sensor_setting_status_msg_pkt_t *p_out,
//                                      uint16_t *p_out_bytes)
//{
    /* Sensor Setting Set is not implemented */
//    *p_out_bytes = sizeof(property_id) + sizeof(setting_property_id);
//    p_out->property_id = property_id;
//    p_out->setting_property_id = setting_property_id;
//}

//static void app_sensor_setting_get_cb(const app_sensor_server_t *p_server,
//                                      uint16_t property_id,
//                                      uint16_t setting_property_id,
//                                      sensor_setting_status_msg_pkt_t *p_out,
///                                      uint16_t *p_out_bytes)
//{
    /* Sensor Setting Set is not implemented */
//    *p_out_bytes = sizeof(property_id) + sizeof(setting_property_id);
//    p_out->property_id = property_id;
//    p_out->setting_property_id = setting_property_id;
//}

//static void app_sensor_column_get_cb(const app_sensor_server_t *p_server,
//                                     const sensor_column_get_msg_pkt_t *p_in,
//                                     uint16_t in_len,
//                                     sensor_column_status_msg_pkt_t *p_out,
//                                     uint16_t *p_out_bytes)
//{
    /* not implemented */
//    *p_out_bytes = sizeof(p_in->property_id);
//    p_out->property_id = p_in->property_id;
//}

//static void app_sensor_series_get_cb(const app_sensor_server_t *p_server,
//                                     const sensor_series_get_msg_pkt_t *p_in,
//                                     uint16_t in_len,
//                                     sensor_series_status_msg_pkt_t *p_out,
//                                     uint16_t *p_out_bytes)
//{
    /* not implemented */
//    *p_out_bytes = sizeof(p_in->property_id);
//    p_out->property_id = p_in->property_id;
//}


//static bool app_sensor_power_status_flag_cb(void)
//{
//    UNUSED_VARIABLE(sensor_status_publish(&m_sensor_server, SENSOR_PRESENT_DEVICE_INPUT_POWER_PROPERTY_ID));
//    return true;
//}

//static bool app_sensor_current_status_flag_cb(void)
//{
//    UNUSED_VARIABLE(sensor_status_publish(&m_sensor_server, SENSOR_PRESENT_INPUT_CURRENT_PROPERTY_ID));
//    return true;
//}

//static bool app_sensor_voltage_status_flag_cb(void)
//{
//    UNUSED_VARIABLE(sensor_status_publish(&m_sensor_server, SENSOR_PRESENT_INPUT_VOLTAGE_PROPERTY_ID));
//    return true;
//}

//static bool app_sensor_energy_use_status_flag_cb(void)
//{
//    UNUSED_VARIABLE(sensor_status_publish(&m_sensor_server, SENSOR_PRECISE_TOTAL_DEVICE_ENERGY_USE_PROPERTY_ID));
//    return true;
//}



/*** provisionoing section ***/

static void provisioning_output_request_cb(uint8_t action, uint8_t size, const uint8_t *p_data)
{
    if (action == NRF_MESH_PROV_OUTPUT_ACTION_BLINK) {
        memcpy(&m_pin_numeric, &p_data[PROV_AUTH_LEN - sizeof(m_pin_numeric)], sizeof(m_pin_numeric));
        m_pin_numeric = BE2LE32(m_pin_numeric);
//        set_device_state(APP_OUTPUT_REQUEST);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Provisioning output PIN: %d\n", m_pin_numeric);
    }
}


static void provisioning_complete_cb(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Successfully provisioned\n");

#if MESH_FEATURE_GATT_ENABLED
    /* Restores the application parameters after switching from the Provisioning
     * service to the Proxy  */
    gap_params_init();
    conn_params_init();
#endif

    unicast_address_print();

    /* restart TUYA module */
    tuya_module_start();
}


static void provisioning_abort_cb(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Provisioning abort\n");
//    set_device_state(APP_UNPROVISIONED);
}



/*** mesh basic section ***/

static void mesh_events_handle(const nrf_mesh_evt_t * p_evt)
{
    if (p_evt->type == NRF_MESH_EVT_ENABLED) {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- NRF_MESH_EVT_ENABLED  -----\n");

//        APP_ERROR_CHECK(app_sensor_cadence_restore(&m_sensor_server));

//        if (mesh_stack_is_device_provisioned()) {
//            set_device_state(APP_PROVISIONED);
//        } else {
//            set_device_state(APP_UNPROVISIONED);
//        }

        /* update Mesh LED */
//        update_device_state();

        /* start TUYA module */
        tuya_module_start();
    }
}


static void node_reset(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- NODE RESET ----- \n");

#if MESH_FEATURE_GATT_PROXY_ENABLED
    proxy_stop();
#endif
    mesh_stack_config_clear();

    /* this function may return if there are ongoing flash operations */
    model_config_file_clear();
    mesh_stack_device_reset();
}


static void config_server_evt_cb(const config_server_evt_t * p_evt)
{
    if (p_evt->type == CONFIG_SERVER_EVT_NODE_RESET) {
        node_reset();
    }
}


static void models_init_cb(void)
{
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Initializing and adding models\n");

    /* instantiate onoff server on element index APP_ONOFF_ELEMENT_INDEX */
//    ERROR_CHECK(app_onoff_init(&m_onoff_server, APP_ONOFF_ELEMENT_INDEX));
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "    App OnOff Model Handle: %d\n", m_onoff_server.server.model_handle);

//#if SCENE_SETUP_SERVER_INSTANCES_MAX > 0
    /* Instantiate Generic Default Transition Time server as needed by Scene models */
//    ERROR_CHECK(app_dtt_init(&m_dtt_server, APP_ONOFF_ELEMENT_INDEX));
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "    App DTT Model Handle: %d\n", m_dtt_server.server.model_handle);

    /* Instantiate scene server and register onoff server to have scene support */
//    ERROR_CHECK(app_scene_model_init(&m_scene_server, APP_ONOFF_ELEMENT_INDEX));
//    ERROR_CHECK(app_scene_model_add(&m_scene_server, &m_onoff_server.scene_if));
//    ERROR_CHECK(app_onoff_scene_context_set(&m_onoff_server, &m_scene_server));
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "    App Scene Model Handle: %d\n", m_scene_server.scene_setup_server.model_handle);
//#endif

//    ERROR_CHECK(app_sensor_init(&m_sensor_server, APP_SENSOR_ELEMENT_INDEX));
//    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "    App Sensor Model Handle: %d\n", m_sensor_server.server.model_handle);
}


static void mesh_init(void)
{
    /* initialize the application storage for models */
    model_config_file_init();

    mesh_stack_init_params_t init_params = {
        .core.irq_priority       = NRF_MESH_IRQ_PRIORITY_LOWEST,
        .core.lfclksrc           = DEV_BOARD_LF_CLK_CFG,
        .core.p_uuid             = NULL,
        .models.models_init_cb   = models_init_cb,
        .models.config_server_cb = config_server_evt_cb
    };

    uint32_t status = mesh_stack_init(&init_params, &m_device_provisioned);

    if (status == NRF_SUCCESS) {
        /* check if application stored data is valid, if not clear all data and use default values */
        status = model_config_file_config_apply();
    }

    switch (status) {
    case NRF_ERROR_INVALID_DATA:
        /* clear model config file as loading failed */
        model_config_file_clear();
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Data in the persistent memory was corrupted. Device starts as unprovisioned.\n");
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Reboot device before starting of the provisioning process.\n");
        mesh_stack_device_reset();
        break;
    case NRF_SUCCESS:
        break;
    default:
        ERROR_CHECK(status);
    }
}


/****** ... */
static void gpio_output_voltage_setup(void)
{
    // Configure UICR_REGOUT0 register only if it is set to default value.^M
    if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) ==
        (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos))
    {
        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

        NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~((uint32_t)UICR_REGOUT0_VOUT_Msk)) |
                            (UICR_REGOUT0_VOUT_3V0 << UICR_REGOUT0_VOUT_Pos);

        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

        // System reset is needed to update UICR registers.^M
        NVIC_SystemReset();
    }
}


static void initialize(void)
{
    gpio_output_voltage_setup();

    /* mark timers main structure as unused */
//    const app_timer_id_t m_timer0 __attribute__((unused)) = m_sensor_server_cadence_timer_0;
//    const app_timer_id_t m_timer1 __attribute__((unused)) = m_sensor_server_cadence_timer_1;
//    const app_timer_id_t m_timer2 __attribute__((unused)) = m_sensor_server_cadence_timer_2;
//    const app_timer_id_t m_timer3 __attribute__((unused)) = m_sensor_server_cadence_timer_3;
//    const app_timer_id_t m_min_interval_timer0 __attribute__((unused))
//        = m_sensor_server_min_interval_timer_0;
//    const app_timer_id_t m_min_interval_timer1 __attribute__((unused))
//        = m_sensor_server_min_interval_timer_1;
//    const app_timer_id_t m_min_interval_timer2 __attribute__((unused))
//        = m_sensor_server_min_interval_timer_2;
//    const app_timer_id_t m_min_interval_timer3 __attribute__((unused))
//        = m_sensor_server_min_interval_timer_3;

    __LOG_INIT(LOG_SRC_APP, LOG_LEVEL_DBG1, LOG_CALLBACK_DEFAULT);
    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- %s -----\n", GAP_DEVICE_NAME);

    ERROR_CHECK(app_timer_init());

//    m_on_off_status_flag = bearer_event_flag_add(app_onoff_status_flag_cb);
//    m_sensor_power_status_flag = bearer_event_flag_add(app_sensor_power_status_flag_cb);
//    m_sensor_current_status_flag = bearer_event_flag_add(app_sensor_current_status_flag_cb);
//    m_sensor_voltage_status_flag = bearer_event_flag_add(app_sensor_voltage_status_flag_cb);
//    m_sensor_energy_use_status_flag = bearer_event_flag_add(app_sensor_energy_use_status_flag_cb);

    tuya_module_init();

    ble_stack_init();

#if MESH_FEATURE_GATT_ENABLED
    gap_params_init();
    conn_params_init();
#endif

    mesh_init();
}


static void start(void)
{
    if (!m_device_provisioned) {
//        m_device_state = APP_UNPROVISIONED;

        mesh_provisionee_start_params_t prov_start_params = {
            .prov_sd_ble_opt_set_cb = NULL,
            .prov_complete_cb = provisioning_complete_cb,
            .prov_device_identification_start_cb = NULL,
            .prov_device_identification_stop_cb = NULL,
            .prov_abort_cb = provisioning_abort_cb,
            .prov_output_request_cb = provisioning_output_request_cb,
            .p_device_uri = EX_URI_DM_SERVER
        };
        ERROR_CHECK(mesh_provisionee_prov_start(&prov_start_params, 5));
    } else {
        unicast_address_print();
    }

    mesh_app_uuid_print(nrf_mesh_configure_device_uuid_get());

    /* NRF_MESH_EVT_ENABLED is triggered in the mesh IRQ context after the stack is fully enabled.
     * This event is used to call Model APIs for establishing bindings and publish a model state information. */
    nrf_mesh_evt_handler_add(&m_event_handler);
    ERROR_CHECK(mesh_stack_start());
}





/*** entry point ***/

int main(void)
{
    initialize();
    start();

    for (;;) {
        (void)sd_app_evt_wait();
    }
}
