/* main.c - Application main entry point */

/*
 * Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <string.h>

#include "esp_log.h"
#include "nvs_flash.h"
#include "access.h"
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_provisioning_api.h"
#include "esp_ble_mesh_networking_api.h"
#include "esp_ble_mesh_config_model_api.h"
#include "esp_ble_mesh_generic_model_api.h"

#include "esp_ble_mesh_health_model_api.h"
#include "ble_mesh_demo_init.h"

#define LED_OFF             0x0
#define LED_ON              0x1

#define CID_ESP             0x02E5
#define CID_NVAL            0xFFFF

#define PROV_OWN_ADDR       0x0001

#define MSG_SEND_TTL        3
#define MSG_SEND_REL        false
#define MSG_TIMEOUT         0
#define MSG_ROLE            ROLE_PROVISIONER

#define COMP_DATA_PAGE_0    0x00

#define APP_KEY_IDX         0x0000
#define APP_KEY_OCTET       0x12

static uint8_t dev_uuid[16];
//==========================================
uint32_t msg_tid=0;
//==========================================
typedef struct {
    uint8_t  uuid[16];
    uint16_t unicast;
    uint8_t  elem_num;
    uint8_t  onoff;
} esp_ble_mesh_node_info_t;

static esp_ble_mesh_node_info_t nodes[CONFIG_BLE_MESH_MAX_PROV_NODES] = {
    [0 ... (CONFIG_BLE_MESH_MAX_PROV_NODES - 1)] = {
        .unicast = ESP_BLE_MESH_ADDR_UNASSIGNED,
        .elem_num = 0,
        .onoff = LED_OFF,
    }
};

static struct esp_ble_mesh_key {
    uint16_t net_idx;
    uint16_t app_idx;
    uint8_t  app_key[16];
} prov_key;

static esp_ble_mesh_client_t config_client;
static esp_ble_mesh_client_t onoff_client;

//==========================================
static esp_ble_mesh_client_t health_client;
uint8_t bind_count =0;
uint16_t first_unicast = 0x0010;
//==========================================
static esp_ble_mesh_cfg_srv_t config_server = {
    .relay = ESP_BLE_MESH_RELAY_DISABLED,
    .beacon = ESP_BLE_MESH_BEACON_ENABLED,
#if defined(CONFIG_BLE_MESH_FRIEND)
    .friend_state = ESP_BLE_MESH_FRIEND_ENABLED,
#else
    .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BLE_MESH_GATT_PROXY_SERVER)
    .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED,
#else
    .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
    .default_ttl = 7,
    /* 3 transmissions with 20ms interval */
    .net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20),
    .relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20),
};

static esp_ble_mesh_model_t root_models[] = {
    ESP_BLE_MESH_MODEL_CFG_SRV(&config_server),
    ESP_BLE_MESH_MODEL_CFG_CLI(&config_client),
    ESP_BLE_MESH_MODEL_GEN_ONOFF_CLI(NULL, &onoff_client),
//==========================================
    ESP_BLE_MESH_MODEL_HEALTH_CLI(&health_client),
//==========================================
};

static esp_ble_mesh_elem_t elements[] = {
    ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE),
};

static esp_ble_mesh_comp_t composition = {
    .cid = CID_ESP,
    .elements = elements,
    .element_count = ARRAY_SIZE(elements),
};

