/* Multistate input endpoint */


/*** Macros and defines ***/

/*!< Multistate Input Device ID */
#define ZB_MULTI_INPUT_DEVICE_ID   ZB_HA_TEST_DEVICE_ID

/*!< Multistate Input device version */
#define ZB_MULTI_INPUT_DEVICE_VER   0

/*!< Multistate Input device IN (server) clusters number (Basic, Identify, Multistate input) */
#define ZB_MULTI_INPUT_IN_CLUSTER_NUM   3

/*!< Multistate Input device OUT (client) clusters number (Identify) */
#define ZB_MULTI_INPUT_OUT_CLUSTER_NUM   1

#define ZB_MULTI_INPUT_CLUSTER_NUM   (ZB_MULTI_INPUT_IN_CLUSTER_NUM + ZB_MULTI_INPUT_OUT_CLUSTER_NUM)

/*! @brief Number of attribute for reporting on Multistate Input device (PresentValue and StatusFlags of Multistate input cluster) */
#define ZB_MULTI_INPUT_REPORT_ATTR_COUNT   2


#define ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID   0x004a
#define ZB_ZCL_ATTR_MULTI_INPUT_OUT_OF_SERVICE_ID     0x0051
#define ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID      0x0055
#define ZB_ZCL_ATTR_MULTI_INPUT_STATUS_FLAGS_ID       0x006F


#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID(data_ptr) \
{                                                                                    \
  ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID,                                       \
  ZB_ZCL_ATTR_TYPE_U16,                                                              \
  ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL,                  \
  (void*) data_ptr                                                                   \
}


#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_MULTI_INPUT_OUT_OF_SERVICE_ID(data_ptr) \
{                                                                                  \
  ZB_ZCL_ATTR_MULTI_INPUT_OUT_OF_SERVICE_ID,                                       \
  ZB_ZCL_ATTR_TYPE_BOOL,                                                           \
  ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL,                \
  (void*) data_ptr                                                                 \
}


#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID(data_ptr)                  \
{                                                                                                  \
  ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID,                                                        \
  ZB_ZCL_ATTR_TYPE_U16,                                                                            \
  ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL | ZB_ZCL_ATTR_ACCESS_REPORTING, \
  (void*) data_ptr                                                                                 \
}


#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_MULTI_INPUT_STATUS_FLAGS_ID(data_ptr) \
{                                                                                \
  ZB_ZCL_ATTR_MULTI_INPUT_STATUS_FLAGS_ID,                                       \
  ZB_ZCL_ATTR_TYPE_8BITMAP,                                                      \
  ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_REPORTING,                   \
  (void*) data_ptr                                                               \
}


/** @brief Declare attribute list for Multitate Input (Basic) cluster
    @param attr_list - attribute list name
    @param number_of_states - pointer to variable to store NumberOfStates attribute value
    @param out_of_service - pointer to variable to store OutOfService attribute value
    @param present_value -  pointer to variable to store PresentValue attribute value
    @param status_flags -  pointer to variable to store StatusFlag attribute value
*/
#define ZB_ZCL_DECLARE_MULTI_INPUT_ATTRIB_LIST(                                         \
    attr_list, number_of_states, out_of_service, present_value, status_flags)           \
  ZB_ZCL_START_DECLARE_ATTRIB_LIST(attr_list)                                           \
  ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID, (number_of_states)) \
  ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_MULTI_INPUT_OUT_OF_SERVICE_ID, (out_of_service))     \
  ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID, (present_value))       \
  ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_MULTI_INPUT_STATUS_FLAGS_ID, (status_flags))         \
  ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST


/** @brief Declare cluster list for Multistate Input device
    @param cluster_list_name - cluster list variable name
    @param basic_attr_list - attribute list for Basic cluster
    @param identify_attr_list - attribute list for Identify cluster
    @param multi_input_attr_list - attribute list for Multistate Input (Basic) cluster
 */
#define ZB_ZCL_DECLARE_MULTISTATE_INPUT_CLUSTER_LIST(          \
  cluster_list_name,                                           \
  basic_attr_list,                                             \
  identify_attr_list,                                          \
  multi_input_attr_list)                                       \
  zb_zcl_cluster_desc_t cluster_list_name[] =                  \
  {                                                            \
    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_IDENTIFY,                              \
      0,                                                       \
      NULL,                                                    \
      ZB_ZCL_CLUSTER_CLIENT_ROLE,                              \
      ZB_ZCL_MANUF_CODE_INVALID                                \
      ),                                                       \
    ZB_ZCL_CLUSTER_DESC(                                       \
      ZB_ZCL_CLUSTER_ID_IDENTIFY,                              \
      ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t),    \
      (identify_attr_list),                                    \
      ZB_ZCL_CLUSTER_SERVER_ROLE,                              \
      ZB_ZCL_MANUF_CODE_INVALID                                \
      ),                                                       \
    ZB_ZCL_CLUSTER_DESC(                                       \
      ZB_ZCL_CLUSTER_ID_MULTI_INPUT,                           \
      ZB_ZCL_ARRAY_SIZE(multi_input_attr_list, zb_zcl_attr_t), \
      multi_input_attr_list,                                   \
      ZB_ZCL_CLUSTER_SERVER_ROLE,                              \
      ZB_ZCL_MANUF_CODE_INVALID                                \
    )                                                          \
  }


