#include "hb_com.h"
#include <string.h>

int hbGetAvailableZoneProperties(HbMachineType type, HbZoneProperty props[12]) {
	switch (type) {
	case HMT_WIFI:
		break;
	case HMT_CAMERA:
		break;
	case HMT_GPRS_IOT:
	case HMT_GPRS:
	case HMT_WIFI2:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL, };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_NETMOD:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_SUB_MACHINE,
			HZP_REMOTE_CONTROL, HZP_BUGLAR_HALF, HZP_SHIELD, HZP_DOOR_RING, };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_LCD:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_SUB_MACHINE,
			HZP_REMOTE_CONTROL, HZP_BUGLAR_HALF, HZP_SHIELD, HZP_DOOR_RING, HZP_BYPASS };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_WIRED:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL, };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_TRUE_COLOR:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL,
			HZP_SHIELD, HZP_DOOR_RING, HZP_BYPASS };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_3_SECTION:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL,
			HZP_SHIELD, HZP_DOOR_RING, HZP_BYPASS };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_IOT:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL,
			HZP_SHIELD, HZP_DOOR_RING, HZP_BYPASS };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_GPRS_PHONE:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_REMOTE_CONTROL, };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	case HMT_NB:
	{
		HbZoneProperty hzps[] = { HZP_BUGLAR, HZP_EMERGENCY, HZP_FIRE, HZP_DURESS, HZP_GAS, HZP_WATER, HZP_DOOR_RING, };
		memcpy(props, hzps, sizeof(hzps));
		return sizeof(hzps) / sizeof(HbZoneProperty);
	}
	default: return 0;
	}
	return 0;
}

uint16_t hbZoneMax(HbMachineType type) {
	switch (type) {
	case HMT_GPRS_IOT:
	case HMT_GPRS:
	case HMT_WIRED:
	case HMT_WIFI2:
		return 99;

	case HMT_NETMOD:
	case HMT_NB:
		return 999;

	case HMT_LCD:
		return 249;

	case HMT_TRUE_COLOR:
		return 68;

	case HMT_3_SECTION:
		return 191;

	case HMT_IOT:
	case HMT_GPRS_PHONE:
		return 60;

	case HMT_WIFI:
	case HMT_CAMERA:
	default:
		return 0;
		break;
	}
}

int hbIsValidZone(HbMachineType type, uint16_t zone) {
	return ADEMCO_ZONE_FOR_MACHINE_SELF <= zone && zone <= hbZoneMax(type);
}

int hbIsValidZoneStrict(HbMachineType type, uint16_t zone) {
	return ADEMCO_ZONE_MIN <= zone && zone <= hbZoneMax(type);
}

int hbMachineIsSelling(HbMachineType type) {
    return type == HMT_GPRS_IOT ||
           type == HMT_NETMOD ||
           type == HMT_GPRS ||
           type == HMT_IOT ||
           type == HMT_LCD ||
           type == HMT_WIRED ||
           type == HMT_TRUE_COLOR ||
           type == HMT_3_SECTION ||
           type == HMT_GPRS_PHONE ||
           type == HMT_NB ||
           type == HMT_WIFI2;
}

int hbMachineCanArm(HbMachineType type) {
	return hbMachineIsSelling(type) && (type != HMT_NB);
}

int hbMachineCanDisarm(HbMachineType type) {
	return hbMachineIsSelling(type) && (type != HMT_NB);
}

int hbMachineCanEnterSettings(HbMachineType type) {
	return hbMachineIsSelling(type);
}

int hbMachineCanHalfArm(HbMachineType type) {
    return type == HMT_NETMOD || type == HMT_LCD;
}

int hbMachineCanReportSignalStrength(HbMachineType type) {
    return type == HMT_GPRS ||
           type == HMT_GPRS_IOT ||
           type == HMT_IOT ||
           type == HMT_LCD ||
           type == HMT_TRUE_COLOR ||
           type == HMT_3_SECTION ||
           type == HMT_GPRS_PHONE ||
           type == HMT_NB;
}

int hbMachineCanReportBySMS(HbMachineType type) {
    return type == HMT_GPRS ||
           type == HMT_LCD ||
           type == HMT_TRUE_COLOR ||
           type == HMT_3_SECTION ||
           type == HMT_GPRS_PHONE;
}

