/**
 * Copyright (c) 2018 - 2019, 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)
 */

/* Includes ------------------------------------------------------------------*/
#include "sdk_config.h"
#include "zboss_api.h"
#include "zb_mem_config_med.h"
#include "zb_ha_dimmable_light.h"
#include "zb_error_handler.h"
#include "zb_nrf52840_internal.h"
#include "zigbee_helpers.h"

//flash include files 
#include "nrf_fstorage.h"
#include "nrf_fstorage_nvmc.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"
#include "nrf_drv_gpiote.h"
#include "drv_ws2812.h"
#include "nrf_delay.h"

#include "hardfault.h"
/* MACROS ---------------------------------------------------------*/

#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 IEEE_CHANNEL_MASK                  (ZB_TRANSCEIVER_ALL_CHANNELS_MASK)
#define HA_DIMMABLE_LIGHT_ENDPOINT        10                                    /**< Device endpoint, used to receive light controlling commands. */
#define ERASE_PERSISTENT_CONFIG           ZB_FALSE                              /**< 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          "Dimable_Light_v0.1"                  /**< 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_ENTER_BUTTON        BSP_BOARD_BUTTON_0                    /**< Enter the Bulb into the Identify mode. */
#ifdef  BOARD_PCA10059                                                          /**< If it is Dongle */
#define ZIGBEE_NETWORK_STATE_LED          BSP_BOARD_LED_0                       /**< LED indicating that light switch successfully joind ZigBee network. */
#else
#define ZIGBEE_NETWORK_STATE_LED          BSP_BOARD_LED_2                       /**< LED indicating that light switch successfully joind ZigBee network. */
#endif

#define ZCD_IN_PIN                            NRF_GPIO_PIN_MAP(0,6)             /**< ZCD pin configuration, this is the input pin to read the interrupt*/

#ifndef ZCD_IN_PIN 
    #error "Please indicate input pin"
#endif

#define DIMMER_TRIGGER_OUT_PIN                NRF_GPIO_PIN_MAP(0,8)            /**< Dimmer trigger pin configuration, this is the output pin to drive the LED/BULB*/

#ifndef DIMMER_TRIGGER_OUT_PIN
    #error "Please indicate output pin"
#endif

//Flash related MACROS 
#define FLASH_START_ADDRESS           0x3F000
#define FLASH_END_ADDRESS             0x3FFFF


/* Declare endpoint for Dimmable Light device with scenes. */
#define ZB_HA_DECLARE_LIGHT_EP(ep_name, ep_id, cluster_list)                         \
  ZB_ZCL_DECLARE_HA_DIMMABLE_LIGHT_SIMPLE_DESC(ep_name, ep_id,                       \
    ZB_HA_DIMMABLE_LIGHT_IN_CLUSTER_NUM, ZB_HA_DIMMABLE_LIGHT_OUT_CLUSTER_NUM);      \
  ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info## device_ctx_name,               \
                                     ZB_HA_DIMMABLE_LIGHT_REPORT_ATTR_COUNT);        \
  ZBOSS_DEVICE_DECLARE_LEVEL_CONTROL_CTX(cvc_alarm_info## device_ctx_name,           \
                                         ZB_HA_DIMMABLE_LIGHT_CVC_ATTR_COUNT);       \
  ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID,                   \
                              0,     \
                              NULL,                 \
                              ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t),\
                              cluster_list,                                          \
                              (zb_af_simple_desc_1_1_t*)&simple_desc_##ep_name,      \
                              ZB_HA_DIMMABLE_LIGHT_REPORT_ATTR_COUNT,                \
                              reporting_info## device_ctx_name,                      \
                              ZB_HA_DIMMABLE_LIGHT_CVC_ATTR_COUNT,                   \
                              cvc_alarm_info## device_ctx_name)

#if !defined ZB_ROUTER_ROLE
#error Define ZB_ROUTER_ROLE to compile light bulb (Router) source code.
#endif


/* Private variables ---------------------------------------------------------*/

uint32_t time_us = 0;
uint32_t time_ticks;
uint32_t ui32DimmerONTime = 0;
uint32_t ui32LastBrightness = 0;				// Value of last brightness between 0 to 64
uint32_t ui32ZCDCounts = 0;
bool boolDimmerTrigger = false;
bool boolZCDTrigger = false;