static esp_ble_mesh_prov_t provision = {
    // .uuid           = dev_uuid,
    .prov_uuid           = dev_uuid,
    .prov_unicast_addr   = PROV_OWN_ADDR,
    .prov_start_address  = 0x0010,
    .prov_attention      = 0x00,
    .prov_algorithm      = 0x00,
    .prov_pub_key_oob    = 0x00,
    .prov_static_oob_val = NULL,
    .prov_static_oob_len = 0x00,
    .flags               = 0x00,
    .iv_index            = 0x00,
};
int bt_mesh_print_local_composition(void)
{
    const struct bt_mesh_comp *comp = NULL;
    struct bt_mesh_model *model = NULL;
    struct bt_mesh_elem *elem = NULL;
    int i, j, k;
    u16_t app_idx = 0;
    u16_t groups1 = 0xc000;
    u16_t groups2 = 0xcfff;

    comp = bt_mesh_comp_get();
    // comp = bt_mesh_conn_ref();

    BT_WARN("************************************************");
    BT_WARN("* cid: 0x%04x    pid: 0x%04x    vid: 0x%04x    *", comp->cid, comp->pid, comp->vid);
    BT_WARN("* Element Number: 0x%02x                         *", comp->elem_count);
    for (i = 0; i < comp->elem_count; i++) {
        elem = &comp->elem[i];
        BT_WARN("* Element %d: 0x%04x                            *", i, elem->addr);
        BT_WARN("*     Loc: 0x%04x   NumS: 0x%02x   NumV: 0x%02x    *", elem->loc, elem->model_count, elem->vnd_model_count);
        for (j = 0; j < elem->model_count; j++) {
            model = &elem->models[j];
            BT_WARN("*     sig_model %d: id - 0x%04x                 *", j, model->id);
            if (model->id == BLE_MESH_MODEL_ID_CFG_SRV ||
                    model->id == BLE_MESH_MODEL_ID_CFG_CLI) {
                continue;
            }
            for (k = 0; k < ARRAY_SIZE(model->keys); k++) {
                if (model->keys[k] == BLE_MESH_KEY_UNUSED) {
                    model->keys[k] = app_idx;
                } else {
                    app_idx = model->keys[k];
                }
                BT_WARN("*          key 0x%04x                          *", model->keys[k]);
            }
            model->groups[0] = groups1;
            model->groups[1] = groups2;
            for (k = 0; k < ARRAY_SIZE(model->groups); k++) {
                BT_WARN("*          group 0x%04x                        *", model->groups[k]);
            }
            // BT_WARN("*          publish addr 0x%04x                        *", model->pub->addr);
        }

        for (j = 0; j < elem->vnd_model_count; j++) {
            model = &elem->vnd_models[j];
            BT_WARN("*     vnd_model %d: id - 0x%04x, cid - 0x%04x   *", j, model->vnd.id, model->vnd.company);
            for (k = 0; k < ARRAY_SIZE(model->keys); k++) {
                if (model->keys[k] == BLE_MESH_KEY_UNUSED) {
                    model->keys[k] = app_idx;
                } else {
                    app_idx = model->keys[k];
                }
                BT_WARN("*          key 0x%04x                          *", model->keys[k]);
            }
            model->groups[0] = groups1;
            model->groups[1] = groups2;
            for (k = 0; k < ARRAY_SIZE(model->groups); k++) {
                BT_WARN("*          group 0x%04x                        *", model->groups[k]);
            }
            model->pub->addr = 0xF000;
            BT_WARN("*          publish addr 0x%04x                        *", model->pub->addr);
        }
    }
    BT_WARN("************************************************");

    ((void) model);

    return 0;
}
static esp_err_t example_ble_mesh_store_node_info(const uint8_t uuid[16], uint16_t unicast,
                                                  uint8_t elem_num, uint8_t onoff_state)
{
    int i;

    if (!uuid || !ESP_BLE_MESH_ADDR_IS_UNICAST(unicast)) {
        return ESP_ERR_INVALID_ARG;
    }

    /* Judge if the device has been provisioned before 判断设备之前是否已配置*/
    for (i = 0; i < ARRAY_SIZE(nodes); i++) {
        if (!memcmp(nodes[i].uuid, uuid, 16)) {
            ESP_LOGW(TAG, "%s: reprovisioned device 0x%04x", __func__, unicast);
            nodes[i].unicast = unicast;
            nodes[i].elem_num = elem_num;
            nodes[i].onoff = onoff_state;
            return ESP_OK;
        }
    }

    for (i = 0; i < ARRAY_SIZE(nodes); i++) {
        if (nodes[i].unicast == ESP_BLE_MESH_ADDR_UNASSIGNED) {
            memcpy(nodes[i].uuid, uuid, 16);
            nodes[i].unicast = unicast;
            nodes[i].elem_num = elem_num;
            nodes[i].onoff = onoff_state;
            return ESP_OK;
        }
    }

    return ESP_FAIL;
}

static esp_ble_mesh_node_info_t *example_ble_mesh_get_node_info(uint16_t unicast)
{
    int i;

    if (!ESP_BLE_MESH_ADDR_IS_UNICAST(unicast)) {
        return NULL;
    }

    for (i = 0; i < ARRAY_SIZE(nodes); i++) {
        if (nodes[i].unicast <= unicast &&
                nodes[i].unicast + nodes[i].elem_num > unicast) {
            return &nodes[i];
        }
    }

    return NULL;
}

