/**
 * Copyright (c) 2018 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 *
 * @defgroup zigbee_examples_light_bulb main.c
 * @{
 * @ingroup zigbee_examples
 * @brief Dimmable light sample (HA profile)
 */

#include "sdk_config.h"
#include "zboss_api.h"
#include "zboss_api_addons.h"
#include "zb_mem_config_med.h"
#include "zb_error_handler.h"
#include "zb_nrf52_internal.h"
#include "zigbee_helpers.h"

#include "bsp.h"
#include "boards.h"
#include "app_pwm.h"
#include "app_timer.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define MAX_CHILDREN                      10                                    /**< The maximum amount of connected devices. Setting this value to 0 disables association to this device.  */
#define IEEE_CHANNEL_MASK                 (1l << ZIGBEE_CHANNEL)                /**< Scan only one, predefined channel to find the coordinator. */
#define ERASE_PERSISTENT_CONFIG           ZB_TRUE                              /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. */

/* Basic cluster attributes initial values. */
#define BULB_INIT_BASIC_APP_VERSION       01                                    /**< Version of the application software (1 byte). */
#define BULB_INIT_BASIC_STACK_VERSION     10                                    /**< Version of the implementation of the Zigbee stack (1 byte). */
#define BULB_INIT_BASIC_HW_VERSION        11                                    /**< Version of the hardware of the device (1 byte). */
#define BULB_INIT_BASIC_MANUF_NAME        "Nordic"                              /**< Manufacturer name (32 bytes). */
#define BULB_INIT_BASIC_MODEL_ID          "Tunnel server"                  /**< Model number assigned by manufacturer (32-bytes long string). */
#define BULB_INIT_BASIC_DATE_CODE         "20180416"                            /**< First 8 bytes specify the date of manufacturer of the device in ISO 8601 format (YYYYMMDD). The rest (8 bytes) are manufacturer specific. */
#define BULB_INIT_BASIC_POWER_SOURCE      ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE   /**< Type of power sources available for the device. For possible values see section 3.2.2.2.8 of ZCL specification. */
#define BULB_INIT_BASIC_LOCATION_DESC     "Office desk"                         /**< Describes the physical location of the device (16 bytes). May be modified during commisioning process. */
#define BULB_INIT_BASIC_PH_ENV            ZB_ZCL_BASIC_ENV_UNSPECIFIED          /**< Describes the type of physical environment. For possible values see section 3.2.2.2.10 of ZCL specification. */

#define IDENTIFY_MODE_BSP_EVT             BSP_EVENT_KEY_0                       /**< Button event used to enter the Bulb into the Identify mode. */
#define ZIGBEE_NETWORK_STATE_LED          BSP_BOARD_LED_0                       /**< LED indicating that light switch successfully joind Zigbee network. */
#define BULB_LED                          LED_RGB_RED                       /**< LED immitaing dimmable light bulb. */

#if !defined ZB_ROUTER_ROLE
#error Define ZB_ROUTER_ROLE to compile light bulb (Router) source code.
#endif

/* Main application customizable context. Stores all settings and static values. */
typedef struct
{
    zb_zcl_basic_attrs_ext_t         basic_attr;
    zb_zcl_tunneling_attrs_t         tunneling_attr;
} bulb_device_ctx_t;

static bulb_device_ctx_t m_dev_ctx;

ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(basic_attr_list,
                                     &m_dev_ctx.basic_attr.zcl_version,
                                     &m_dev_ctx.basic_attr.app_version,
                                     &m_dev_ctx.basic_attr.stack_version,
                                     &m_dev_ctx.basic_attr.hw_version,
                                     m_dev_ctx.basic_attr.mf_name,
                                     m_dev_ctx.basic_attr.model_id,
                                     m_dev_ctx.basic_attr.date_code,
                                     &m_dev_ctx.basic_attr.power_source,
                                     m_dev_ctx.basic_attr.location_id,
                                     &m_dev_ctx.basic_attr.ph_env,
                                     m_dev_ctx.basic_attr.sw_ver);