int hbMachineHasWiredZone(HbMachineType type) {
    return type == HMT_NETMOD ||
           type == HMT_TRUE_COLOR ||
           type == HMT_3_SECTION;
}

AdemcoZone hbWiredZoneMin(HbMachineType type) {
	switch (type) {
	case HMT_NETMOD:		return 1;
	case HMT_TRUE_COLOR:	return 1;
	case HMT_3_SECTION:		return 61;
	default: return 0;
	}
}

AdemcoZone hbWiredZoneMax(HbMachineType type) {
	switch (type) {
	case HMT_NETMOD:		return 8;
	case HMT_TRUE_COLOR:	return 8;
	case HMT_3_SECTION:		return 68;
	default: return 0;
	}
}

int hbMachineCanDirectlyWriteZone(HbMachineType type) {
	return type == HMT_NETMOD;
}

int hbMachineCanLinkSubMachine(HbMachineType type) {
	return type == HMT_NETMOD;
}

int hbZonePropCanReportLost(HbZoneProperty zp) {
	switch (zp) {
	case HZP_BUGLAR:
	case HZP_EMERGENCY:
	case HZP_DURESS:
	case HZP_SUB_MACHINE:
	case HZP_BUGLAR_HALF:
	case HZP_BYPASS:
		return 1;
	default:
		return 0;
	}
}

void hbInitMachineTimer(HbMachineTimer* timer) {
	memset(timer->data, 25, sizeof(timer));
}

int hbIsValidTimePoint(HbMachineTimePoint* tp) {
	return tp && 0 <= tp->hour && tp->hour < 24 &&
		0 <= tp->minute && tp->minute < 60;
}

void hbTimePointToGreenwich(HbMachineTimePoint* tp) {
	if (tp && tp->hour != 25)
		tp->hour = (tp->hour + 16) % 24;
}

void hbTimePointFromGreenwich(HbMachineTimePoint* tp) {
	if (tp && tp->hour != 25)
		tp->hour = (tp->hour + 8) % 24;
}

int hbIsValidTimer(HbTimer* timer) {
	return timer && hbIsValidTimePoint(&timer->armAt) && hbIsValidTimePoint(&timer->disarmAt);
}

int hbIsValidMachineTimer(HbMachineTimer* timer) {
	return timer && hbIsValidTimer(&timer->timer[0]) && hbIsValidTimer(&timer->timer[1]);
}

void hbMachineTimerToGreenwich(HbMachineTimer* timer) {
	hbTimePointToGreenwich(&timer->timer[0].armAt);
	hbTimePointToGreenwich(&timer->timer[0].disarmAt);
	hbTimePointToGreenwich(&timer->timer[1].armAt);
	hbTimePointToGreenwich(&timer->timer[1].disarmAt);
}

void hbMachineTimerFromGreenwich(HbMachineTimer* timer) {
	hbTimePointFromGreenwich(&timer->timer[0].armAt);
	hbTimePointFromGreenwich(&timer->timer[0].disarmAt);
	hbTimePointFromGreenwich(&timer->timer[1].armAt);
	hbTimePointFromGreenwich(&timer->timer[1].disarmAt);
}

AdemcoEvent hbMachineStatusToAdemcoEvent(HbMachineStatus status) {
	switch (status) {
	case HMS_ARM:		return EVENT_ARM;
	case HMS_DISARM:	return EVENT_DISARM;
	case HMS_HALF_ARM:	return EVENT_HALFARM;				// EVENT_HALFARM_1456
	case HMS_SETTING:	return EVENT_ENTER_SETTING_MODE;	// EVENT_ENTER_SET_MODE
	default:			return EVENT_INVALID_EVENT;
	}
}

HbMachineStatus hbMachineStatusFromAdemcoEvent(AdemcoEvent ademcoEvent) {
	switch (ademcoEvent) {
	case EVENT_ARM:
		return HMS_ARM;
	case EVENT_DISARM:
		return HMS_DISARM;
	case EVENT_HALFARM:
	case EVENT_HALFARM_1456:
		return HMS_HALF_ARM;
	case EVENT_ENTER_SETTING_MODE:
	case EVENT_ENTER_SET_MODE:
		return HMS_SETTING;
	default:
		return HMS_INVALID;
	}
}

