#ifndef __ADEMCO_H__
#define __ADEMCO_H__

#include "ademco_config.h"
#include "ademco_version.h"

#ifdef __cplusplus
extern "C" {
#endif

// Prototypes
typedef uint32_t ademco_id_t;
typedef uint8_t ademco_gg_t;
typedef uint16_t ademco_zone_t;

#define ADEMCO_PACKET_ACCT_MIN_LEN 6                // #acct之acct最小长度
#define ADEMCO_PACKET_ACCT_MAX_LEN 18               // #acct之acct最大长度
#define ADEMCO_PACKET_ACCT_MAC_LEN 12               // WiFi主机账号使用MAC地址
#define ADEMCO_PACKET_PWD_MIN_LEN 6                 // 主机密码最小长度
#define ADEMCO_PACKET_PWD_MAX_LEN 8                 // 主机密码最大长度
#define ADEMCO_PACKET_DATA_SEGMENT_EMPTY_LEN 2      // 空data段[]长度
#define ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN 21      // 非空data段长度,acct长度6
#define ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN_MAX 32  // 非空data段长度,acct长度未知
#define ADEMCO_PACKET_MAX_LEN 256                   // 安定宝协议最大长度,包括前缀、后缀、data段、xdata段
#define CONGWIN_FE100_PACKET_LEN 31                 // 丛文FE100协议长度
#define ADEMCO_PACKET_TIMESTAMP_LEN 20              // 时间戳长度
#define ADEMCO_PACKET_XDATA_MAX_LEN 128             // xdata段最大长度

// 安定宝ID范围
#define ADEMCO_ID_INVALID 0
#define ADEMCO_ID_MIN 1
#define ADEMCO_ID_MAX 999999  // 兼容性考虑,最大安定宝 ID 为 0x0F423F
#define ADEMCO_ID_SENTINEL (ADEMCO_ID_MAX + 1)
#define ADEMCO_ID_MASK 0xFFFFFF
#define ademco_is_valid_ademco_id(ademco_id) \
    ((ADEMCO_ID_MIN <= (ademco_id)) && ((ademco_id) <= ADEMCO_ID_MAX))

// 防区号为0时表示主机自身
#define ADEMCO_ZONE_FOR_MACHINE_SELF 0
// 主机防区号范围
#define ADEMCO_ZONE_MIN 1
// 对任何主机类型,最大的防区号
#define ADEMCO_ZONE_MAX 999
#define ADEMCO_ZONE_SENTINEL (ADEMCO_ZONE_MAX + 1)
#define ADEMCO_ZONE_MASK 0xFFFF

// 对任何主机类型,防区号是否合法(可以包含0防区)
#define ademco_is_valid_zone(zone) \
    ((ADEMCO_ZONE_FOR_MACHINE_SELF <= (zone)) && ((zone) <= ADEMCO_ZONE_MAX))

// 对任何主机类型,防区号是否合法(不可以包含0防区)
#define ademco_is_valid_zone_strict(zone) \
    ((ADEMCO_ZONE_MIN <= (zone)) && ((zone) <= ADEMCO_ZONE_MAX))

#define ADEMCO_GG_MIN 0
#define ADEMCO_GG_MAX 99
#define ADEMCO_GG_SENTINEL 100
#define ADEMCO_GG_MASK 0xFF

// 信号强度范围 [0, 31]
#define ADEMCO_SIGNAL_STRENGTH_MIN 0
#define ADEMCO_SIGNAL_STRENGTH_MAX 31

// 主机状态
#define ADEMCO_STATUS_EVENTS_MAP(XX) \
    XX(3400, ARM_AWAY, "离家布防")   \
    XX(1400, DISARM, "撤防")         \
    XX(3456, ARM_STAY, "留守布防")   \
    XX(1456, ARM_STAY_1456, "留守布防")

// 防区报警
#define ADEMCO_ALARM_EVENTS_MAP(XX)   \
    XX(1120, EMERGENCY, "紧急报警")   \
    XX(1130, BURGLAR, "盗警")         \
    XX(1134, DOOR_RING, "门铃")       \
    XX(1110, FIRE, "火警")            \
    XX(1121, DURESS, "胁迫")          \
    XX(1151, GAS, "煤气")             \
    XX(1113, WATER, "水警")           \
    XX(1137, TAMPER, "主机防拆")      \
    XX(1383, ZONE_TAMPER, "防区防拆") \
    XX(1570, BYPASS, "旁路")

// 防区报警恢复
#define ADEMCO_ALARM_RECOVER_EVENTS_MAP(XX)       \
    XX(3120, EMERGENCY_RECOVER, "紧急恢复")       \
    XX(3130, BURGLAR_RECOVER, "盗警恢复")         \
    XX(3134, DOOR_RING_RECOVER, "门铃恢复")       \
    XX(3110, FIRE_RECOVER, "火警恢复")            \
    XX(3121, DURESS_RECOVER, "胁迫恢复")          \
    XX(3151, GAS_RECOVER, "煤气恢复")             \
    XX(3113, WATER_RECOVER, "水警恢复")           \
    XX(3137, TAMPER_RECOVER, "主机防拆恢复")      \
    XX(3383, ZONE_TAMPER_RECOVER, "防区防拆恢复") \
    XX(3570, BYPASS_RECOVER, "解除旁路")

// 防区异常
#define ADEMCO_EXEPTION_EVENTS_MAP(XX)    \
    XX(1301, AC_BROKEN, "主机AC掉电")     \
    XX(1302, LOW_BATTERY, "低电")         \
    XX(1311, BAD_BATTERY, "坏电")         \
    XX(1387, SOLAR_DISTURB, "光扰")       \
    XX(1381, DISCONNECT, "失效")          \
    XX(1393, LOST, "失联")                \
    XX(1384, POWER_EXCEPTION, "电源故障") \
    XX(1380, OTHER_EXCEPTION, "其他故障")

// 防区异常恢复
#define ADEMCO_EXEPTION_RECOVER_EVENTS_MAP(XX)        \
    XX(3301, AC_RECOVER, "主机AC复电")                \
    XX(3302, LOW_BATTERY_RECOVER, "低电恢复")         \
    XX(3311, BAD_BATTERY_RECOVER, "坏电恢复")         \
    XX(3387, SOLAR_DISTURB_RECOVER, "光扰恢复")       \
    XX(3381, DISCONNECT_RECOVER, "失效恢复")          \
    XX(3393, LOST_RECOVER, "失联恢复")                \
    XX(3384, POWER_EXCEPTION_RECOVER, "电源故障恢复") \
    XX(3380, OTHER_EXCEPTION_RECOVER, "其他故障恢复") \
    XX(3100, CLEAR_EXCPTION, "清除异常指示")

// 恒博自定义安定宝事件码
#define ADEMCO_HB_EVENTS_MAP(XX)                               \
    XX(1485, SERIAL_485_DIS, "485断开")                        \
    XX(3485, SERIAL_485_RECOVER, "485连接")                    \
    XX(1700, CONN_HANGUP, "链路挂起")                          \
    XX(3700, CONN_RECOVER, "链路恢复")                         \
    XX(1701, DISARM_PWD_ERR, "撤防密码错误")                   \
    XX(1702, SUB_MACHINE_SENSOR_EXCEPTION, "分机探头异常")     \
    XX(3702, SUB_MACHINE_SENSOR_RECOVER, "分机探头恢复")       \
    XX(1703, SUB_MACHINE_POWER_EXCEPTION, "分机电源异常")      \
    XX(3703, SUB_MACHINE_POWER_RECOVER, "分机电源恢复")        \
    XX(1704, COM_PASSTHROUGH, "串口透传")                      \
    XX(2704, ENTER_SET_MODE, "进入设置状态")                   \
    XX(3704, EXIT_SET_MODE, "退出设置状态")                    \
    XX(1705, QUERY_SUB_MACHINE, "查询分机信息")                \
    XX(1706, WRITE_TO_MACHINE, "写入主机信息")                 \
    XX(1707, I_AM_NET_MODULE, "主机类型--网络模块")            \
    XX(1717, I_AM_GPRS, "主机类型--GPRS主机")                  \
    XX(1727, I_AM_LCD, "主机类型--液晶主机")                   \
    XX(1737, I_AM_WIRE, "主机类型--网线主机")                  \
    XX(1747, I_AM_WIFI, "主机类型--WiFi主机(停用)")            \
    XX(1757, I_AM_3_SECTION, "主机类型--三区段主机")           \
    XX(1767, I_AM_IOT, "主机类型--物联卡主机")                 \
    XX(1777, I_AM_TRUE_COLOR, "主机类型--真彩主机")            \
    XX(1787, I_AM_GPRS_IOT, "主机类型--物联卡主机")            \
    XX(1797, I_AM_GPRS_PHONE, "主机类型--GRPS主机带电话功能")  \
    XX(1807, I_AM_NB, "主机类型--NB报警接收主机")              \
    XX(1817, I_AM_WIFI2, "主机类型--WiFi主机(新版)")           \
    XX(1709, PHONE_USER_SOS, "手机用户SOS")                    \
    XX(1711, PHONE_USER_CANCLE_ALARM, "手机用户消警")          \
    XX(1712, ENTER_SETTING_MODE, "主机进入设置状态")           \
    XX(3712, EXIT_SETTING_MODE, "主机退出设置状态")            \
    XX(1710, RESTORE_FACTORY_SETTINGS_710, "主机恢复出厂设置") \
    XX(1713, RESTORE_FACTORY_SETTINGS, "主机恢复出厂设置")     \
    XX(1756, SIM_IS_IOT_CARD, "物联卡")                        \
    XX(2756, SIM_IS_IOT_PLATFORM_CARD, "平台物联卡")           \
    XX(3756, SIM_IS_NOT_IOT_CARD, "非物联卡")                  \
    XX(1798, WHAT_IS_YOUR_TYPE, "索要主机类型")                \
    XX(1799, SIGNAL_STRENGTH_CHANGED, "信号强度")              \
    XX(1944, OFFLINE, "主机断线")                              \
    XX(1946, ONLINE, "主机上线")

// Ademco events
typedef enum ademco_event_t {
    EVENT_INVALID_EVENT = 0,

#define XX(code, name, zh) EVENT_##name = code,

    // ----标准安定宝协议事件----
    // 主机状态
    ADEMCO_STATUS_EVENTS_MAP(XX)
    // 防区报警
    ADEMCO_ALARM_EVENTS_MAP(XX)
    // 防区报警恢复
    ADEMCO_ALARM_RECOVER_EVENTS_MAP(XX)
    // 防区异常
    ADEMCO_EXEPTION_EVENTS_MAP(XX)
    // 防区异常恢复
    ADEMCO_EXEPTION_RECOVER_EVENTS_MAP(XX)

    // ---私有事件---------
    ADEMCO_HB_EVENTS_MAP(XX)
#undef XX

#define ADEMCO_EVENT_SENTINEL 10000

} ademco_event_t;

// 安定宝事件级别
typedef enum ademco_event_level_t {
    EVENT_LEVEL_NULL,
    EVENT_LEVEL_STATUS,             // 主机状态
    EVENT_LEVEL_EXCEPTION_RECOVER,  // 黄色报警
    EVENT_LEVEL_EXCEPTION,          // 橙色报警
    EVENT_LEVEL_ALARM,              // 红色报警
} ademco_event_level_t;

// 安定宝协议解析结果
typedef enum ademco_parse_result_t {
    ADEMCO_PARSE_RESULT_OK,          // 解析到正确、完整的一包
    ADEMCO_PARSE_RESULT_NOT_ENOUGH,  // 解析到正确、不完整的一包,需要缓存数据,下次继续处理
    ADEMCO_PARSE_RESULT_ERROR,       // 解析到错误的数据,应当丢弃该数据包,丢弃长度为 *ate
} ademco_parse_result_t;

// 安定宝协议解析错误信息
typedef struct ademco_parse_error_t {
    const char* file;  // user do not free
    int line;          // line number in ademco.c
    size_t offset;     // error offset
    const char* msg;   // user do not free
} ademco_parse_error_t;

// 安定宝协议data段
typedef struct ademco_data_t {
    ademco_char_t raw[ADEMCO_PACKET_DATA_SEGMENT_FULL_LEN_MAX];
    size_t raw_len;
    ademco_id_t ademco_id;
    ademco_event_t ademco_event;
    ademco_gg_t gg;
    ademco_zone_t zone;
} ademco_data_t;

typedef enum ademco_xdata_length_format_t {
    ADEMCO_XDATA_LENGTH_FMT_TWO_HEX,       // 两个hex字节表示长度
    ADEMCO_XDATA_LENGTH_FMT_FOUR_DECIMAL,  // 四个decimal char 表示长度
} ademco_xdata_length_format_t;

typedef enum ademco_xdata_transform_t {
    ADEMCO_XDATA_TRANSFORM_AS_IS,     // 原样打包
    ADEMCO_XDATA_TRANSFORM_TO_ASCII,  // 转为ascii打包,如 0xEB 0xBA 打包为 "EBBA"
} ademco_xdata_transform_t;

typedef struct ademco_xdata_t {
    ademco_xdata_length_format_t lenghFormat;
    ademco_char_t raw[ADEMCO_PACKET_XDATA_MAX_LEN];
    size_t raw_len;
} ademco_xdata_t;

#define ADEMCO_PACKET_ID_NULL "\"NULL\""
#define ADEMCO_PACKET_ID_ACK "\"ACK\""
#define ADEMCO_PACKET_ID_NAK "\"NAK\""
#define ADEMCO_PACKET_ID_DUH "\"DUH\""
#define ADEMCO_PACKET_ID_HB "\"HENG-BO\""
#define ADEMCO_PACKET_ID_ADM_CID "\"ADM-CID\""
#define ADEMCO_PACKET_ID_MOD_REG "\"MODULE_REG\""
#define ADEMCO_PACKET_ID_REG_RSP "\"REG-RSP\""

typedef enum ademco_packet_id_t {
    AID_NULL,
    AID_ACK,
    AID_NAK,
    AID_DUH,
    AID_HB,
    AID_ADM_CID,
    AID_MOD_REG,
    AID_REG_RSP,
    AID_COUNT,
    AID_INVALID = -1,
} ademco_packet_id_t;

#define ADEMCO_RRCVR_DEFAULT "R123ABC"
#define ADEMCO_LPREF_DEFAULT "L456DEF"

#define ADEMCO_PACKET_PREFIX '\n'
#define ADEMCO_PACKET_SUFIX '\r'

typedef struct ademco_packet_t {
    uint16_t crc;
    size_t len;
    ademco_packet_id_t id;
    uint16_t seq;
    // +1 for '\0'
    char acct[ADEMCO_PACKET_ACCT_MAX_LEN + 1];
    ademco_data_t dat;
    ademco_xdata_t xdat;
    time_t timestamp;

    ademco_char_t raw[ADEMCO_PACKET_MAX_LEN];
    size_t raw_len;
} ademco_packet_t;

/* control source defs
 * 范围 0-255
 * 0: 主机(布撤防来源目前只有键盘,紧急报警可以是键盘,也可以是遥控器)
 * 1-10: 遥控器
 * 11-50: 保留
 * 51-96: 智能家居
 * 97: 定时器自动控制
 * 98: 中转接警中心
 * 99: 直连接警中心
 * 100-199: 手机APP,末二位为手机尾号
 * 200: web用户,web端包括 网页,H5, 公众号,小程序等
 * 201-255: web分享用户
 * 特别注意:三区段主机,0 代表主机,1-255 都是遥控器
 */
#define ADEMCO_CONTROL_SOURCES_MAP(XX)                \
    XX(0, MACHINE, "主机")                            \
    XX(1, REMOTE, "遥控器")                           \
    XX(10, REMOTE_MAX, "遥控器MAX")                   \
    XX(51, SMART_HOME_ALEXA, "Amazon Alexa")          \
    XX(52, SMART_HOME_GOOGLE, "Google Home")          \
    XX(54, SMART_HOME_APPLE, "Apple Homekit")         \
    XX(55, SMART_HOME_SAMSUNG, "Samsung Smartthings") \
    XX(56, SMART_HOME_ALI_GENIE, "阿里天猫精灵")      \
    XX(57, SMART_HOME_MI_AI, "小米小爱同学")          \
    XX(58, SMART_HOME_BAIDU, "百度小度")              \
    XX(96, SMART_HOME_MAX, "智能家居MAX")             \
    XX(97, TIMER, "定时器")                           \
    XX(98, CENTER_TRANSMIT, "中转接警中心")           \
    XX(99, CENTER_DIRECT, "直连接警中心")             \
    XX(100, PHONE_APP, "手机APP")                     \
    XX(199, PHONE_APP_MAX, "手机APP_MAX")             \
    XX(200, OWNER, "web用户")                         \
    XX(201, SHAREE, "web分享用户")                    \
    XX(255, SHAREE_MAX, "web分享用户MAX")

typedef enum ademco_control_source_t {
#define XX(code, name, zh) ADEMCO_CONTROL_SOURCE_##name = code,
    ADEMCO_CONTROL_SOURCES_MAP(XX)
#undef XX
} ademco_control_source_t;

//////////////////// bcd ////////////////////

#define bcd_encode(x) (((x) / 10) << 4 | ((x) % 10))
#define bcd_decode(x) (((x) >> 4) * 10 + ((x) & 0x0F))

/////////////////// reverse //////////////////

#define rev16(u) (uint16_t)((((uint16_t)(u) & 0xFF) << 8) | (((uint16_t)(u) & 0xFF00) >> 8))
#define rev32(u) (uint32_t)((((uint32_t)(u) & 0xFF) << 24) |    \
                            (((uint32_t)(u) & 0xFF00) << 8) |   \
                            (((uint32_t)(u) & 0xFF0000) >> 8) | \
                            (((uint32_t)(u) & 0xFF000000) >> 24))