/* These variables are used for read, write operation in flash.
   Because the fstorage interface is asynchrounous, the data must be kept in memory.
 */
uint32_t ui8ResetReadCount = 0;
uint32_t ui8ResetWriteCount = 0;
ret_code_t     rc;

/* External variables ---------------------------------------------------------*/


/* Private function prototypes -----------------------------------------------*/

const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(4);          //timer 4 instance creation and declaration 
const nrf_drv_timer_t TIMER0_LED = NRF_DRV_TIMER_INSTANCE(1);         //timer 0 instance creation and declaration 
const nrf_drv_timer_t TIMER2_RESET = NRF_DRV_TIMER_INSTANCE(2);       //timer 2 instance creation and declaration 

/* Functions definitions -----------------------------------------------------*/
void timer4creates_start (void);                       //timer 4 creation function 
void timer0creates_start (void);                       //timer 0 creation function 
void timer2creates_start (void);                       //timer 2 creation function


/* Basic cluster attributes. */
typedef struct
{
    zb_uint8_t zcl_version;
    zb_uint8_t app_version;
    zb_uint8_t stack_version;
    zb_uint8_t hw_version;
    zb_char_t  mf_name[32];
    zb_char_t  model_id[32];
    zb_char_t  date_code[16];
    zb_uint8_t power_source;
    zb_char_t  location_id[17];
    zb_uint8_t ph_env;
    zb_char_t  sw_ver[17];
} bulb_device_basic_attr_t;

/* Identify cluster attributes. */
typedef struct
{
    zb_uint16_t identify_time;
} bulb_device_identify_attr_t;

/* ON/Off cluster attributes. */
typedef struct
{
    zb_bool_t   on_off;
    zb_bool_t   global_scene_ctrl;
    zb_uint16_t on_time;
    zb_uint16_t off_wait_time;
} bulb_device_on_off_attr_t;

/* Level Control cluster attributes. */
typedef struct
{
    zb_uint8_t  current_level;
    zb_uint16_t remaining_time;
} bulb_device_level_control_attr_t;

/* Scenes cluster attributes. */
typedef struct
{
    zb_uint8_t  scene_count;
    zb_uint8_t  current_scene;
    zb_uint8_t  scene_valid;
    zb_uint8_t  name_support;
    zb_uint16_t current_group;
} bulb_device_scenes_attr_t;

/* Groups cluster attributes. */
typedef struct
{
    zb_uint8_t name_support;
} bulb_device_groups_attr_t;

/* Main application customizable context. Stores all settings and static values. */
typedef struct
{
    bulb_device_basic_attr_t         basic_attr;
    bulb_device_identify_attr_t      identify_attr;
    bulb_device_scenes_attr_t        scenes_attr;
    bulb_device_groups_attr_t        groups_attr;
    bulb_device_on_off_attr_t        on_off_attr;
    bulb_device_level_control_attr_t level_control_attr;
} bulb_device_ctx_t;


static bulb_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_GROUPS_ATTRIB_LIST(groups_attr_list, &m_dev_ctx.groups_attr.name_support);

ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST(scenes_attr_list,
                                  &m_dev_ctx.scenes_attr.scene_count,
                                  &m_dev_ctx.scenes_attr.current_scene,
                                  &m_dev_ctx.scenes_attr.current_group,
                                  &m_dev_ctx.scenes_attr.scene_valid,
                                  &m_dev_ctx.scenes_attr.name_support);

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);

/* On/Off cluster attributes additions data */
ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST_EXT(on_off_attr_list,
                                      &m_dev_ctx.on_off_attr.on_off,
                                      &m_dev_ctx.on_off_attr.global_scene_ctrl,
                                      &m_dev_ctx.on_off_attr.on_time,
                                      &m_dev_ctx.on_off_attr.off_wait_time);

ZB_ZCL_DECLARE_LEVEL_CONTROL_ATTRIB_LIST(level_control_attr_list,
                                         &m_dev_ctx.level_control_attr.current_level,
                                         &m_dev_ctx.level_control_attr.remaining_time);

ZB_HA_DECLARE_DIMMABLE_LIGHT_CLUSTER_LIST(dimmable_light_clusters,
                                          basic_attr_list,
                                          identify_attr_list,
                                          groups_attr_list,
                                          scenes_attr_list,
                                          on_off_attr_list,
                                          level_control_attr_list);