AdemcoEvent hbMachineTypeToAdemcoEvent(HbMachineType type) {
	switch (type) {
	case HMT_WIFI: 	        return EVENT_I_AM_WIFI_MACHINE;
	case HMT_GPRS_IOT:		return EVENT_I_AM_GPRS_IOT;
	case HMT_NETMOD:	    return EVENT_I_AM_NET_MODULE;
	case HMT_GPRS:	        return EVENT_I_AM_GPRS;
	case HMT_LCD:		    return EVENT_I_AM_LCD_MACHINE;
	case HMT_WIRED: 	    return EVENT_I_AM_WIRE_MACHINE;
	case HMT_TRUE_COLOR:	return EVENT_I_AM_TRUE_COLOR;
	case HMT_3_SECTION: 	return EVENT_I_AM_3_SECTION_MACHINE;
	case HMT_IOT:			return EVENT_I_AM_IOT_MACHINE;
	case HMT_GPRS_PHONE:	return EVENT_I_AM_GPRS_PHONE;
	case HMT_NB:			return EVENT_I_AM_NB_MACHINE;
	case HMT_WIFI2:			return EVENT_I_AM_WIFI2_MACHINE;
	default: 				return EVENT_INVALID_EVENT;
	}
}

HbMachineType hbMachineTypeFromAdemcoEvent(AdemcoEvent ademcoEvent) {
	switch (ademcoEvent) {
	case EVENT_I_AM_NET_MODULE:		    return HMT_NETMOD;
	case EVENT_I_AM_GPRS: 			    return HMT_GPRS;
	case EVENT_I_AM_LCD_MACHINE: 	    return HMT_LCD;
	case EVENT_I_AM_WIRE_MACHINE: 	    return HMT_WIRED;
	case EVENT_I_AM_WIFI_MACHINE: 	    return HMT_WIFI;
	case EVENT_I_AM_3_SECTION_MACHINE: 	return HMT_3_SECTION;
	case EVENT_I_AM_IOT_MACHINE: 		return HMT_IOT;
	case EVENT_I_AM_TRUE_COLOR:			return HMT_TRUE_COLOR;
	case EVENT_I_AM_GPRS_IOT:			return HMT_GPRS_IOT;
	case EVENT_I_AM_GPRS_PHONE:			return HMT_GPRS_PHONE;
	case EVENT_I_AM_NB_MACHINE:			return HMT_NB;
	case EVENT_I_AM_WIFI2_MACHINE:		return HMT_WIFI2;
	default: 							return HMT_INVALID;
	}
}

AdemcoEvent hbZonePropertyToAdemcoEvent(HbZoneProperty zp) {
	switch (zp) {
	case HZP_BUGLAR:
	case HZP_BUGLAR_HALF:	return EVENT_BURGLAR;
	case HZP_EMERGENCY:	    return EVENT_EMERGENCY;
	case HZP_FIRE:			return EVENT_FIRE;
	case HZP_DURESS:		return EVENT_DURESS;
	case HZP_GAS:			return EVENT_GAS;
	case HZP_WATER:		    return EVENT_WATER;
	case HZP_DOOR_RING:		return EVENT_DOORRINGING;
	default:				return EVENT_INVALID_EVENT;
	}
}

const char* hbMachineStatusToString(HbMachineStatus status) {
	switch (status) {
	case HMS_ARM:		return "Arm";
	case HMS_HALF_ARM:	return "Half-Arm";
	case HMS_DISARM:	return "Disarm";
	case HMS_SETTING:	return "Setting";
	default:			return "InvalidMachineStatus";
	}
}

const char* hbMachineTypeToString(HbMachineType type) {
	switch (type) {
	case HMT_WIFI:			return "0 WiFi";
	case HMT_CAMERA:		return "1 Camera";
	case HMT_GPRS_IOT:		return "2 Gprs_IoT";
	case HMT_NETMOD:		return "3 NetMod";
	case HMT_GPRS:			return "4 GPRS";
	case HMT_LCD:			return "5 LCD";
	case HMT_WIRED:			return "6 Wired";
	case HMT_TRUE_COLOR:	return "7 TrueColor";
	case HMT_3_SECTION:		return "8 ThreeSection";
	case HMT_IOT:			return "9 IoT";
	case HMT_GPRS_PHONE:	return "10 Gprs_Phone";
	case HMT_NB:			return "11 Nb";
	case HMT_WIFI2:			return "12 WiFi2";
	default:				return "Unknown HbMachineType";
	}
}