#define TUNNELING_ENDPOINT_ID 11
#define TUNNELING_PROFILE_ID ZB_AF_HA_PROFILE_ID
#define TUNNELING_DEVICE_ID 0x508 // Remote Communications Device
#define TUNNELING_PROTOCOL_ID 200
#define TUNNELING_MANUF_CODE 3910
ZB_ZCL_DECLARE_TUNNELING_ATTRIB_LIST(tunneling_attr_list, &m_dev_ctx.tunneling_attr.close_tunnel_timeout);
zb_zcl_cluster_desc_t tunneling_clusters[]={
        ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_BASIC,
                ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t),
                basic_attr_list,
                ZB_ZCL_CLUSTER_SERVER_ROLE,
                ZB_ZCL_MANUF_CODE_INVALID),
        ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_TUNNELING,
                ZB_ZCL_ARRAY_SIZE(tunneling_attr_list, zb_zcl_attr_t),
                tunneling_attr_list,
                ZB_ZCL_CLUSTER_SERVER_ROLE,
                TUNNELING_MANUF_CODE)
};
zb_af_simple_desc_1_1_t simple_desc_tunnel={
        TUNNELING_ENDPOINT_ID,
        TUNNELING_PROFILE_ID,
        TUNNELING_DEVICE_ID,
        1, 0,
        2, 0,
        { ZB_ZCL_CLUSTER_ID_BASIC, ZB_ZCL_CLUSTER_ID_TUNNELING }
};
ZB_AF_DECLARE_ENDPOINT_DESC(tunneling_ep, TUNNELING_ENDPOINT_ID,
        TUNNELING_PROFILE_ID, 0, NULL,
        ZB_ZCL_ARRAY_SIZE(tunneling_clusters, zb_zcl_cluster_desc_t), tunneling_clusters,
        &simple_desc_tunnel,
        0, NULL, 0, NULL);
ZBOSS_DECLARE_DEVICE_CTX_1_EP(dimmable_light_ctx, tunneling_ep);

/**@brief Function for initializing the application timer.
 */
static void timer_init(void)
{
    uint32_t error_code = app_timer_init();
    APP_ERROR_CHECK(error_code);
}

/**@brief Function for initializing the nrf log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}

/**@brief Callback for button events.
 *
 * @param[in]   evt      Incoming event from the BSP subsystem.
 */
static void buttons_handler(bsp_event_t evt)
{
    switch(evt)
    {
        case IDENTIFY_MODE_BSP_EVT:
            break;

        default:
            NRF_LOG_INFO("Unhandled BSP Event received: %d", evt);
            break;
    }
}


/**@brief Function for initializing LEDs and a single PWM channel.
 */
static void leds_buttons_init(void)
{
    ret_code_t       err_code;

    /* Initialize all LEDs and buttons. */
    err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, buttons_handler);
    APP_ERROR_CHECK(err_code);
    /* By default the bsp_init attaches BSP_KEY_EVENTS_{0-4} to the PUSH events of the corresponding buttons. */
    nrf_gpio_cfg_input(BSP_SW1, NRF_GPIO_PIN_PULLUP);
}

/**@brief Function for initializing all clusters attributes.
 */
static void bulb_clusters_attr_init(void)
{
    /* Basic cluster attributes data */
    m_dev_ctx.basic_attr.zcl_version   = ZB_ZCL_VERSION;
    m_dev_ctx.basic_attr.app_version   = BULB_INIT_BASIC_APP_VERSION;
    m_dev_ctx.basic_attr.stack_version = BULB_INIT_BASIC_STACK_VERSION;
    m_dev_ctx.basic_attr.hw_version    = BULB_INIT_BASIC_HW_VERSION;

    /* Use ZB_ZCL_SET_STRING_VAL to set strings, because the first byte should
     * contain string length without trailing zero.
     *
     * For example "test" string wil be encoded as:
     *   [(0x4), 't', 'e', 's', 't']
     */
    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.mf_name,
                          BULB_INIT_BASIC_MANUF_NAME,
                          ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MANUF_NAME));

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.model_id,
                          BULB_INIT_BASIC_MODEL_ID,
                          ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MODEL_ID));

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.date_code,
                          BULB_INIT_BASIC_DATE_CODE,
                          ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_DATE_CODE));

    m_dev_ctx.basic_attr.power_source = BULB_INIT_BASIC_POWER_SOURCE;

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.location_id,
                          BULB_INIT_BASIC_LOCATION_DESC,
                          ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_LOCATION_DESC));


    m_dev_ctx.basic_attr.ph_env = BULB_INIT_BASIC_PH_ENV;

    m_dev_ctx.tunneling_attr.close_tunnel_timeout=60;//ZB_ZCL_TUNNELING_CLOSE_TUNNEL_TIMEOUT_DEFAULT_VALUE;
}

/**@brief Function which tries to sleep down the MCU 
 *
 * Function which sleeps the MCU on the non-sleepy End Devices to optimize the power saving.
 * The weak definition inside the OSIF layer provides some minimal working template
 */