ZB_HA_DECLARE_LIGHT_EP(dimmable_light_ep,
                       HA_DIMMABLE_LIGHT_ENDPOINT,
                       dimmable_light_clusters);

ZB_HA_DECLARE_DIMMABLE_LIGHT_CTX(dimmable_light_ctx,
                                 dimmable_light_ep);


//Flash related callback function initialization
static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt);

//structure defines the callback function, flash start and end address 
NRF_FSTORAGE_DEF(nrf_fstorage_t fstorage) =
{
    /* Set a handler for fstorage events. */
    .evt_handler = fstorage_evt_handler,

    /* These below are the boundaries of the flash space assigned to this instance of fstorage.
     * You must set these manually, even at runtime, before nrf_fstorage_init() is called.
     * The function nrf5_flash_end_addr_get() can be used to retrieve the last address on the
     * last page of flash available to write data. */
    .start_addr = FLASH_START_ADDRESS,
    .end_addr   = FLASH_END_ADDRESS,
};
//fstorage event handler/callback function 
static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
{
    if (p_evt->result != NRF_SUCCESS)
    {
        NRF_LOG_INFO("--> Event received: ERROR while executing an fstorage operation.");
        return;
    }

    switch (p_evt->id)
    {
        case NRF_FSTORAGE_EVT_WRITE_RESULT:
        {
            NRF_LOG_INFO("--> Event received: wrote %d bytes at address 0x%x.",
                         p_evt->len, p_evt->addr);
        } break;

        case NRF_FSTORAGE_EVT_ERASE_RESULT:
        {
            NRF_LOG_INFO("--> Event received: erased %d page from address 0x%x.",
                         p_evt->len, p_evt->addr);
        } break;

        default:
            break;
    }
}

/**@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 Sets brightness of on-board LED
 *
 * @param[in] brightness_level Brightness level, allowed values 0 ... 255, 0 - turn off, 255 - full brightness
 */
static void light_bulb_onboard_set_brightness(zb_uint8_t brightness_level)
{
     /* Scale level value: timer uses 0-100 scale, but ZigBee level control cluster uses values from 0 up to 255. */
      ui32DimmerONTime = ((brightness_level * 100)/ 255);
      //set brightness level to 100% Max  
      ui32LastBrightness = ui32DimmerONTime * 100;
}


/**@brief Sets brightness of bulb luminous executive element
 *
 * @param[in] brightness_level Brightness level, allowed values 0 ... 255, 0 - turn off, 255 - full brightness
 */
static void light_bulb_set_brightness(zb_uint8_t brightness_level)
{
    light_bulb_onboard_set_brightness(brightness_level);
}

/**@brief Function for setting the light bulb brightness.
  *
  * @param[in]   new_level   Light bulb brightness value.
 */
static void level_control_set_value(zb_uint16_t new_level)
{
    NRF_LOG_INFO("Set level value: %i", new_level);

    ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT,                                       
                         ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,            
                         ZB_ZCL_CLUSTER_SERVER_ROLE,                 
                         ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, 
                         (zb_uint8_t *)&new_level,                                       
                         ZB_FALSE);                                  

    /* According to the table 7.3 of Home Automation Profile Specification v 1.2 rev 29, chapter 7.1.3. */
    if (new_level == 0)
    {
        zb_uint8_t value = ZB_FALSE;
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                             ZB_ZCL_CLUSTER_ID_ON_OFF,    
                             ZB_ZCL_CLUSTER_SERVER_ROLE,  
                             ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                             &value,                        
                             ZB_FALSE);                   
    }
    else
    {
        zb_uint8_t value = ZB_TRUE;
        ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                             ZB_ZCL_CLUSTER_ID_ON_OFF,    
                             ZB_ZCL_CLUSTER_SERVER_ROLE,  
                             ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                             &value,                        
                             ZB_FALSE);
    }

    light_bulb_set_brightness(new_level);
}


/**@brief Function for turning ON/OFF the light bulb.
 *
 * @param[in]   on   Boolean light bulb state.
 */
static void on_off_set_value(zb_bool_t on)
{
    NRF_LOG_INFO("Set ON/OFF value: %i", on);

    ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                         ZB_ZCL_CLUSTER_ID_ON_OFF,    
                         ZB_ZCL_CLUSTER_SERVER_ROLE,  
                         ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                         (zb_uint8_t *)&on,                        
                         ZB_FALSE);

    if (on)
    {
        level_control_set_value(m_dev_ctx.level_control_attr.current_level);
    }
    else
    {
        light_bulb_set_brightness(0U);
      
    }
}