//////////////////////// Functions ////////////////////////

ADEMCO_EXPORT_SYMBOL
uint32_t ademco_version(void);

#if ADEMCO_ENABLE_TO_STRING
ADEMCO_EXPORT_SYMBOL
const char* ademco_version_string(void);
#endif

ADEMCO_EXPORT_SYMBOL
void ademco_print(const ademco_char_t* buf, size_t len);

ADEMCO_EXPORT_SYMBOL
uint8_t ademco_encode_signal_strength(uint8_t strength);

ADEMCO_EXPORT_SYMBOL
uint8_t ademco_decode_signal_strength(uint8_t bcd_strength);

// 是否合法主机账号
ADEMCO_EXPORT_SYMBOL
bool ademco_is_valid_account(const char* acct);

// 是否合法主机密码
ADEMCO_EXPORT_SYMBOL
bool ademco_is_valid_password(const char* pwd);

//////////////////////// ademco_event_t functions ////////////////////////

// 是否主机状态事件
ADEMCO_EXPORT_SYMBOL
bool ademco_is_machine_status_event(ademco_event_t ademco_event);

// 是否主机类型事件
ADEMCO_EXPORT_SYMBOL
bool ademco_is_machine_type_event(ademco_event_t ademco_event);

// 事件是否需要控制源
ADEMCO_EXPORT_SYMBOL
bool ademco_is_event_need_control_source(ademco_event_t ademco_event);

