/**
 * 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_simple_sensor main.c
 * @{
 * @ingroup zigbee_examples
 * @brief Zigbee Pressure and Temperature sensor
 */

#include "zboss_api.h"
#include "zb_mem_config_med.h"
#include "zb_error_handler.h"
#include "zigbee_helpers.h"
#include "app_timer.h"
#include "bsp.h"
#include "boards.h"
#include "sensorsim.h"
#include "nrf_802154.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include "zb_binary_sensor.h"

#include "zboss_api_zcl.h"

#if defined (DEBUG) 
  #warning ****** DEBUG Compiling!******
#endif

#define MAX_CHILDREN                       10  
#define IEEE_CHANNEL_MASK                  (1l << ZIGBEE_CHANNEL)               /**< Scan only one, predefined channel to find the coordinator. */
#define ERASE_PERSISTENT_CONFIG            ZB_FALSE                             /**< Do not erase NVRAM to save the network parameters after device reboot or power-off. */

#define ZIGBEE_NETWORK_STATE_LED           BSP_BOARD_LED_2                      /**< LED indicating that light switch successfully joind Zigbee network. */

#define SLEEPY_DEVICE_BUTTON               /* _BUTTON, _ENABLE, _DISABLE */
#define SLEEPY_ON_BUTTON                   BSP_BOARD_BUTTON_3
#define SLEEPY_ON_LED                      BSP_BOARD_LED_3

// Only End Device Only, No routing support! 
#if !defined ZB_ED_ROLE
  //#error Define ZB_ED_ROLE to compile End Device source code.
  #warning Compiling for End Device and Router!  
#else
  #define MY_ZED_ONLY
  #warning ****** Compiling for End Device ONLY! ****** 
#endif

static sensor_device_ctx_t m_dev_ctx;

ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &m_dev_ctx.identify_attr.identify_time);

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);


ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST(binary_attr_list,
                                        &m_dev_ctx.binary_attr.out_of_service_value,
                                        &m_dev_ctx.binary_attr.present_value,   
                                        &m_dev_ctx.binary_attr.status_flag);                       

ZB_DECLARE_SIMPLE_SENSOR_CLUSTER_LIST(simple_sensor_clusters,
                                     basic_attr_list,
                                     identify_attr_list,
                                     binary_attr_list );

ZB_ZCL_DECLARE_SIMPLE_SENSOR_EP(simple_sensor_ep,
                               SIMPLE_SENSOR_ENDPOINT,
                               simple_sensor_clusters);

ZBOSS_DECLARE_DEVICE_CTX_1_EP(simple_sensor_ctx, simple_sensor_ep);


APP_TIMER_DEF(zb_app_timer);


/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static void timers_init(void)
{
    ret_code_t err_code;

    // Initialize timer module.
    err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}

/**@brief Function for initializing the nrf log module.
 */
static void log_init(void)
{
    //ret_code_t err_code = NRF_LOG_INIT(NULL);
    ret_code_t err_code = NRF_LOG_INIT( app_timer_cnt_get ); 
    //ret_code_t err_code = NRF_LOG_INIT( rtc1_counter_get ); // app_timer_cnt_get
    APP_ERROR_CHECK(err_code);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
}

/**@brief Function for initializing all clusters attributes.
 */
static void simple_sensor_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   = SENSOR_INIT_BASIC_APP_VERSION;
    m_dev_ctx.basic_attr.stack_version = SENSOR_INIT_BASIC_STACK_VERSION;
    m_dev_ctx.basic_attr.hw_version    = SENSOR_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,
                          SENSOR_INIT_BASIC_MANUF_NAME,
                          ZB_ZCL_STRING_CONST_SIZE(SENSOR_INIT_BASIC_MANUF_NAME));

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.model_id,
                          SENSOR_INIT_BASIC_MODEL_ID,
                          ZB_ZCL_STRING_CONST_SIZE(SENSOR_INIT_BASIC_MODEL_ID));

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.date_code,
                          SENSOR_INIT_BASIC_DATE_CODE,
                          ZB_ZCL_STRING_CONST_SIZE(SENSOR_INIT_BASIC_DATE_CODE));

    m_dev_ctx.basic_attr.power_source = SENSOR_INIT_BASIC_POWER_SOURCE;

    ZB_ZCL_SET_STRING_VAL(m_dev_ctx.basic_attr.location_id,
                          SENSOR_INIT_BASIC_LOCATION_DESC,
                          ZB_ZCL_STRING_CONST_SIZE(SENSOR_INIT_BASIC_LOCATION_DESC));


    m_dev_ctx.basic_attr.ph_env = SENSOR_INIT_BASIC_PH_ENV;

    /* Identify cluster attributes data */
    m_dev_ctx.identify_attr.identify_time        = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;

    /* Binary Input cluster atributes data */
    m_dev_ctx.binary_attr.out_of_service_value   =  0 ; // False
    m_dev_ctx.binary_attr.present_value          =  0 ; // 0
    m_dev_ctx.binary_attr.status_flag            =  ZB_ZCL_BINARY_INPUT_STATUS_FLAG_DEFAULT_VALUE ; // 0-Normal 
}