/**@brief Function for initializing LEDs and a single PWM channel.
 */
static void leds_buttons_init(void)
{

      bsp_board_init(BSP_INIT_LEDS);    // Initialization the board leds 
 
}

/**
* @brief Handler for ZCD events.
* 
* ZCD_handler function to read the input pin P0_6 interrupt at every 10ms
* Incorporated the dimming effect dependance upon the time values  
* Start timer 4 and timer 0   
*/
void ZCD_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{ 
    if(!boolZCDTrigger)
    {
         //read the ZCD pulse if the count is 1 then goto next operation step
        if(++ui32ZCDCounts ==1)
        {
              //start TIMER 0 
              timer0creates_start();
    
               //increment the dimmer brightness in steps 0.3% to soft start and stop 
              if(time_us < ui32LastBrightness)
              {
                  time_us += 30;	 	
                  if(time_us >= 9960)
                  {
                      time_us = 10000;
                  }                                   
              }
              //decrease the dimmer brightness in steps 0.3% to soft start and stop 
              if(time_us > ui32LastBrightness)
              {
                  time_us -= 30;	 	
                  if(time_us <= 20)
                  { 
                      time_us = 0;
                  }
          
              }
              //if the time 10ms and birghtness level is 100% then turn ON the LED/BULB 
              if(ui32LastBrightness == 10000 && time_us == 10000)
              {
                nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 1);   // turn ON the LED/BULB
              }
              //if the time is 0ms and brightness level is 0% then turn OFF the LED/BULB
              else if(ui32LastBrightness == 0 && time_us == 0)
              {
                nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 0);   // turn OFF the LED/BULB 
              }
              else
              {
                nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 1);
                boolDimmerTrigger = true;
                timer4creates_start();   // start the timer4 
              }

              ui32ZCDCounts = 0;

        }
    }
              
}
/**
* @brief Handler for timer events.
* 
* timer_led_event_handler function to turn OFF the bulb and disable the timer 
*   
*/
void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
{
    if(boolDimmerTrigger)
    {
      // turn Off the dimer
      nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 0);
 
      boolDimmerTrigger = false;
    }
    nrf_drv_timer_disable(&TIMER_LED);   //disable the timer 

}
  
/**
* @brief function for timer4 initialization.
* 
* timer4creates_start function to configure timer 4 with the frequency parameter set 1MHz
* Intialise the timer event handler and start the timer in us to control the dimming effect 
*   
*/
void timer4creates_start (void)
{
   
    uint32_t err_code = NRF_SUCCESS;

    //Configure TIMER_LED for generating simple light effect
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = 4;
   // timer_cfg.bit_width = 3;
    err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);
    if (err_code != NRF_SUCCESS)
    {
        // handle error condition
    }

    time_ticks = nrf_drv_timer_us_to_ticks(&TIMER_LED, time_us);

    nrf_drv_timer_extended_compare(
         &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    nrf_drv_timer_enable(&TIMER_LED);

}

/**
* @brief Handler for timer events.
* 
* timer0_led_event_handler function to control the brightness of LED and drive the lower MOSFET  
*   
*/
void timer0_led_event_handler(nrf_timer_event_t event_type, void* p_context)
{

        nrf_drv_timer_disable(&TIMER0_LED);    // disable timer 0 
      
         //increment the dimmer brightness in steps 0.5% to soft start and stop 
        if(time_us < ui32LastBrightness)
        {
            time_us += 30;	 	
            if(time_us >= 9960)
            {
                time_us = 10000;
            }                                   
        }
        //decrease the dimmer brightness in steps 0.5% to soft start and stop 
        if(time_us > ui32LastBrightness)
        {
            time_us -= 30;	 	
            if(time_us <= 20)
            { 
                time_us = 0;
            }
            
        }
       //if the time 10ms and birghtness level is 100% then turn ON the LED/BULB 
        if(ui32LastBrightness == 10000 && time_us == 10000)
        {
          nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 1);   // turn ON the LED/BULB
        }
        //if the time is 0ms and brightness level is 0% then turn OFF the LED/BULB
        else if(ui32LastBrightness == 0 && time_us == 0)
        {
          nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 0);   // turn OFF the LED/BULB 
        }
        else
        {
          nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN, 1);
          boolDimmerTrigger = true;
          timer4creates_start();   // start the timer4 
        }
}