// 获取安定宝事件级别
ADEMCO_EXPORT_SYMBOL
ademco_event_level_t ademco_get_event_level(ademco_event_t ademco_event);

// 获取异常恢复事件所对应的异常事件
ADEMCO_EXPORT_SYMBOL
ademco_event_t ademco_get_exception_event_by_recover_event(ademco_event_t recover_event);

#if ADEMCO_ENABLE_TO_STRING
ADEMCO_EXPORT_SYMBOL
const char* ademco_event_to_string(ademco_event_t ademco_event);
#endif

#if ADEMCO_ENABLE_CHINESE
ADEMCO_EXPORT_SYMBOL
const char* ademco_event_to_string_chinese(ademco_event_t ademco_event);
#endif

//////////////////////// ademco_data_t functions ////////////////////////

// make a `DATA` packet and store to `packet`, return length
ADEMCO_EXPORT_SYMBOL
size_t ademco_append_data(ademco_char_t* packet,
                          const char* acct,
                          ademco_id_t ademco_id,
                          ademco_event_t ademco_event,
                          ademco_gg_t gg,
                          ademco_zone_t zone);

// make a `DATA` packet and store to `ademco_data`, return length
ADEMCO_EXPORT_SYMBOL
size_t ademco_append_data2(ademco_data_t* ademco_data,
                           const char* acct,
                           ademco_id_t ademco_id,
                           ademco_event_t ademco_event,
                           ademco_gg_t gg,
                           ademco_zone_t zone);