const char* hbZonePropertyToString(HbZoneProperty zp) {
	switch (zp) {
	case HZP_BUGLAR:			return "HZP_BUGLAR";
	case HZP_EMERGENCY:			return "HZP_EMERGENCY";
	case HZP_FIRE:				return "HZP_FIRE";
	case HZP_DURESS:			return "HZP_DURESS";
	case HZP_GAS:				return "HZP_GAS";
	case HZP_WATER:				return "HZP_WATER";
	case HZP_SUB_MACHINE:		return "HZP_SUB_MACHINE";
	case HZP_REMOTE_CONTROL:	return "HZP_REMOTE_CONTROL";
	case HZP_BUGLAR_HALF:		return "HZP_BUGLAR_HALF";
	case HZP_SHIELD:			return "HZP_SHIELD";
	case HZP_DOOR_RING:			return "HZP_DOOR_RING";
	case HZP_RESERVED_0B:		return "Reserved0B";
	case HZP_RESERVED_0C:		return "Reserved0C";
	case HZP_RESERVED_0D:		return "Reserved0D";
	case HZP_RESERVED_0E:		return "Reserved0E";
	case HZP_BYPASS:			return "HZP_BYPASS";
	default:					return "InvalidZoneProperty";
	}
}

#ifdef ADEMCO_ENABLE_CHINESE
const char* hbMachineStatusToStringChinese(HbMachineStatus status) {
	switch (status) {
	case HMS_ARM:		return "布防";
	case HMS_HALF_ARM:	return "半布防";
	case HMS_DISARM:	return "撤防";
	case HMS_SETTING:	return "设置";
	default:			return "InvalidMachineStatus";
	}
}

const char* hbMachineTypeToStringChinese(HbMachineType type) {
	switch (type) {
	case HMT_WIFI:			return "0 WiFi主机";
	case HMT_CAMERA:		return "1 摄像头主机";
	case HMT_GPRS_IOT:		return "2 简化的物联卡主机";
	case HMT_NETMOD:		return "3 网络模块+工程主机";
	case HMT_GPRS:			return "4 GPRS主机";
	case HMT_LCD:			return "5 液晶主机";
	case HMT_WIRED:			return "6 网线主机";
	case HMT_TRUE_COLOR:	return "7 真彩主机";
	case HMT_3_SECTION:		return "8 三区段主机";
	case HMT_IOT:			return "9 物联卡主机";
	case HMT_GPRS_PHONE:	return "10 GPRS主机能打电话";
	case HMT_NB:			return "11 NB报警接收主机";
	case HMT_WIFI2:			return "12 WiFi主机新版";
	default:				return "未知主机";
	}
}

const char* hbZonePropertyToStringChinese(HbZoneProperty zp) {
	switch (zp) {
	case HZP_BUGLAR:			return "匪警全局";
	case HZP_EMERGENCY:			return "匪警紧急";
	case HZP_FIRE:				return "火警防区";
	case HZP_DURESS:			return "胁迫防区";
	case HZP_GAS:				return "燃气防区";
	case HZP_WATER:				return "淹水防区";
	case HZP_SUB_MACHINE:		return "分机";
	case HZP_REMOTE_CONTROL:	return "遥控器";
	case HZP_BUGLAR_HALF:		return "匪警半局";
	case HZP_SHIELD:			return "屏蔽防区";
	case HZP_DOOR_RING:			return "门铃防区";
	case HZP_RESERVED_0B:		return "保留";
	case HZP_RESERVED_0C:		return "保留";
	case HZP_RESERVED_0D:		return "保留";
	case HZP_RESERVED_0E:		return "保留";
	case HZP_BYPASS:			return "旁路防区";
	default:					return "无效属性";
	}
}
#endif