/**
* @brief function for timer0 initialization.
* 
* timer0creates_start function to configure timer 0 with the frequency parameter set 1MHz
* Intialise the timer event handler and start the timer in us to control the dimming effect 
*   
*/
void timer0creates_start (void)
{
    uint32_t time0_us = 10000;
    uint32_t time0_ticks;
    uint32_t err_code = NRF_SUCCESS;

   //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = 4;
   // timer_cfg.bit_width = 0;
    err_code = nrf_drv_timer_init(&TIMER0_LED, &timer_cfg, timer0_led_event_handler);
    if (err_code != NRF_SUCCESS)
    {
        // handle error condition
    }

    time0_ticks = nrf_drv_timer_us_to_ticks(&TIMER0_LED, time0_us);

    nrf_drv_timer_extended_compare(
         &TIMER0_LED, NRF_TIMER_CC_CHANNEL0, time0_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    nrf_drv_timer_enable(&TIMER0_LED);

}

/**
* @brief function for timer 2 interrupt handler timer2_reset_handler
* 
*Every 5sec the handler will execute and here erase the flash reset read and write count value  
* and disable the timer
*/ 
void timer2_reset_handler(nrf_timer_event_t event_type, void* p_context)
{
      
      ui8ResetWriteCount = 0;
      ui8ResetReadCount = 0;
      rc = nrf_fstorage_erase(&fstorage,FLASH_START_ADDRESS,1, NULL);
      APP_ERROR_CHECK(rc);
      boolZCDTrigger = false;
      nrf_drv_timer_disable(&TIMER2_RESET);   //disable the timer 

}

/**
* @brief function for timer 2 initialization.
* 
* timer2creates_start function to configure timer 2 for 5sec 
* 
*/
void timer2creates_start (void)
{
    uint32_t time2_ms = 5000;
    uint32_t time2_ticks;
    uint32_t err_code = NRF_SUCCESS;

   //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
    nrf_drv_timer_config_t timer_cfg;
    timer_cfg.frequency = 4;     //1MHz frequecy
    timer_cfg.bit_width = 3;     //32 bit width
    timer_cfg.mode = 0;

    err_code = nrf_drv_timer_init(&TIMER2_RESET, &timer_cfg, timer2_reset_handler);
    if (err_code != NRF_SUCCESS)
    {
        // handle error condition
    }

    time2_ticks = nrf_drv_timer_ms_to_ticks(&TIMER2_RESET, time2_ms);

    nrf_drv_timer_extended_compare(
         &TIMER2_RESET, NRF_TIMER_CC_CHANNEL0, time2_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    nrf_drv_timer_enable(&TIMER2_RESET);
}

/**
 * @brief Function for configuring: ZCD_IN_PIN pin for input,
 * and configures GPIOTE to give an interrupt on pin change.
 */
static void gpio_init(void)
{
    uint32_t err_code;
	
    if(!nrf_drv_gpiote_is_init())
    {
      err_code = nrf_drv_gpiote_init();
    }

    //Configure the Dimmer output pin trigger pin 
    nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_HIGH;
    //GPIOTE_CONFIG_OUT_SIMPLE(false);
    
    err_code = nrf_drv_gpiote_out_init(DIMMER_TRIGGER_OUT_PIN, &out_config);
    if (err_code != NRF_SUCCESS)
    {
        // handle error condition
    }

    //Configure the ZCD input pin 
    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true); //GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
    in_config.pull = NRF_GPIO_PIN_PULLUP; //NRF_GPIO_PIN_PULLUP;
    err_code = nrf_drv_gpiote_in_init(ZCD_IN_PIN, &in_config, ZCD_handler);
    if (err_code != NRF_SUCCESS)
    {
        // handle error condition
    }

    //Enable the interrupt the pin event handler
    nrf_drv_gpiote_in_event_enable(ZCD_IN_PIN, true);
}

/**@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;

    /* Identify cluster attributes data */
    m_dev_ctx.identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;

    /* On/Off cluster attributes data */
    m_dev_ctx.on_off_attr.on_off = (zb_bool_t)ZB_ZCL_ON_OFF_IS_ON;

    m_dev_ctx.level_control_attr.current_level  = ZB_ZCL_LEVEL_CONTROL_LEVEL_MAX_VALUE;
    m_dev_ctx.level_control_attr.remaining_time = ZB_ZCL_LEVEL_CONTROL_REMAINING_TIME_DEFAULT_VALUE;

    ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT, 
                         ZB_ZCL_CLUSTER_ID_ON_OFF,    
                         ZB_ZCL_CLUSTER_SERVER_ROLE,  
                         ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
                         (zb_uint8_t *)&m_dev_ctx.on_off_attr.on_off,                        
                         ZB_FALSE);                   

    ZB_ZCL_SET_ATTRIBUTE(HA_DIMMABLE_LIGHT_ENDPOINT,                                       
                         ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,            
                         ZB_ZCL_CLUSTER_SERVER_ROLE,                 
                         ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, 
                         (zb_uint8_t *)&m_dev_ctx.level_control_attr.current_level,                                       
                         ZB_FALSE);                                  
}