static esp_err_t example_ble_mesh_set_msg_common(esp_ble_mesh_client_common_param_t *common,
                                                 uint16_t unicast,//esp_ble_mesh_node_info_t *node,
                                                 esp_ble_mesh_model_t *model, uint32_t opcode)
{
    if (!common || !unicast ) {
        return ESP_ERR_INVALID_ARG;
    }

    common->opcode = opcode;
    common->model = model;
    common->ctx.net_idx = prov_key.net_idx;
    common->ctx.app_idx = prov_key.app_idx;
    common->ctx.addr = unicast;
    // common->ctx.addr = node->unicast;
    common->ctx.send_ttl = MSG_SEND_TTL;
    common->ctx.send_rel = MSG_SEND_REL;
    common->msg_timeout = MSG_TIMEOUT;
    common->msg_role = MSG_ROLE;

    return ESP_OK;
}

void chang_led_state(uint16_t addr,uint8_t onoff)
{
    esp_ble_mesh_client_common_param_t common_my = {0};
    esp_ble_mesh_generic_client_set_state_t set_state_my = {0};
    example_ble_mesh_set_msg_common(&common_my,addr,onoff_client.model,ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
    // common_my.opcode = ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK;
    // common_my.model = onoff_client.model;
    // common_my.ctx.addr = addr;   /* to all nodes */
    // common_my.ctx.send_ttl = MSG_SEND_TTL;
    // common_my.ctx.send_rel = MSG_SEND_REL;
    // common_my.msg_timeout = 0;     
    // common_my.msg_role = ROLE_PROVISIONER;//ROLE_NODE;

    set_state_my.onoff_set.op_en = false;
    set_state_my.onoff_set.onoff = onoff;
    set_state_my.onoff_set.tid = msg_tid++;
    esp_ble_mesh_generic_client_set_state(&common_my, &set_state_my);
}
static esp_err_t prov_complete(int node_idx, const esp_ble_mesh_octet16_t uuid,
                               uint16_t unicast, uint8_t elem_num, uint16_t net_idx)
{
    esp_ble_mesh_client_common_param_t common = {0};
    esp_ble_mesh_cfg_client_get_state_t get_state = {0};
    esp_ble_mesh_node_info_t *node = NULL;
    char name[10];
    int err;

    ESP_LOGI(TAG, "node index: 0x%x, unicast address: 0x%02x, element num: %d, netkey index: 0x%02x",
             node_idx, unicast, elem_num, net_idx);
    ESP_LOGI(TAG, "device uuid: %s", bt_hex(uuid, 16));

    sprintf(name, "%s%d", "NODE-", node_idx);
    err = esp_ble_mesh_provisioner_set_node_name(node_idx, name);
    if (err) {
        ESP_LOGE(TAG, "%s: Set node name failed", __func__);
        return ESP_FAIL;
    }

    err = example_ble_mesh_store_node_info(uuid, unicast, elem_num, LED_OFF);
    if (err) {
        ESP_LOGE(TAG, "%s: Store node info failed", __func__);
        return ESP_FAIL;
    }

    node = example_ble_mesh_get_node_info(unicast);
    if (!node) {
        ESP_LOGE(TAG, "%s: Get node info failed", __func__);
        return ESP_FAIL;
    }

    example_ble_mesh_set_msg_common(&common, unicast, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
    get_state.comp_data_get.page = COMP_DATA_PAGE_0;
    err = esp_ble_mesh_config_client_get_state(&common, &get_state);
    if (err) {
        ESP_LOGE(TAG, "%s: Send config comp data get failed", __func__);
        return ESP_FAIL;
    }

    return ESP_OK;
}

static void prov_link_open(esp_ble_mesh_prov_bearer_t bearer)
{
    ESP_LOGI(TAG, "%s link open", bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
}

static void prov_link_close(esp_ble_mesh_prov_bearer_t bearer, uint8_t reason)
{
    ESP_LOGI(TAG, "%s link close, reason 0x%02x",
             bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", reason);
}

static void recv_unprov_adv_pkt(uint8_t dev_uuid[16], uint8_t addr[BD_ADDR_LEN],
                                esp_ble_mesh_addr_type_t addr_type, uint16_t oob_info,
                                uint8_t adv_type, esp_ble_mesh_prov_bearer_t bearer)
{
    esp_ble_mesh_unprov_dev_add_t add_dev = {0};
    int err;

    /* Due to the API esp_ble_mesh_provisioner_set_dev_uuid_match, Provisioner will only
     * use this callback to report the devices, whose device UUID starts with 0xdd & 0xdd,
     * to the application layer.
     */

    ESP_LOGI(TAG, "address: %s, address type: %d, adv type: %d", bt_hex(addr, BD_ADDR_LEN), addr_type, adv_type);
    ESP_LOGI(TAG, "device uuid: %s", bt_hex(dev_uuid, 16));
    ESP_LOGI(TAG, "oob info: %d, bearer: %s", oob_info, (bearer & ESP_BLE_MESH_PROV_ADV) ? "PB-ADV" : "PB-GATT");

    memcpy(add_dev.addr, addr, BD_ADDR_LEN);
    add_dev.addr_type = (uint8_t)addr_type;
    memcpy(add_dev.uuid, dev_uuid, 16);
    add_dev.oob_info = oob_info;
    add_dev.bearer = (uint8_t)bearer;
    /* Note: If unprovisioned device adv packets have not been received, we should not add
             device with ADD_DEV_START_PROV_NOW_FLAG set. */
    err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev,
            ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG);
    if (err) {
        ESP_LOGE(TAG, "%s: Add unprovisioned device into queue failed", __func__);
    }

    return;
}

static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event,
                                             esp_ble_mesh_prov_cb_param_t *param)
{
    switch (event) {
    case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT, err_code %d", param->provisioner_prov_enable_comp.err_code);
        break;
    case ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT, err_code %d", param->provisioner_prov_disable_comp.err_code);
        break;
    case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT");
        recv_unprov_adv_pkt(param->provisioner_recv_unprov_adv_pkt.dev_uuid, param->provisioner_recv_unprov_adv_pkt.addr,
                            param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info,
                            param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer);
        bind_count = 0;
        break;
    case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT:
        prov_link_open(param->provisioner_prov_link_open.bearer);
        break;
    case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT:
        prov_link_close(param->provisioner_prov_link_close.bearer, param->provisioner_prov_link_close.reason);
        break;
    case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT:
        prov_complete(param->provisioner_prov_complete.node_idx, param->provisioner_prov_complete.device_uuid,
                      param->provisioner_prov_complete.unicast_addr, param->provisioner_prov_complete.element_num,
                      param->provisioner_prov_complete.netkey_idx);
                      first_unicast = param->provisioner_prov_complete.unicast_addr;
        break;
    case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, err_code %d", param->provisioner_add_unprov_dev_comp.err_code);
        break;
    case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, err_code %d", param->provisioner_set_dev_uuid_match_comp.err_code);
        break;
    case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT: {
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code %d", param->provisioner_set_node_name_comp.err_code);
        if (param->provisioner_set_node_name_comp.err_code == ESP_OK) {
            const char *name = NULL;
            name = esp_ble_mesh_provisioner_get_node_name(param->provisioner_set_node_name_comp.node_index);
            if (!name) {
                ESP_LOGE(TAG, "Get node name failed");
                return;
            }
            ESP_LOGI(TAG, "Node %d name is: %s", param->provisioner_set_node_name_comp.node_index, name);
        }
        break;
    }
    case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT: {
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT, err_code %d", param->provisioner_add_app_key_comp.err_code);
        if (param->provisioner_add_app_key_comp.err_code == ESP_OK) {
            esp_err_t err = 0;
            prov_key.app_idx = param->provisioner_add_app_key_comp.app_idx;
            err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_key.app_idx,
                    ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI, CID_NVAL);
                    // printf(" ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_CLI \n");
            if (err != ESP_OK) {
                ESP_LOGE(TAG, "Provisioner bind local model appkey failed A");
                return;
            }
            // err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_key.app_idx,
            //         ESP_BLE_MESH_MODEL_ID_HEALTH_CLI, CID_NVAL);
            //         // printf(" ESP_BLE_MESH_MODEL_ID_HEALTH_CLI \n");
            // if (err != ESP_OK) {
            //     ESP_LOGE(TAG, "Provisioner bind local model appkey failed B");
            //     return;
            // }
        }
        break;
    }
    case ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT, err_code %d", param->provisioner_bind_app_key_to_model_comp.err_code);
        break;
    default:
        break;
    }

    return;
}

static void example_ble_mesh_config_client_cb(esp_ble_mesh_cfg_client_cb_event_t event,
                                              esp_ble_mesh_cfg_client_cb_param_t *param)
{
    esp_ble_mesh_client_common_param_t common = {0};
    esp_ble_mesh_node_info_t *node = NULL;
    uint32_t opcode;
    uint16_t addr;
    int err;

    opcode = param->params->opcode;
    addr = param->params->ctx.addr;

    ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x, opcode: 0x%04x",
             __func__, param->error_code, event, param->params->ctx.addr, opcode);

    if (param->error_code) {
        ESP_LOGE(TAG, "Send config client message failed, opcode 0x%04x", opcode);
        return;
    }

    node = example_ble_mesh_get_node_info(addr);
    if (!node) {
        ESP_LOGE(TAG, "%s: Get node info failed", __func__);
        return;
    }

    switch (event) {
    case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: {
            ESP_LOGI(TAG, "composition data %s", bt_hex(param->status_cb.comp_data_status.composition_data->data,
                     param->status_cb.comp_data_status.composition_data->len));
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};

                printf("ctx addr 0x%04x \n",common.ctx.addr);
                // if(!(&common))
                // {
                //     printf("common = 0 \n");
                // }
            err = example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
                printf("ctx addr 0x%04x err = 0x%x\n",common.ctx.addr,err);
            set_state.app_key_add.net_idx = prov_key.net_idx;
            set_state.app_key_add.app_idx = prov_key.app_idx;
            memcpy(set_state.app_key_add.app_key, prov_key.app_key, 16);
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config AppKey Add failed", __func__);
                return;
            }
            break;
        }
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: {
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
            set_state.model_app_bind.element_addr = node->unicast;
            set_state.model_app_bind.model_app_idx = prov_key.app_idx;
            set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV;
            set_state.model_app_bind.company_id = CID_NVAL;
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
            break;
        }
        case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND: {
            bind_count++;
            if(bind_count<2)
            {   
                // printf("ESP_BLE_MESH_MODEL_ID_HEALTH_SRV \n");
                esp_ble_mesh_cfg_client_set_state_t set_state = {0};
                example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
                set_state.model_app_bind.element_addr = node->unicast;
                set_state.model_app_bind.model_app_idx = prov_key.app_idx;
                set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_HEALTH_SRV;
                set_state.model_app_bind.company_id = CID_NVAL;
                err = esp_ble_mesh_config_client_set_state(&common, &set_state);
                if (err) {
                    ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                    return;
                }
            }
            else if (bind_count<3)
            {
                // printf("ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV \n");
                esp_ble_mesh_cfg_client_set_state_t set_state = {0};
                example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
                set_state.model_app_bind.element_addr = node->unicast+1;
                set_state.model_app_bind.model_app_idx = prov_key.app_idx;
                set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV;
                set_state.model_app_bind.company_id = CID_NVAL;
                err = esp_ble_mesh_config_client_set_state(&common, &set_state);
                if (err) {
                    ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                    return;
                }
            
            }
            
            else
            {
                // esp_ble_mesh_cfg_client_set_state_t set_state = {0};
                // example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET);
                // set_state.heartbeat_pub_set.dst = 50;
                // set_state.heartbeat_pub_set.ttl = MSG_SEND_TTL;
                // set_state.heartbeat_pub_set.period = 1;
                // // set_state.heartbeat_pub_set.feature = ;
                // set_state.heartbeat_pub_set.net_idx = prov_key.net_idx;
                // err = esp_ble_mesh_config_client_set_state(&common, &set_state);
                // if (err) {
                //     ESP_LOGE(TAG, "%s: Config Model eartbeat_pub_set failed", __func__);
                //     return;
                // }

                // esp_ble_mesh_cfg_client_set_state_t set_state = {0};
                // example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET);
                // set_state.model_pub_set.element_addr = addr+1;//node->unicast;
                // set_state.model_pub_set.publish_addr = addr+10;//node->unicast+1;
                // set_state.model_pub_set.publish_app_idx = prov_key.app_idx;
                // set_state.model_pub_set.cred_flag = true;
                // set_state.model_pub_set.publish_ttl = 7;
                // set_state.model_pub_set.publish_period = 1;
                // set_state.model_pub_set.publish_retransmit = 1;
                // set_state.model_pub_set.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV;
                // set_state.model_pub_set.company_id = CID_NVAL;
                // err = esp_ble_mesh_config_client_set_state(&common, &set_state);
                // if (err) {
                //     ESP_LOGE(TAG, "%s: config client Set failed", __func__);return;
                // }


                // esp_ble_mesh_health_client_set_state_t set_state = {0};
                // example_ble_mesh_set_msg_common(&common, addr, health_client.model, ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST);
                // set_state.fault_test.company_id = CID_NVAL;
                // set_state.fault_test.test_id = 0x01;
                // err = esp_ble_mesh_health_client_set_state(&common, &set_state);
                // if (err) {

                //     printf(" err : 0x%x\n",err);
                //     ESP_LOGE(TAG, "%s: Health client Get failed", __func__);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2020 05 09
                //     return;
                // }
                printf(" [%d]\n",__LINE__);
                esp_ble_mesh_health_client_get_state_t get_state = {0};
                printf(" addr 0x%04x \n",addr);
                err =  example_ble_mesh_set_msg_common(&common, addr, health_client.model, ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET );
               
                printf(" err : 0x%x\n",err);
                printf("ctx addr 0x%04x \n",common.ctx.addr);
                get_state.fault_get.company_id = CID_NVAL;
                // common.model = health_client.model;p
                // // printf("model id 0x%04x \n",common.model->model_id);
                // printf("ctx addr 0x%04x \n",common.ctx.addr);
                // printf("model id 0x%04x \n",common.model->model_id);
                // printf("ctx addr 0x%04x \n",common.ctx.addr);
                err = esp_ble_mesh_health_client_get_state(&common, &get_state);
                if (err) {

                    printf(" err : 0x%x\n",err);
                    ESP_LOGE(TAG, "%s: Health client Get failed", __func__);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2020 05 09
                    return;
                } 


                // esp_ble_mesh_generic_client_get_state_t get_state = {0};
                // printf(" addr 0x%04x \n",addr);
                // err = example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET);
                //  printf(" err : 0x%x\n",err);
                //  printf("ctx addr 0x%04x \n",common.ctx.addr);
                // printf("model id 0x%04x \n",common.model->model_id);
                // err = esp_ble_mesh_generic_client_get_state(&common, &get_state);
                // if (err) {
                //     ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__);
                //     return;
                // }
            }
            



            // esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            // example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET);
            // set_state.model_pub_set.element_addr = addr+1;//node->unicast;
            // set_state.model_pub_set.publish_addr = addr+10;//node->unicast+1;
            // set_state.model_pub_set.publish_app_idx = prov_key.app_idx+10;
            // set_state.model_pub_set.cred_flag = true;
            // set_state.model_pub_set.publish_ttl = 7;
            // set_state.model_pub_set.publish_period = 1;
            // set_state.model_pub_set.publish_retransmit = 1;
            // set_state.model_pub_set.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV;
            // set_state.model_pub_set.company_id = CID_NVAL;
            // err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            // if (err) {
            //     ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
            //     return;
            // }


            break;
        }

        case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET: {

            esp_ble_mesh_generic_client_get_state_t get_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model,ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET );
            err = esp_ble_mesh_generic_client_get_state(&common, &get_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__);
                return;
            }
            break;
        }
        case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET: {
            ESP_LOGI(TAG, "%s:ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET", __func__);
            esp_ble_mesh_generic_client_get_state_t get_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET);
            err = esp_ble_mesh_generic_client_get_state(&common, &get_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__);
                return;
            }
            break;
        }
        default:
            ESP_LOGE(TAG, "Not a config client status message event");
            break;
        }
        break;
    case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_STATUS:
            ESP_LOG_BUFFER_HEX("composition data %s", param->status_cb.comp_data_status.composition_data->data,
                               param->status_cb.comp_data_status.composition_data->len);
            break;
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_STATUS:
            break;
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: {
            esp_ble_mesh_cfg_client_get_state_t get_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
            get_state.comp_data_get.page = COMP_DATA_PAGE_0;
            err = esp_ble_mesh_config_client_get_state(&common, &get_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config Composition Data Get failed", __func__);
                return;
            }
            break;
        }
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD: {
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
            set_state.app_key_add.net_idx = prov_key.net_idx;
            set_state.app_key_add.app_idx = prov_key.app_idx;
            memcpy(set_state.app_key_add.app_key, prov_key.app_key, 16);
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Config AppKey Add failed", __func__);
                return;
            }
            break;
        }
        case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND: {
            esp_ble_mesh_cfg_client_set_state_t set_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
            set_state.model_app_bind.element_addr = node->unicast;
            set_state.model_app_bind.model_app_idx = prov_key.app_idx;
            set_state.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV;
            set_state.model_app_bind.company_id = CID_NVAL;
            err = esp_ble_mesh_config_client_set_state(&common, &set_state);

            if (err) {
                ESP_LOGE(TAG, "%s: Config Model App Bind failed", __func__);
                return;
            }
            break;
        }
        default:
            break;
        }
        break;
    default:
        ESP_LOGE(TAG, "Not a config client status message event");
        break;
    }
}