zb_void_t zb_osif_go_idle(zb_void_t)
{
    //TODO: implement your own logic if needed
    zb_osif_wait_for_event();
}

/**@brief Callback function for handling ZCL commands.
 *
 * @param[in]   bufid   Reference to Zigbee stack buffer used to pass received data.
 */
static zb_void_t zcl_device_cb(zb_bufid_t bufid)
{
    zb_zcl_device_callback_param_t * p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);

    NRF_LOG_INFO("zcl_device_cb id %hd", p_device_cb_param->device_cb_id);

    /* Set default response value. */
    p_device_cb_param->status = RET_OK;

    switch (p_device_cb_param->device_cb_id)
    {
        default:
            p_device_cb_param->status = RET_ERROR;
            break;
    }

    NRF_LOG_INFO("zcl_device_cb status: %hd", p_device_cb_param->status);
}

/**@brief Zigbee stack event handler.
 *
 * @param[in]   bufid   Reference to the Zigbee stack buffer used to pass signal.
 */
void zboss_signal_handler(zb_bufid_t bufid)
{
    /* Update network status LED */
    zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);

    zb_zdo_app_signal_hdr_t      * p_sg_p = NULL;
    zb_zdo_app_signal_type_t       sig    = zb_get_app_signal(bufid, &p_sg_p);

    NRF_LOG_INFO("zboss_signal_handler: %i", sig);

    switch(sig)
    {
        default:
		/* No application-specific behavior is required. Call default signal handler. */
		ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
    }

    if (bufid)
    {
        zb_buf_free(bufid);
    }
}

static zb_zcl_parsed_hdr_t tunnel_header;
static zb_uint16_t max_incoming_transfer_size;
static const zb_uint16_t tunnel_id=72;

static void request_tunnel_response(zb_bufid_t bufid)
{
	NRF_LOG_INFO("Tunneling request response");
    ZB_ZCL_TUNNELING_SEND_REQUEST_TUNNEL_RESPONSE(bufid, tunnel_header.addr_data.common_data.source.u,
    		tunnel_header.addr_data.common_data.source.addr_type == ZB_ZCL_ADDR_TYPE_SHORT ? ZB_APS_ADDR_MODE_16_ENDP_PRESENT : ZB_APS_ADDR_MODE_64_ENDP_PRESENT,
					TUNNELING_ENDPOINT_ID, TUNNELING_ENDPOINT_ID, ZB_AF_HA_PROFILE_ID,
					ZB_ZCL_DISABLE_DEFAULT_RESPONSE, ZB_ZCL_GET_SEQ_NUM(),
					NULL, tunnel_id,
					ZB_ZCL_TUNNELING_STATUS_SUCCESS, max_incoming_transfer_size );
}

static void tunnel_send_transfer_data_cb(zb_bufid_t bufid)
{
    NRF_LOG_INFO("tunnel_send_transfer_data_cb: %d", bufid);
    zb_buf_free(bufid);
}

static void tunnel_send_transfer_data(zb_bufid_t bufid)
{
    zb_uint8_t *data=(zb_uint8_t*)"server data";
    NRF_LOG_INFO("Transfer data: %d/%d", tunnel_id, strlen((char*)data));
    zb_ret_t zb_err_code=ZB_ZCL_TUNNELING_SERVER_SEND_TRANSFER_DATA(bufid, TUNNELING_ENDPOINT_ID, TUNNELING_PROFILE_ID,
            ZB_ZCL_DISABLE_DEFAULT_RESPONSE, tunnel_send_transfer_data_cb,
            tunnel_id, strlen((char*)data), data);
    ZB_ERROR_CHECK(zb_err_code);
}