// inplace make a `DATA` packet and store to `ademco_data`, return length
ADEMCO_EXPORT_SYMBOL
size_t ademco_append_data3(ademco_data_t* ademco_data, const char* acct);

// parse `DATA` packet, if ok, `ademco_data`'s members will be useful
// err can be NULL; caller should only check err on got ADEMCO_PARSE_RESULT_ERROR
ADEMCO_EXPORT_SYMBOL
ademco_parse_result_t ademco_parse_data(const ademco_char_t* packet,
                                        size_t packet_len,
                                        ademco_data_t* ademco_data,
                                        ademco_parse_error_t* err);

// return 0 for empty packet, CONGWIN_FE100_PACKET_LEN for success
ADEMCO_EXPORT_SYMBOL
size_t ademco_data_to_congwin_fe100(ademco_char_t* fe100,
                                    size_t fe100_len,
                                    const ademco_data_t* ademco_data);

//////////////////////// ademco_xdata_t functions ////////////////////////

// init a empty xdat
ADEMCO_EXPORT_SYMBOL
void ademco_xdata_init(ademco_xdata_t* xdat);

ADEMCO_EXPORT_SYMBOL
bool ademco_xdata_convert(ademco_xdata_t* xdat, ademco_xdata_length_format_t xlf);

#ifndef SWIG
// get valid content address of xdat (except [len])
ADEMCO_EXPORT_SYMBOL
const char* ademco_xdata_get_valid_content_addr(const ademco_xdata_t* xdat);
#endif