static void example_ble_mesh_health_client_cb(esp_ble_mesh_health_client_cb_event_t event,
                                               esp_ble_mesh_health_client_cb_param_t *param)
{
    esp_ble_mesh_client_common_param_t common = {0};
    esp_ble_mesh_node_info_t *node = NULL;
    uint32_t opcode;
    uint16_t addr;
    int err;

    opcode = param->params->opcode;
    addr = param->params->ctx.addr;

    ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x, opcode: 0x%04x",
             __func__, param->error_code, event, param->params->ctx.addr, opcode);

    if (param->error_code) {
        ESP_LOGE(TAG, "Send generic client message failed, opcode 0x%04x", opcode);
        return;
    }

    node = example_ble_mesh_get_node_info(addr);
    if (!node) {
        ESP_LOGE(TAG, "%s: Get node info failed", __func__);
        return;
    }
    switch (event) {
    case ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT:
        switch (opcode) {
            case ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_GET:{
                ESP_LOGE(TAG, "ESP_BLE_MESH_HEALTH_CLIENT_GET_STATE_EVT Generic OnOff Get failed");
                esp_ble_mesh_health_client_get_state_t get_state = {0};
                example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET);
                err = esp_ble_mesh_health_client_get_state(&common, &get_state);
                if (err) {
                    ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__);
                    return;
                }
                }
                break;                  
            case ESP_BLE_MESH_MODEL_OP_HEALTH_PERIOD_GET:
                break;                  
            case ESP_BLE_MESH_MODEL_OP_ATTENTION_GET:
                break;            
        }
        break;
    case ESP_BLE_MESH_HEALTH_CLIENT_SET_STATE_EVT:
        break;
    case ESP_BLE_MESH_HEALTH_CLIENT_PUBLISH_EVT:
        break;
    case ESP_BLE_MESH_HEALTH_CLIENT_TIMEOUT_EVT:
        break;
    case ESP_BLE_MESH_HEALTH_CLIENT_EVT_MAX:
        break;
    
    default:
        ESP_LOGE(TAG, "Not a health client status message event");
        break;
    }
}
static void example_ble_mesh_generic_client_cb(esp_ble_mesh_generic_client_cb_event_t event,
                                               esp_ble_mesh_generic_client_cb_param_t *param)
{
    esp_ble_mesh_client_common_param_t common = {0};
    esp_ble_mesh_node_info_t *node = NULL;
    uint32_t opcode;
    uint16_t addr;
    int err;

    opcode = param->params->opcode;
    addr = param->params->ctx.addr;

    ESP_LOGI(TAG, "%s, error_code = 0x%02x, event = 0x%02x, addr: 0x%04x, opcode: 0x%04x",
             __func__, param->error_code, event, param->params->ctx.addr, opcode);
    // if(param->error_code==0xfffffff0)
    // {
    //     printf(" node lost node unicast = 0x%04x!!! \n",addr);
    //     return;
    // }
    if (param->error_code) {
        ESP_LOGE(TAG, "Send generic client message failed, opcode 0x%04x", opcode);
        return;
    }

    node = example_ble_mesh_get_node_info(addr);
    if (!node) {
        ESP_LOGE(TAG, "%s: Get node info failed", __func__);
        // return;
    }

    switch (event) {
    case ESP_BLE_MESH_GENERIC_CLIENT_GET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: {

            // esp_ble_mesh_health_client_set_state_t set_state = {0};
            // example_ble_mesh_set_msg_common(&common, addr, health_client.model, ESP_BLE_MESH_MODEL_OP_HEALTH_FAULT_TEST);
            // set_state.fault_test.company_id = CID_NVAL;
            // set_state.fault_test.test_id = 0x01;
            // err = esp_ble_mesh_health_client_set_state(&common, &set_state);
            // printf(" err : 0x%x\n",err);
            // if (err) {
            //     ESP_LOGE(TAG, "%s: Health client Get failed", __func__);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2020 05 09
            //     return;
            // }
            
            esp_ble_mesh_generic_client_set_state_t set_state = {0};
            node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET onoff: 0x%02x", node->onoff);
            /* After Generic OnOff Status for Generic OnOff Get is received, Generic OnOff Set will be sent */
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET);
            set_state.onoff_set.op_en = false;
            set_state.onoff_set.onoff = !node->onoff;
            // set_state.onoff_set.onoff = LED_OFF;
            set_state.onoff_set.tid = msg_tid++;
            err = esp_ble_mesh_generic_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__);
                return;
            }
            break;
        }
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_GENERIC_CLIENT_SET_STATE_EVT:
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
            // node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET onoff: 0x%02x", param->status_cb.onoff_status.present_onoff);
            
            esp_ble_mesh_generic_client_set_state_t set_state = {0};
            node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET onoff: 0x%02x", node->onoff);
            /* After Generic OnOff Status for Generic OnOff Get is received, Generic OnOff Set will be sent */
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
            set_state.onoff_set.op_en = false;
            set_state.onoff_set.onoff = !node->onoff;
            // set_state.onoff_set.onoff = LED_OFF;
            set_state.onoff_set.tid = msg_tid++;
            err = esp_ble_mesh_generic_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__);
                return;
            }
            break;
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_GENERIC_CLIENT_PUBLISH_EVT:
        break;
    case ESP_BLE_MESH_GENERIC_CLIENT_TIMEOUT_EVT:
        /* If failed to receive the responses, these messages will be resend */
        switch (opcode) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET: {
            esp_ble_mesh_generic_client_get_state_t get_state = {0};
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET);
            err = esp_ble_mesh_generic_client_get_state(&common, &get_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Get failed", __func__);
                return;
            }
            break;
        }
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET: {
            esp_ble_mesh_generic_client_set_state_t set_state = {0};
            // node->onoff = param->status_cb.onoff_status.present_onoff;
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET onoff: 0x%02x", node->onoff);
            example_ble_mesh_set_msg_common(&common, addr, onoff_client.model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET);
            set_state.onoff_set.op_en = false;
            // set_state.onoff_set.onoff = LED_OFF;
            set_state.onoff_set.onoff = !node->onoff;
            set_state.onoff_set.tid = msg_tid++;
            err = esp_ble_mesh_generic_client_set_state(&common, &set_state);
            if (err) {
                ESP_LOGE(TAG, "%s: Generic OnOff Set failed", __func__);
                return;
            }
            break;
        }
        default:
            break;
        }
        break;
    default:
        ESP_LOGE(TAG, "Not a generic client status message event");
        break;
    }
}