const char* hbGetZoneFormatString(HbMachineType type) {
	uint16_t zone = hbZoneMax(type);
	if (10 < zone && zone < 100)
		return "%02d";
	else if (zone < 1000)
		return "%03d";
	else
		return "%d";
}

void hbSum(uint8_t* data, size_t len) {
	uint8_t sum = 0;
	uint8_t* p = data;
	while (p != data + len - 1)
		sum += *p++;
	*p = sum;
}

int hbCheckSum(const uint8_t* data, size_t len) {
	uint8_t sum = 0;
	const uint8_t* p = data;
	while (p != data + len - 1)
		sum += *p++;
	return sum == *p;
}

#define copy_data_to_com                    \
    if (cd) {                               \
        cd->len = len & 0xFF;               \
        memcpy(cd->data, data, len & 0xFF); \
    }

HbComRequestType hbComParseRequest(const uint8_t* data, size_t len, HbComData* cd) {
	do {
		if (len < 5) { break; }
		if (data[0] != 0xEB) { break; }
		switch (data[1]) {
		case 0xAB:
		{
			if (data[2] != 0x3F) { break; }
			switch (data[3]) {
			case 0xA0: // EB AB 3F A0 75
			{
				if (len != HbComReq_A0_len) { break; }
				if (memcmp(HbComReq_A0_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_A0;
			}
			case 0xA1: // EB AB 3F A1 76
			{
				if (len != HbComReq_A1_len) { break; }
				if (memcmp(HbComReq_A1_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_A1;
			}
			case 0xA2: // EB AB 3F A2 77
			{
				if (len != HbComReq_A2_len) { break; }
				if (memcmp(HbComReq_A2_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_A2;
			}
			case 0xA5: // EB AB 3F A5 7A
			{
				if (len != HbComReq_A5_len) { break; }
				if (memcmp(HbComReq_A5_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_A5;
			}
			case 0xAC: // EB AB 3F AC 81
			{
				if (len != HbComReq_AC_len) { break; }
				if (memcmp(HbComReq_AC_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_AC;
			}
			case 0xAD: // EB AB 3F AD 82
			{
				if (len != HbComReq_AD_len) { break; }
				if (memcmp(HbComReq_AD_data, data, len) != 0) { break; }
				copy_data_to_com;
				return HbComReq_AD;
			}
			default:
				break;
			}
			break;
		}
		case 0xBA:
		{
			if (data[2] != 0x3F) { break; }
			if (len == HbComReq_RD_acct_len &&
				memcmp(data, HbComReq_RD_acct_data, len) == 0) {
				copy_data_to_com;
				return HbComReq_RD_acct;
			}
			break;
		}
		case 0xCB:
		{
			if (data[2] != 0x3F) { break; }

			if (data[3] == 0x09 &&
				data[4] == 0xA3 &&
				len == HbComReq_A3_len) { // EB CB 3F 09 A3 P1 P2 P3 SUM
				if (hbCheckSum(data, len)) {
					copy_data_to_com;
					return HbComReq_A3;
				}
			} else if (data[3] == 0x0F &&
					   data[4] == 0x4D &&
					   len == HbComReq_WR_acct_len) {
				if (hbCheckSum(data, len)) {
					copy_data_to_com;
					return HbComReq_WR_acct;
				}
			} else if (data[3] == 0x0E &&
					   data[4] == 0xA7 &&
					   len == HbComReq_A7_len) { // EB CB 3F 0E A7 H1 M1 H2 M2 H3 M3 H4 M4 SUM
				if (hbCheckSum(data, len)) {
					copy_data_to_com;
					return HbComReq_A7;
				}
			}

			/*else if (data[3] == 0x08 && data[4] == 0xA9 && len == A9_len) {
				A7 req; memcpy(req.data, data, req.len); sum(req);
				if (data[len - 1] == req.data[len - 1]) { return A7; }
			}*/

			else if (data[3] == 0x08 &&
					 data[4] == 0xAA &&
					 len == HbComReq_AA_len) { // EB CB 3F 08 AA P1 P2 SUM
				if (hbCheckSum(data, len)) {
					copy_data_to_com;
					return HbComReq_AA;
				}
			}

			/*else if (data[3] == 0x08 && data[4] == 0xAE && len == AE_len) {
				AA req; memcpy(req.data, data, req.len); sum(req);
				if (data[len - 1] == req.data[len - 1]) { return AA; }
			}*/

			else if (data[3] == 0x06 && data[4] == 0xB0 &&
					 len == HbComReq_B0_len &&
					 memcmp(HbComReq_B0_data, data, len) == 0) { // EB CB 3F 06 B0 AB
				copy_data_to_com;
				return HbComReq_B0;
			}
		}
		}
	} while (0);
	return HbComReq_Invalid;
}

ADEMCO_EXPORT_SYMBOL HbComRequestType hbComParseXDataRequest(const AdemcoXDataSegment* xdata,
															 HbComData* cd) {
	if (!xdata) { return HbComReq_Invalid; }
	return hbComParseRequest((const uint8_t*)ademcoXDataGetValidContentAddr(xdata),
							 ademcoXDataGetValidContentLen(xdata), cd);
}

HbComResponseType hbComParseResponse(const uint8_t* data, size_t len, HbComData* cd) {
	do {
		if (len < 7) { break; } // 所有的 response ,长度最小为 7
		if (data[0] != 0xEB || data[1] != 0xBA || data[2] != 0x3F) { break; }
		switch (data[5]) {
		case 0xA0: // EB BA 3F 07 P0 A0 P1 P2 P3 SUM
		{
			if (len != HbComResp_A0_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A0;
			}
			break;
		}

		case 0xA2: // EB BA 3F PN P0 A2 [Z, P]xN P1 SUM
		{
			if (len != data[3]) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A2;
			}
			break;
		}

		case 0xA3:
		{
			if (len != HbComResp_A3_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A3;
			}
			break;
		}

		case 0xA4:
		{
			if (len != HbComResp_A4_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A4;
			}
			break;
		}

		case 0xA6:
		{
			if (len != HbComResp_A6_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A6;
			}
			break;
		}

		case 0xA7:
		{
			if (len != HbComResp_A7_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A7;
			}
			break;
		}

		case 0xA8:
		{
			if (len != HbComResp_A8_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_A8;
			}
			break;
		}

		case 0xA9:
		{
			// TODO
			break;
		}

		case 0xAB:
		{
			if (len != HbComResp_AB_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_AB;
			}
			break;
		}

		case 0xAD: // EB BA 3F PN P0 AD P1 DATA P2 SUM
		{
			if (len != data[3]) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_AD;
			}
			break;
		}

		case 0xAF: // TODO
		{

			break;
		}

		case 0xB1: // EB BA 3F 08 P0 B1 P1 SUM
		{
			if (len != HbComResp_B1_len) { break; }
			if (hbCheckSum(data, len)) {
				copy_data_to_com;
				return HbComResp_B1;
			}
			break;
		}

		}
	} while (0);
	return HbComResp_Invalid;
}

HbComResponseType hbComParseXDataResponse(const AdemcoXDataSegment* xdata,
										  HbComData* cd) {
	if (!xdata) { return HbComResp_Invalid; }
	return hbComParseResponse((const uint8_t*)ademcoXDataGetValidContentAddr(xdata),
							  ademcoXDataGetValidContentLen(xdata), cd);
}

void hbComMakeReqA0_getMachineStatus(HbComData* data) {
	memcpy(data->data, HbComReq_A0_data, HbComReq_A0_len);
	data->len = HbComReq_A0_len;
}

void hbComMakeReqA1_getMachineZones(HbComData* data) {
	memcpy(data->data, HbComReq_A1_data, HbComReq_A1_len);
	data->len = HbComReq_A1_len;
}

void hbComMakeReqA2_getMoreMachineZones(HbComData* data) {
	memcpy(data->data, HbComReq_A2_data, HbComReq_A2_len);
	data->len = HbComReq_A2_len;
}

void hbComMakeReqA3_modifyMachineZone(HbComData* data, uint8_t zone,
									  HbZoneProperty prop, HbComReq_A3_op op) {
	memcpy(data->data, HbComReq_A3_head, 5);
	data->data[5] = zone;
	data->data[6] = prop;
	data->data[7] = op;
	data->len = HbComReq_A3_len;
	hbSum(data->data, data->len);
}

void hbComMakeReqA5_getMachineTimer(HbComData* data) {
	memcpy(data->data, HbComReq_A5_data, HbComReq_A5_len);
	data->len = HbComReq_A5_len;
}

void hbComMakeReqA7_setMachineTimer(HbComData* data, HbMachineTimer* timer) {
	memcpy(data->data, HbComReq_A7_head, 5);
	memcpy(data->data + 5, timer, sizeof(*timer));
	data->len = HbComReq_A7_len;
	hbSum(data->data, data->len);
}

void hbComMakeReqAA_modifyMachineZoneLostConfig(HbComData* data, uint8_t zone, int on) {
	memcpy(data->data, HbComReq_AA_head, 5);
	data->data[5] = zone;
	data->data[6] = !!on;
	data->len = HbComReq_AA_len;
	hbSum(data->data, data->len);
}

void hbComMakeReqAC_getMachineZoneLostConfig(HbComData* data) {
	memcpy(data->data, HbComReq_AC_data, HbComReq_AC_len);
	data->len = HbComReq_AC_len;
}

void hbComMakeReqAD_getMoreMachineZoneLostConfig(HbComData* data) {
	memcpy(data->data, HbComReq_AD_data, HbComReq_AD_len);
	data->len = HbComReq_AD_len;
}

void hbComMakeReqAE_set3SectionMachineStatus(HbComData* data, HbCom_3sec_which p1,
											 HbCom_3sec_status status) {
	memcpy(data->data, HbComReq_AE_head, 5);
	data->data[5] = p1;
	data->data[6] = status;
	data->len = HbComReq_AE_len;
	hbSum(data->data, data->len);
}

void hbComMakeReqB0_get3SectionMachineStatus(HbComData* data) {
	memcpy(data->data, HbComReq_B0_data, HbComReq_B0_len);
	data->len = HbComReq_B0_len;
}

void hbComMakeReqRD_acct(HbComData* data) {
	memcpy(data->data, HbComReq_RD_acct_data, HbComReq_RD_acct_len);
	data->len = HbComReq_RD_acct_len;
}

void hbComMakeReqWR_acct(HbComData* data, const char* acct) {
	memcpy(data->data, HbComReq_WR_acct_head, 5);
	ademcoDecStrToHiLoArray(data->data + 5, 9, acct);
	data->len = HbComReq_WR_acct_len;
	hbSum(data->data, data->len);
}

void hbComResp_A2_Iter_init(HbComResp_A2_Iter* iter, const HbComData* com) {
	if (!iter || !com)return;
	memcpy(&iter->com, com, sizeof(HbComData));
	iter->i = 0;
	if (com->len > HbComResp_A2_len_min) {
		iter->total = (com->len - HbComResp_A2_len_min) / 2;
		iter->p1 = (com->data[com->len - 2] == 0xFF) ?
			HbComResp_A2_p1_nomore :
			HbComResp_A2_p1_more;
	} else {
		iter->total = 0;
		iter->p1 = HbComResp_A2_p1_nomore;
	}
}

HbComResp_A2_p1 hbComResp_A2_Iter_next(HbComResp_A2_Iter* iter, HbZoneAndProperty* zp) {
	if (iter->i == iter->total)
		return HbComResp_A2_p1_nomore;
	zp->zone = iter->com.data[6 + iter->i * 2];
	zp->prop = (HbZoneProperty)iter->com.data[7 + iter->i * 2];
	iter->i++;
	return HbComResp_A2_p1_more;
}

void hbComMakeRespA0_getMachineStatus(HbComData* data, HbMachineStatus status,
									  HbMachineType type) {
	memcpy(data->data, HbComResp_A0_head, 6);
	data->data[6] = status;
	data->data[7] = type;
	data->len = HbComResp_A0_len;
	hbSum(data->data, data->len);
}

void hbComMakeRespA2_getMachineZones(HbComData* data, size_t count, AdemcoZone* zones,
									 HbZoneProperty* props, HbComResp_A2_p1 p1) {
	if (count > HbComResp_A2_max_zone) { return; }
	memcpy(data->data, HbComResp_A2_head, 6);
	data->data[3] = (HbComResp_A2_len_min + count * 2) & 0xFF;
	uint8_t* p = data->data + 6;
	for (size_t i = 0; i < count; i++) {
		*p++ = zones[i] & 0xFF;
		*p++ = props[i];
	}
	*p++ = (count > 0) ? p1 : HbComResp_A2_p1_nomore;
	data->len = (++p - data->data) & 0xFF;
	hbSum(data->data, data->len);
}

void hbComMakeRespA3_waitingSignal(HbComData* data) {
	memcpy(data->data, HbComResp_A3_data, HbComResp_A3_len);
	data->len = HbComResp_A3_len;
}

void hbComMakeRespA4_modifyMachineZone(HbComData* data, AdemcoZone zone, 
									   HbZoneProperty prop, HbComResp_A4_p3 p3) {
	memcpy(data->data, HbComResp_A4_head, 6);
	data->data[6] = zone & 0xFF;
	data->data[7] = prop;
	data->data[8] = p3;
	data->len = HbComResp_A4_len;
	hbSum(data->data, data->len);
}

void hbComMakeRespA6_getMachineTimer(HbComData* data, HbMachineTimer* timer) {
	memcpy(data->data, HbComResp_A6_head, 6);
	memcpy(data->data + 6, timer->data, sizeof(*timer));
	data->len = HbComResp_A6_len;
	hbSum(data->data, data->len);
}

void hbComMakeRespA7_setMachineTimer(HbComData* data) {
	memcpy(data->data, HbComResp_A7_data, HbComResp_A7_len);
	data->len = HbComResp_A7_len;
}

void hbComMakeRespA8_reject(HbComData* data) {
	memcpy(data->data, HbComResp_A8_data, HbComResp_A8_len);
	data->len = HbComResp_A8_len;
}

void hbComMakeRespAB_modifyMachineZoneLostConfig(HbComData* data, AdemcoZone zone,
												 HbComResp_AB_p2 p2) {
	memcpy(data->data, HbComResp_AB_head, 6);
	data->data[6] = zone & 0xFF;
	data->data[7] = p2;
	data->len = HbComResp_AB_len;
	hbSum(data->data, data->len);
}

void hbComMakeRespAD_getMachineZoneLostConfig(HbComData* data, HbComResp_AD_p1 p1, 
											  size_t count, AdemcoZone* zones,
											  HbComResp_AD_p2 p2) {
	if (count > HbComResp_AD_max_zone) { return; }
	memcpy(data->data, HbComResp_AD_head, 6);
	data->data[3] = (HbComResp_AD_len_min + (p1 == HbComResp_AD_p1_single ? count : count * 2)) & 0xFF;
	data->data[6] = p1;
	uint8_t* p = data->data + 7;
	for (size_t i = 0; i < count; i++) {
		if (p1 == HbComResp_AD_p1_single) {
			*p++ = zones[i] & 0xFF;
		} else {
			*p++ = zones[i] >> 8 & 0xFF;
			*p++ = zones[i] & 0xFF;
		}
	}
	*p++ = (count > 0) ? p2 : HbComResp_AD_p2_nomore;
	data->len = (++p - data->data) & 0xFF;
	hbSum(data->data, data->len);
}

void hbComMakeRespAF_set3SectionMachineStatus(HbComData* data, HbCom_3sec_which p1,
											  HbCom_3sec_status status) {
	memcpy(data->data, HbComResp_AF_head, 6);
	data->data[6] = p1;
	data->data[7] = status;
	data->len = HbComResp_AF_len;
	hbSum(data->data, data->len);
}

void hbComMakeRespB1_get3SectionMachineStatus(HbComData* data,
											  HbMachineStatus statusMachine,
											  HbMachineStatus statusSec1,
											  HbMachineStatus statusSec2,
											  HbMachineStatus statusSec3) {
	memcpy(data->data, HbComResp_B1_head, 6);
	data->data[6] = (statusMachine << 6) | (statusSec1 << 4) | (statusSec2 << 2) | (statusSec3);
	data->len = HbComResp_B1_len;
	hbSum(data->data, data->len);
}

void hbComDataToAdemcoXData(const HbComData* const data, AdemcoXDataSegment* xdata,
							AdemcoXDataLengthFormat xlf, AdemcoXDataTransform xtr) {
	ademcoMakeXData(xdata, xlf, xtr, (const char*)data->data, data->len);
}