// get valid content length of xdat (except [len])
ADEMCO_EXPORT_SYMBOL
size_t ademco_xdata_get_valid_content_len(const ademco_xdata_t* xdat);

// return 0 if xdat's valid content is exactly the same as [buf, buf+buf_len)
ADEMCO_EXPORT_SYMBOL
bool ademco_xdata_memeq(const ademco_xdata_t* xdat,
                        const void* buf,
                        size_t buf_len);

// copy xdat content from src to dst, return copied length = src.raw_len
ADEMCO_EXPORT_SYMBOL
size_t ademco_xdata_copy(ademco_xdata_t* dst,
                         const ademco_xdata_t* src);

ADEMCO_EXPORT_SYMBOL
bool ademco_make_xdata(ademco_xdata_t* xdat,
                       ademco_xdata_length_format_t xlf,
                       ademco_xdata_transform_t xtr,
                       const ademco_char_t* content,
                       size_t len);

//////////////////////// ademco_packet_id_t functions ////////////////////////

ADEMCO_EXPORT_SYMBOL
bool ademco_is_valid_packet_id(const char* standard,
                               const char* id,
                               size_t len);

ADEMCO_EXPORT_SYMBOL
ademco_packet_id_t ademco_packet_id_from_string(const char* id,
                                                size_t len);