static esp_err_t ble_mesh_init(void)
{
    uint8_t match[2] = {0xdd, 0xdd};
    esp_err_t err;

    prov_key.net_idx = ESP_BLE_MESH_KEY_PRIMARY;
    prov_key.app_idx = APP_KEY_IDX;
    memset(prov_key.app_key, APP_KEY_OCTET, sizeof(prov_key.app_key));

    esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb);
    esp_ble_mesh_register_config_client_callback(example_ble_mesh_config_client_cb);
    esp_ble_mesh_register_generic_client_callback(example_ble_mesh_generic_client_cb);
    esp_ble_mesh_register_health_client_callback(example_ble_mesh_health_client_cb);


    err = esp_ble_mesh_init(&provision, &composition);
    if (err) {
        ESP_LOGE(TAG, "Initializing mesh failed (err %d)", err);
        return err;
    }

    esp_ble_mesh_provisioner_set_dev_uuid_match(match, sizeof(match), 0x0, false);

    esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);

    esp_ble_mesh_provisioner_add_local_app_key(prov_key.app_key, prov_key.net_idx, prov_key.app_idx);

    ESP_LOGI(TAG, "BLE Mesh Provisioner initialized");

    return err;
}


extern void gatt_server_init();

void app_main(void)
{
    esp_err_t err;

    ESP_LOGI(TAG, "Initializing...");

    err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);

    err = bluetooth_init();
    if (err) {
        ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err);
        return;
    }

    ble_mesh_get_dev_uuid(dev_uuid);

    /* Initialize the Bluetooth Mesh Subsystem */
    err = ble_mesh_init();
    if (err) {
        ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err);
    }

    gatt_server_init();
    bt_mesh_print_local_composition();
}