static zb_uint8_t zb_tunnel_endpoint_handler(zb_bufid_t bufid)
{
	zb_zcl_parsed_hdr_t * header = ZB_BUF_GET_PARAM(bufid, zb_zcl_parsed_hdr_t);
    NRF_LOG_INFO("zb_tunnel_endpoint_handler: %i", header->cmd_id);

	switch(header->cmd_id)
	{
		case ZB_ZCL_TUNNELING_CLI_CMD_REQUEST_TUNNEL:
		{
			zb_zcl_tunneling_request_tunnel_t tun;
			zb_zcl_parse_status_t status;

			ZB_ZCL_TUNNELING_GET_REQUEST_TUNNEL(&tun, bufid, status);
			NRF_LOG_INFO("Tunneling request: %d/%d/%d", tun.protocol_id, tun.manufacturer_code, status);
			if (status==ZB_ZCL_PARSE_STATUS_SUCCESS &&
				tun.protocol_id==TUNNELING_PROTOCOL_ID &&
				tun.manufacturer_code==TUNNELING_MANUF_CODE)
			{
				zb_bufid_t buf_resp = zb_buf_get_out();
				tunnel_header=*header;
				max_incoming_transfer_size=tun.max_incoming_transfer_size;
				zb_ret_t zb_err_code=ZB_SCHEDULE_APP_ALARM(request_tunnel_response, buf_resp, ZB_TIME_ONE_SECOND);
                ZB_ERROR_CHECK(zb_err_code);

                zb_bufid_t buf_resp2 = zb_buf_get_out();
                zb_err_code=ZB_SCHEDULE_APP_ALARM(tunnel_send_transfer_data, buf_resp2, 5*ZB_TIME_ONE_SECOND);
                ZB_ERROR_CHECK(zb_err_code);
			}
            zb_buf_free(bufid);
		    return ZB_TRUE;
		}
		case ZB_ZCL_TUNNELING_CLI_CMD_TRANSFER_DATA:
		{
			zb_zcl_tunneling_transfer_data_payload_t data;
			zb_zcl_parse_status_t status;

			ZB_ZCL_TUNNELING_GET_TRANSFER_DATA(&data, bufid, status);
			NRF_LOG_INFO("Transfer data: %d/%d", data.data_size, status);
			if (status==ZB_ZCL_PARSE_STATUS_SUCCESS)
			{
				NRF_LOG_INFO("%s", data.tun_data);
			}
            zb_buf_free(bufid);
		    return ZB_TRUE;
		}
		case ZB_ZCL_TUNNELING_CLI_CMD_CLOSE_TUNNEL:
        {
            zb_zcl_tunneling_close_tunnel_t tun;
            zb_zcl_parse_status_t status;

            ZB_ZCL_TUNNELING_GET_CLOSE_TUNNEL(&tun, bufid, status);
            NRF_LOG_INFO("Tunnel close: %d/%d", tun.tunnel_id, status);
            zb_buf_free(bufid);
            return ZB_TRUE;
        }
        case ZB_ZCL_TUNNELING_SRV_CMD_TUNNEL_CLOSURE_NOTIFICATION:
        {
            NRF_LOG_INFO("Tunnel closed/timeout");
            break;
        }
	}

    return ZB_FALSE;
}

static void init_setunnel()
{
    ZB_ZCL_CLUSTER_ID_TUNNELING_SERVER_ROLE_INIT();
    ZB_AF_SET_ENDPOINT_HANDLER(TUNNELING_ENDPOINT_ID, zb_tunnel_endpoint_handler);

    NRF_LOG_INFO("zb_tunnel_endpoint_handler reg=%i", ZB_AF_IS_EP_REGISTERED(TUNNELING_ENDPOINT_ID));
}

/**@brief Function for application main entry.
 */
int main(void)
{
    zb_ret_t       zb_err_code;
    zb_ieee_addr_t ieee_addr;

    /* Initialize timer, logging system and GPIOs. */
    timer_init();
    log_init();
    leds_buttons_init();

    /* Set Zigbee stack logging level and traffic dump subsystem. */
    ZB_SET_TRACE_LEVEL(ZIGBEE_TRACE_LEVEL);
    ZB_SET_TRACE_MASK(ZIGBEE_TRACE_MASK);
    ZB_SET_TRAF_DUMP_OFF();

    /* Initialize Zigbee stack. */
    ZB_INIT("led_bulb");

    /* Set device address to the value read from FICR registers. */
    zb_osif_get_ieee_eui64(ieee_addr);
    zb_set_long_address(ieee_addr);

    /* Set static long IEEE address. */
    zb_set_network_coordinator_role(IEEE_CHANNEL_MASK);
    //zb_set_network_router_role(IEEE_CHANNEL_MASK);
    zb_set_max_children(MAX_CHILDREN);
    zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);
    zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));

    /* Initialize application context structure. */
    UNUSED_RETURN_VALUE(ZB_MEMSET(&m_dev_ctx, 0, sizeof(m_dev_ctx)));

    /* Register callback for handling ZCL commands. */
    ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);

    /* Register dimmer switch device context (endpoints). */
    ZB_AF_REGISTER_DEVICE_CTX(&dimmable_light_ctx);

    bulb_clusters_attr_init();

    init_setunnel();

    /** Start Zigbee Stack. */
    zb_err_code = zboss_start_no_autostart();
    ZB_ERROR_CHECK(zb_err_code);

    while(1)
    {
        zboss_main_loop_iteration();
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
    }
}


/**
 * @}
 */