ADEMCO_EXPORT_SYMBOL
const char* ademco_packet_id_to_string(ademco_packet_id_t id);

/*
 * ademcoMake*Packet functions
 * if buf != NULL and len >= length needed, return length used and copy dat to buf
 * otherwise return 0
 */

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_empty_data_packet(ademco_char_t* dst_buff,
                                     size_t len,
                                     const char* id,
                                     uint16_t seq,
                                     const char* acct,
                                     ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_adm_empty_data_packet(ademco_char_t* dst_buff,
                                         size_t len,
                                         const char* id,
                                         uint16_t seq,
                                         const char* acct,
                                         ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_null_packet(ademco_char_t* buf,
                               size_t len,
                               uint16_t seq,
                               const char* acct,
                               ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_ack_packet(ademco_char_t* buf,
                              size_t len,
                              uint16_t seq,
                              const char* acct,
                              ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_nak_packet(ademco_char_t* buf,
                              size_t len,
                              uint16_t seq,
                              const char* acct,
                              ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_hb_packet(ademco_char_t* buf,
                             size_t len,
                             uint16_t seq,
                             const char* acct,
                             ademco_id_t ademco_id,
                             ademco_event_t ademco_event,
                             ademco_gg_t gg,
                             ademco_zone_t zone,
                             const ademco_xdata_t* xdat);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_adm_packet(ademco_char_t* buf,
                              size_t len,
                              uint16_t seq,
                              const char* acct,
                              ademco_id_t ademco_id,
                              ademco_event_t ademco_event,
                              ademco_gg_t gg,
                              ademco_zone_t zone,
                              const ademco_xdata_t* xdat);

// like upper funcs, store buf and len to pkt->raw, pkt->raw_len
ADEMCO_EXPORT_SYMBOL
size_t ademco_make_null_packet2(ademco_packet_t* pkt,
                                uint16_t seq,
                                const char* acct,
                                ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_ack_packet2(ademco_packet_t* pkt,
                               uint16_t seq,
                               const char* acct,
                               ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_nak_packet2(ademco_packet_t* pkt,
                               uint16_t seq,
                               const char* acct,
                               ademco_id_t ademco_id);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_hb_packet2(ademco_packet_t* pkt,
                              uint16_t seq,
                              const char* acct,
                              ademco_id_t ademco_id,
                              ademco_event_t ademco_event,
                              ademco_gg_t gg,
                              ademco_zone_t zone,
                              const ademco_xdata_t* xdat);

// inplace make from pkt and to pkt
ADEMCO_EXPORT_SYMBOL
size_t ademco_make_hb_packet3(ademco_packet_t* pkt);

ADEMCO_EXPORT_SYMBOL
size_t ademco_make_adm_packet2(ademco_packet_t* pkt,
                               uint16_t seq,
                               const char* acct,
                               ademco_id_t ademco_id,
                               ademco_event_t ademco_event,
                               ademco_gg_t gg,
                               ademco_zone_t zone,
                               const ademco_xdata_t* xdat);

// parse a ademco packet, if everything is OK, cb_commited is the packet length
// err can be NULL; caller should only check err on got ADEMCO_PARSE_RESULT_ERROR
ADEMCO_EXPORT_SYMBOL
ademco_parse_result_t ademco_parse_packet(const ademco_char_t* buf,
                                          size_t len,
                                          ademco_packet_t* pkt,
                                          size_t* cb_commited,
                                          ademco_parse_error_t* err);
#ifndef ADEMCO_USE_EXTERNAL_CRC16
/* CRC16 implementation according to ARC
 * Name                       : CRC-16/ARC
 * Alias					  : ARC, CRC-16, CRC-16/LHA, CRC-IBM
 * Width                      : 16 bit
 * Poly                       : 0x8005 (That is actually x^16 + x^15 + x^2 + 1)
 * Initialization             : 0x0000
 * Reflect Input byte         : True
 * Reflect Output CRC         : True
 * Xor constant to output CRC : 0x0000
 * Output for "123456789"     : 0xBB3D
 */
ADEMCO_EXPORT_SYMBOL
uint16_t ademco_crc16(const ademco_char_t ADEMCO_BUF_MEMORY_MODIFIER* buf, size_t len);
#endif

// 0~9, A~F to '0'~'9', 'A'~'F'
ADEMCO_EXPORT_SYMBOL uint8_t ademco_hex2char(uint8_t h);

// '0'~'9', 'A'~'F', 'a'~'f' to 0~9, A~F
ADEMCO_EXPORT_SYMBOL uint8_t ademco_char2hex(uint8_t c);

// like snprintf(str, hex_len * 2 + 1, "%0`hex_len`X", dec)
// caller should make sure `str` has enough space
// e.g. ademco_dec_to_hex_str(buf, 0x12345678, 3) => buf = "678"
ADEMCO_EXPORT_SYMBOL size_t ademco_dec_to_hex_str(char* str, uint32_t dec, uint8_t hex_len);

// 将一串以高低字节表示的十六进制数组转换为10进制数字符串
// 每个字节的高四位和低四位若不大于9,将该四位表示的数字以10进制ascii码填入str,否则停止
// 返回字符串长度
// 注意:函数不会在str末尾补 null terminator
// 调用者应确保str有足够空间,至少len的2倍,否则会崩溃
// 示例:输入数组:0x18 0x24 0x08 0x88 0x10 0x1f 0xff,输出字符串"18240888101"
ADEMCO_EXPORT_SYMBOL
size_t ademco_hilo_array_to_dec_str(ademco_char_t ADEMCO_BUF_MEMORY_MODIFIER* str,
                                    const uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                    size_t len);

// 将一串以高低字节表示的十六进制数组转换为字符串
// 每个字节的高四位和低四位若不大于9,将该四位表示的数字以10进制ascii码填入str,否则:
// 若定长6字节包含a~f,说明是WiFi主机
// 返回字符串长度
// 注意:函数不会在str末尾补 null terminator
// 调用者应确保str有足够空间,至少len的2倍,否则会崩溃
// 示例:输入数组:0x18 0x24 0x08 0x88 0x10 0x1f 0xff,输出字符串"18240888101"
// 示例:输入数组:0x24 0x94 0x94 0x4c 0x48 0x15 0xff,输出字符串"2494944C4815"
ADEMCO_EXPORT_SYMBOL
size_t ademco_hilo_array_to_hex_str(ademco_char_t ADEMCO_BUF_MEMORY_MODIFIER* str,
                                    const uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                    size_t len);

// 将一个10进制数字符串转为高低字节表示的数组,节省空间
// str应只包含'0'~'9',否则失败返回0
// str长度若大于len * 2,str[len]及之后的内容将被舍弃以避免溢出
// 转换后的长度若不满len,则以0xF补满。
// 示例:输入字符串"123ABC",返回0
// 示例:输入字符串 "18240888101", len=9, 则arr内容为
// 0x18 0x24 0x08 0x88 0x10 0x1f 0xff 0xff 0xff
// return 9
// 示例:输入字符串 "12345678901234567890", len=9, 则arr内容为
// 0x12 0x34 0x56 0x78 0x90 0x12 0x34 0x56 0x78
// return 9
// 示例:输入NULL,返回 9个 0xFF
ADEMCO_EXPORT_SYMBOL
size_t ademco_dec_str_to_hilo_array(uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                    size_t len,
                                    const char ADEMCO_BUF_MEMORY_MODIFIER* str);

// 将一个16进制数字符串转为高低字节表示的数组,节省空间
// str应只包含'0'~'9', 'A'~'F', 'a'~'f',否则失败返回0
// str长度若大于len * 2,str[len]及之后的内容将被舍弃以避免溢出
// 转换后的长度若不满len,则以0xF补满。
// 示例:输入字符串"123ABC",len=6, 则 arr 内容为 0x12 0x3a 0xbc 0xff 0xff 0xff
// 示例:输入字符串 "18240888101", len=9, 则arr内容为
// 0x18 0x24 0x08 0x88 0x10 0x1f 0xff 0xff 0xff
// return 9
// 示例:输入字符串 "12345678901234567890", len=9, 则arr内容为
// 0x12 0x34 0x56 0x78 0x90 0x12 0x34 0x56 0x78
// return 9
// 示例:输入NULL,返回 9个 0xFF
ADEMCO_EXPORT_SYMBOL
size_t ademco_hex_str_to_hilo_array(uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                    size_t len,
                                    const char ADEMCO_BUF_MEMORY_MODIFIER* str);

// 将一串字节流转换为可打印形式
// 返回str长度(len * 2)
// 调用者应确保str有足够的容量,至少len*2
// 注意:函数不会在str末尾补 null terminator
// 示例:输入 arr=[0xEB 0xBA 0x3F], len=3, 则str = "EBBA3F",返回6
// 示例:输入 ary="123", len=3, 则str = "313233",返回6
ADEMCO_EXPORT_SYMBOL
size_t ademco_hex_array_to_str(char ADEMCO_BUF_MEMORY_MODIFIER* str,
                               const uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                               size_t len);

// 将一串字符串(内容为'0'~'9', 'A'~'F', 'a' ~'f')转为字节流
// 若strlen(str)为奇数,以padding补足arr, padding 应 <= 0x0F
// 若str内包含非hex字符串,返回0
// 调用者应确保arr有足够的容量,至少strlen(str)*2
// 示例:输入字符串str="EBBA3F", 输出arr = [0xEB, 0xBA, 0x3F],返回 3
// 示例:输入字符串str="ABC", padding = 0, 输出arr=[0xAB, 0xC0],返回2
// 示例:输入字符串str="ABC", padding = 0x0F, 输出arr=[0xAB, 0xCF],返回2
ADEMCO_EXPORT_SYMBOL
size_t ademco_hex_str_to_array(uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                               const char ADEMCO_BUF_MEMORY_MODIFIER* str,
                               uint8_t padding);

// 功能同ademcoHexStrToArray
// 若strlen(str) > len, str[len]及之后的内容将被舍弃以避免溢出
// 示例:输入字符串str="ABCDE", len = 4 padding = 0x0F, 输出arr=[0xAB, 0xCD], return 2
ADEMCO_EXPORT_SYMBOL
size_t ademco_hex_str_to_array_n(uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                 const char ADEMCO_BUF_MEMORY_MODIFIER* str,
                                 size_t len, uint8_t padding);

// 同ademcoHexStrToArrayN,但允许str包含非hex字符,
// 即'0'~'9','a'~'f','A'~'F'之外的内容,以 padding 替换
// 示例:str="AB\xFFD", len=4, padding=0x0F, 则 arr=[ 0xAB, 0xFD ], return 2
// 网线主机 0d 00 命令,接警中心账号部分,有可能是这种
ADEMCO_EXPORT_SYMBOL
size_t ademco_hex_str_to_array_n_allow_non_hex_str(uint8_t ADEMCO_BUF_MEMORY_MODIFIER* arr,
                                                   const char ADEMCO_BUF_MEMORY_MODIFIER* str,
                                                   size_t len,
                                                   uint8_t padding);

#ifdef __cplusplus
}
#endif

#endif  // __ADEMCO_H__