/**@brief Function for Buttons.
 */
void my_button_event_handler(bsp_event_t bsp_evt)
{
    zb_zcl_status_t zcl_status;
    uint8_t button_pressed = bsp_evt - BSP_EVENT_KEY_0;
    NRF_LOG_INFO("button %d pressed", button_pressed);
    
    static zb_int8_t  new_binary_value = 0;
    
    if      (button_pressed == 0)
    {
        new_binary_value = 0;
    }
    else if (button_pressed == 1)
    {
        new_binary_value = 1;
    }
    else if (button_pressed == 2)
    {
        new_binary_value = 0 ;
    }
    else if (button_pressed == 3)
    {
        new_binary_value = 1 ;
    }
    
    zcl_status = zb_zcl_set_attr_val(SIMPLE_SENSOR_ENDPOINT,
                                     ZB_ZCL_CLUSTER_ID_BINARY_INPUT, 
                                     ZB_ZCL_CLUSTER_SERVER_ROLE, 
                                     ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, 
                                     (zb_uint8_t *)&new_binary_value, 
                                     ZB_FALSE);
    if(zcl_status != ZB_ZCL_STATUS_SUCCESS)
    {
        NRF_LOG_INFO("Set binary input value fail. zcl_status: %d", zcl_status);
    }

}

/**@brief Function for initializing LEDs.
 */
static zb_void_t leds_init(void)
{
    ret_code_t error_code;

    /* Initialize LEDs and buttons - use BSP to control them. */
    // MBP
    //error_code = bsp_init(BSP_INIT_LEDS, NULL);
    error_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, my_button_event_handler);
    APP_ERROR_CHECK(error_code);

    bsp_board_leds_off();
}

/**@brief Function to set the Sleeping Mode according to the SLEEPY_ON_BUTTON state.
*/
static zb_void_t sleepy_device_setup(void)
{
    zb_set_rx_on_when_idle(bsp_button_is_pressed(SLEEPY_ON_BUTTON) ? ZB_FALSE : ZB_TRUE);

#if ! defined DISABLE_POWER_CONSUMPTION_OPTIMIZATION
    /* If sleepy behaviour is enabled, power off unused RAM to save maximum energy */
    if (ZB_PIBCACHE_RX_ON_WHEN_IDLE() == ZB_FALSE)
    {
        zigbee_power_down_unused_ram();
    }
#endif /* ! defined DISABLE_POWER_CONSUMPTION_OPTIMIZATION */
}


/**@brief Function for handling nrf app timer.
 * 
 * @param[IN]   context   Void pointer to context function is called with.
 * 
 * @details Function is called with pointer to sensor_device_ep_ctx_t as argument.
 */
static void zb_app_timer_handler(void * context)
{
    zb_zcl_status_t zcl_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)
{
    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);
    zb_ret_t                   status      = ZB_GET_APP_SIGNAL_STATUS(bufid);

    /* Update network status LED */
    zigbee_led_status_update(bufid, ZIGBEE_NETWORK_STATE_LED);

    switch (sig)
    {
        case ZB_BDB_SIGNAL_DEVICE_REBOOT:
            /* fall-through */
        case ZB_BDB_SIGNAL_STEERING:
            /* Call default signal handler. */
            ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
            if (status == RET_OK)
            {
                ret_code_t err_code = app_timer_start(zb_app_timer, APP_TIMER_TICKS(1000), NULL);
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            /* Call default signal handler. */
            ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
            break;
    }

    if (bufid)
    {
        zb_buf_free(bufid);
    }
}

#if !(defined ZB_ZCL_DISABLE_REPORTING)