/**@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]   param   Reference to ZigBee stack buffer used to pass received data.
 */
static zb_void_t zcl_device_cb(zb_uint8_t param)
{
    zb_uint8_t                       cluster_id;
    zb_uint8_t                       attr_id;
    zb_buf_t                       * p_buffer = ZB_BUF_FROM_REF(param);
    zb_zcl_device_callback_param_t * p_device_cb_param =
                     ZB_GET_BUF_PARAM(p_buffer, 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)
    {
        case ZB_ZCL_LEVEL_CONTROL_SET_VALUE_CB_ID:
            NRF_LOG_INFO("Level control setting to %d", p_device_cb_param->cb_param.level_control_set_value_param.new_value);
            level_control_set_value(p_device_cb_param->cb_param.level_control_set_value_param.new_value);
            break;

        case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
            cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
            attr_id    = p_device_cb_param->cb_param.set_attr_value_param.attr_id;

            if (cluster_id == ZB_ZCL_CLUSTER_ID_ON_OFF)
            {
                uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;

                NRF_LOG_INFO("on/off attribute setting to %hd", value);
                if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
                {
                    on_off_set_value((zb_bool_t) value);
                }
            }
            else if (cluster_id == ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL)
            {
                uint16_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data16;

                NRF_LOG_INFO("level control attribute setting to %hd", value);
                if (attr_id == ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID)
                {
                    level_control_set_value(value);
                }
            }
            else
            {
                /* Other clusters can be processed here */
                NRF_LOG_INFO("Unhandled cluster attribute id: %d", cluster_id);
            }
            break;

        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]   param   Reference to ZigBee stack buffer used to pass arguments (signal).
 */
void zboss_signal_handler(zb_uint8_t param)
{
    zb_zdo_app_signal_hdr_t  * p_sg_p = NULL;
    zb_zdo_app_signal_type_t   sig    = zb_get_app_signal(param, &p_sg_p);
    zb_ret_t                   status = ZB_GET_APP_SIGNAL_STATUS(param);
    zb_bool_t                  comm_status;

    switch (sig)
    {
        case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
        case ZB_BDB_SIGNAL_DEVICE_REBOOT:
            if (status == RET_OK)
            {
                NRF_LOG_INFO("Joined network successfully");
                bsp_board_led_on(ZIGBEE_NETWORK_STATE_LED);
            }
            else
            {
                NRF_LOG_ERROR("Failed to join network. Status: %d", status);
                bsp_board_led_off(ZIGBEE_NETWORK_STATE_LED);
                comm_status = bdb_start_top_level_commissioning(ZB_BDB_NETWORK_STEERING);
                ZB_COMM_STATUS_CHECK(comm_status);
            }
            break;

        case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
            if (status != RET_OK)
            {
                NRF_LOG_WARNING("Production config is not present or invalid");
            }
            break;

        case ZB_ZDO_SIGNAL_LEAVE:
            if (status == RET_OK)
            {
                bsp_board_led_off(ZIGBEE_NETWORK_STATE_LED);

                zb_zdo_signal_leave_params_t * p_leave_params = ZB_ZDO_SIGNAL_GET_PARAMS(p_sg_p, zb_zdo_signal_leave_params_t);
                NRF_LOG_INFO("Network left. Leave type: %d", p_leave_params->leave_type);
            }
            else
            {
                NRF_LOG_ERROR("Unable to leave network. Status: %d", status);
            }
            break;

        default:
            /* Unhandled signal. For more information see: zb_zdo_app_signal_type_e and zb_ret_e */
            NRF_LOG_INFO("Unhandled signal %d. Status: %d", sig, status);
            break;
    }

    if (param)
    {
        ZB_FREE_BUF_BY_REF(param);
    }
}

//flash initialization function 
static void flash_initialization(void)
{
    ret_code_t     error_code;
    nrf_fstorage_api_t * p_fs_api;

  #ifdef SOFTDEVICE_PRESENT
      NRF_LOG_INFO("SoftDevice is present.");
      NRF_LOG_INFO("Initializing nrf_fstorage_sd implementation...");
      /* Initialize an fstorage instance using the nrf_fstorage_sd backend.
       * nrf_fstorage_sd uses the SoftDevice to write to flash. This implementation can safely be
       * used whenever there is a SoftDevice, regardless of its status (enabled/disabled). */
      p_fs_api = &nrf_fstorage_sd;
  #else
      NRF_LOG_INFO("SoftDevice not present.");
      NRF_LOG_INFO("Initializing nrf_fstorage_nvmc implementation...");
      /* Initialize an fstorage instance using the nrf_fstorage_nvmc backend.
       * nrf_fstorage_nvmc uses the NVMC peripheral. This implementation can be used when the
       * SoftDevice is disabled or not present.
       *
       * Using this implementation when the SoftDevice is enabled results in a hardfault. */
      p_fs_api = &nrf_fstorage_nvmc;
  #endif

    error_code = nrf_fstorage_init(&fstorage, p_fs_api, NULL);
    APP_ERROR_CHECK(error_code);
}
/**@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. */

    log_init();
    leds_buttons_init();
    gpio_init();
    flash_initialization();   //flash 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_router_role(IEEE_CHANNEL_MASK);
    zb_set_max_children(MAX_CHILDREN);
    zigbee_erase_persistent_storage(ERASE_PERSISTENT_CONFIG);
   
    rc = nrf_fstorage_read(&fstorage,FLASH_START_ADDRESS,&ui8ResetReadCount,sizeof(ui8ResetReadCount));   //read count from flash
    APP_ERROR_CHECK(rc);

    if(ui8ResetReadCount == 0xFFFFFFFF)   //if the read data is 0xffffff then map ui8ResetReadCount to zero
          ui8ResetReadCount = 1;
    //check the device turn On/Off cycle is 3. if yes, then erase the device boanding information and reset the device 
    if((ui8ResetReadCount % 3)==0)   
    {
        
        boolZCDTrigger = true;

        nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN,1);
        nrf_delay_ms(500);
        nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN,0);
        nrf_delay_ms(500);
        nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN,1);
        nrf_delay_ms(500);
        nrf_gpio_pin_write(DIMMER_TRIGGER_OUT_PIN,0);
        nrf_delay_ms(500);      
       
        rc = nrf_fstorage_erase(&fstorage,FLASH_START_ADDRESS,1, NULL);
        APP_ERROR_CHECK(rc);
        ui8ResetWriteCount = 0;
        ui8ResetReadCount = 0;
        
        zigbee_erase_persistent_storage(ZB_TRUE);
        boolZCDTrigger = false;
    }
    else
    {
        //if the device ON/Off cycle is less than 3, continue with device normal operation and ui8ResetWriteCount to flash with increment by 1. 
        rc = nrf_fstorage_erase(&fstorage,FLASH_START_ADDRESS,1, NULL);
        APP_ERROR_CHECK(rc);

        ui8ResetWriteCount = ui8ResetReadCount+1;
        rc = nrf_fstorage_write(&fstorage,FLASH_START_ADDRESS, &ui8ResetWriteCount, sizeof(ui8ResetWriteCount), NULL);
        APP_ERROR_CHECK(rc);
        timer2creates_start();  //start timer to count device power On/Off cycle
    }

    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();
    level_control_set_value(m_dev_ctx.level_control_attr.current_level);

    /** Start Zigbee Stack. */
    zb_err_code = zboss_start();
    ZB_ERROR_CHECK(zb_err_code);



    while(1)
    {
        zboss_main_loop_iteration();
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());

    }
}


/**
 * @}
 */