/** @brief Declare simple descriptor for Multistate Input device
    @param ep_name - endpoint variable name
    @param ep_id - endpoint ID
    @param in_clust_num - number of supported input clusters
    @param out_clust_num - number of supported output clusters
*/
#define ZB_ZCL_DECLARE_MULTISTATE_INPUT_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num) \
  ZB_DECLARE_SIMPLE_DESC(in_clust_num, out_clust_num);                                           \
  ZB_AF_SIMPLE_DESC_TYPE(in_clust_num, out_clust_num) simple_desc_##ep_name =                    \
  {                                                                                              \
    ep_id,                                                                                       \
    ZB_AF_HA_PROFILE_ID,                                                                         \
    ZB_MULTI_INPUT_DEVICE_ID,                                                                    \
    ZB_MULTI_INPUT_DEVICE_VER,                                                                   \
    0,                                                                                           \
    in_clust_num,                                                                                \
    out_clust_num,                                                                               \
    {                                                                                            \
      ZB_ZCL_CLUSTER_ID_BASIC,                                                                   \
      ZB_ZCL_CLUSTER_ID_IDENTIFY,                                                                \
      ZB_ZCL_CLUSTER_ID_MULTI_INPUT,                                                             \
      ZB_ZCL_CLUSTER_ID_IDENTIFY,                                                                \
    }                                                                                            \
  }


/** @brief Declare endpoint for Multistate Input device
    @param ep_name - endpoint variable name
    @param ep_id - endpoint ID
    @param cluster_list - endpoint cluster list
 */
#define ZB_DECLARE_MULTISTATE_INPUT_EP(ep_name, ep_id, cluster_list)      \
  ZB_ZCL_DECLARE_MULTISTATE_INPUT_SIMPLE_DESC(                            \
    ep_name,                                                              \
    ep_id,                                                                \
    ZB_MULTI_INPUT_IN_CLUSTER_NUM,                                        \
    ZB_MULTI_INPUT_OUT_CLUSTER_NUM);                                      \
  ZBOSS_DEVICE_DECLARE_REPORTING_CTX(                                     \
    reporting_info## ep_name,                                             \
    ZB_MULTI_INPUT_REPORT_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_MULTI_INPUT_REPORT_ATTR_COUNT, reporting_info## ep_name,           \
    0, NULL)


#define ZB_ZCL_CLUSTER_ID_MULTI_INPUT_SERVER_ROLE_INIT    zb_zcl_multi_input_init_server
#define ZB_ZCL_CLUSTER_ID_MULTI_INPUT_CLIENT_ROLE_INIT    zb_zcl_multi_input_init_client


/** Declarations and functions ***/


typedef struct {
    zb_uint16_t number_of_states;
    zb_bool_t out_of_service;
    zb_uint16_t present_value;
    zb_uint8_t status_flags;
} zb_zcl_multi_input_attr_values_t;


zb_ret_t check_value_multistate_server(zb_uint16_t attr_id, zb_uint8_t endpoint, zb_uint8_t *value)
{
    LOG_ERR("%s(0x%08x): not implemented", __func__, attr_id);
    return RET_OK;
}


void zb_zcl_multi_input_init_server()
{
    zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_MULTI_INPUT,
                                ZB_ZCL_CLUSTER_SERVER_ROLE,
                                check_value_multistate_server,
                                (zb_zcl_cluster_write_attr_hook_t)NULL,
                                (zb_zcl_cluster_handler_t)NULL);
}


void zb_zcl_multi_input_init_client()
{
    zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_MULTI_INPUT,
                                ZB_ZCL_CLUSTER_CLIENT_ROLE,
                                (zb_zcl_cluster_check_value_t)NULL,
                                (zb_zcl_cluster_write_attr_hook_t)NULL,
                                (zb_zcl_cluster_handler_t)NULL);
}


/*** Main Application ***/


/* Main application customizable context. Stores all settings and static values. */
typedef struct {
	zb_zcl_basic_attrs_ext_t           basic_attr;
	zb_zcl_identify_attrs_t            identify_attr;
	zb_zcl_multi_input_attr_values_t   multi_input_attr;
} multistate_input_device_ctx_t;

static multistate_input_device_ctx_t dev_ctx;


ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(
	identify_attr_list,
	&dev_ctx.identify_attr.identify_time);

ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT(
	basic_attr_list,
	&dev_ctx.basic_attr.zcl_version,
	&dev_ctx.basic_attr.app_version,
	&dev_ctx.basic_attr.stack_version,
	&dev_ctx.basic_attr.hw_version,
	dev_ctx.basic_attr.mf_name,
	dev_ctx.basic_attr.model_id,
	dev_ctx.basic_attr.date_code,
	&dev_ctx.basic_attr.power_source,
	dev_ctx.basic_attr.location_id,
	&dev_ctx.basic_attr.ph_env,
	dev_ctx.basic_attr.sw_ver);

ZB_ZCL_DECLARE_MULTI_INPUT_ATTRIB_LIST(
	multi_input_list,
	&dev_ctx.multi_input_attr.number_of_states,
	&dev_ctx.multi_input_attr.out_of_service,
	&dev_ctx.multi_input_attr.present_value,
	&dev_ctx.multi_input_attr.status_flags);

ZB_ZCL_DECLARE_MULTISTATE_INPUT_CLUSTER_LIST(
	multi_input_clusters,
	basic_attr_list,
	identify_attr_list,
	multi_input_list);

ZB_DECLARE_MULTISTATE_INPUT_EP(multistate_ep, MULTI_INPUT_ENDPOINT, multi_input_clusters);