void configure_reporting_locally(void)
{
  zb_zcl_reporting_info_t rep_info;
  memset(&rep_info, 0, sizeof(rep_info));

  rep_info.direction      = ZB_ZCL_CONFIGURE_REPORTING_SEND_REPORT;
  rep_info.ep             = SIMPLE_SENSOR_ENDPOINT;
  rep_info.cluster_id     = ZB_ZCL_CLUSTER_ID_BINARY_INPUT;
  rep_info.cluster_role   = ZB_ZCL_CLUSTER_SERVER_ROLE;
  rep_info.attr_id        = ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID;
  rep_info.dst.profile_id = ZB_AF_HA_PROFILE_ID;

  rep_info.u.send_info.min_interval = 0;   //
  rep_info.u.send_info.max_interval = 0;   //
  rep_info.u.send_info.delta.u8     = 1;   //

  /* Cordinators discription
     src_addr=0x0000 ep=64 profile_id=0x0104 
     app_dev_id=0x5 app_dev_ver=0x0 
     in_clusters=0x0000, 0x0003 out_clusters=0x0000, 0x0003, 0x0004 .
   */

  rep_info.dst.short_addr = 0x0000 ; 
  rep_info.dst.endpoint   = 64 ;
  rep_info.dst.profile_id = ZB_AF_HA_PROFILE_ID ;

  // ******linker error******* 
  //zb_zcl_put_default_reporting_info(&rep_info);
  zb_zcl_put_reporting_info(&rep_info,ZB_TRUE);

} 

#endif

// CLI: 

/**@brief Function for application main entry.
 */
int main(void)
{
    zb_ret_t       zb_err_code;
    ret_code_t     err_code;
    zb_ieee_addr_t ieee_addr;

    /* Initialize loging system and GPIOs. */
    timers_init();
    log_init();
    leds_init();

    /* Create Timer for reporting attribute */
    err_code = app_timer_create(&zb_app_timer, APP_TIMER_MODE_REPEATED, zb_app_timer_handler);
    APP_ERROR_CHECK(err_code);

    /* 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("simple_binary_sensor");

    /* 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. */
#ifdef MY_ZED_ONLY     
    zb_set_network_ed_role(IEEE_CHANNEL_MASK);
    zb_set_ed_timeout(ED_AGING_TIMEOUT_64MIN);      
    zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000)); 
#ifdef SLEEPY_DEVICE_BUTTON      
    sleepy_device_setup();
    #warning ****** Configured for SLEEPY_DEVICE_BUTTON ! ******
#else
    #ifdef SLEEPY_DEVICE_ENABLE
      zb_set_rx_on_when_idle(ZB_FALSE);  // sleppy enabled
      NRF_LOG_INFO("Zigbee Sleepy Device !");
    #else
      zb_set_rx_on_when_idle(ZB_TRUE);  // sleepy disabled (default) 
      NRF_LOG_INFO("Zigbee NonSleepy Device !");
    #endif
#endif
#else //MY_ZED_ROUTER    
    zb_set_network_router_role(IEEE_CHANNEL_MASK);//MBP   
    zb_set_max_children(MAX_CHILDREN);            //MBP
    zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(3000));
#endif
    zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);

     /* Bind to cordinator for reporting */
    configure_reporting_locally();

    zb_int8_t mypower = 0 ;
    zb_bool_t mysleepy = false;

     if(zb_get_rx_on_when_idle() == ZB_FALSE ) //return ZB_TRUE if ZED is not sleepy ZED, ZB_FALSE if ZED is a sleepy ZED
    {  
      bsp_board_led_on(SLEEPY_ON_LED); // ZB_FALSE if ZED is a sleepy ZED
      mysleepy = ZB_TRUE ;
    }
    else 
    {
      bsp_board_led_off(SLEEPY_ON_LED); //  ZB_TRUE if ZED is not sleepy ZED
      mysleepy = ZB_FALSE ; 
    }
    
    /* Initialize application context structure. */
    UNUSED_RETURN_VALUE(ZB_MEMSET(&m_dev_ctx, 0, sizeof(m_dev_ctx)));

    /* Register callback for handling ZCL commands. */
    //MBP: TODO ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);

    /* Register temperature sensor device context (endpoints). */
    ZB_AF_REGISTER_DEVICE_CTX(&simple_sensor_ctx);

    /* Initialize sensor device attibutes */
    simple_sensor_clusters_attr_init();
   
    /** Start Zigbee Stack. */
    zb_err_code = zboss_start_no_autostart();
    ZB_ERROR_CHECK(zb_err_code);

    zb_int32_t i =0;
    zb_nwk_device_type_t role = zb_get_network_role();
    NRF_LOG_INFO("Zigbee role = %d ", role);
    while(1)
    {
        zboss_main_loop_iteration();
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        i--;
        if(i<=0)
        {
          mypower=nrf_802154_tx_power_get();
          NRF_LOG_INFO("Zigbee Txpower %d !", mypower);
          i=10000*4;
          /*
          if(mypower >0 ) // suppose 8
          {
            mypower = 4;
            nrf_802154_tx_power_set(mypower);
          }
          */
        } 
    }
}

//
/**
 * @}
 */
