/**********************************************************************
 * INCLUDES
 */
#include "ikk-cluster.h"
#include "ikk-debug.h"
#include "ikk-config.h"
#include "ikk-network.h"
#include "ikk-token.h"
#include "zcl.h"
#include PROJECT_H



#define ENDPOINT_MAXN										5
#define CLUSTER_MAXN										5


static uint8_t g_tmp_buffer[64] = { 0 };

pOnOffClusterOnOffStatusCallback g_pfOnOffStatusCallback = NULL;

static pClusterAttributeChangeCallback pOnOffClusterCallback = NULL;
//static pClusterAttributeChangeCallback pLevelClusterCallback = NULL;
//static pClusterAttributeChangeCallback pColorClusterCallback = NULL;

static status_t WriteServerAttribute(u8 endpoint, u16 clusterId,uint8_t attributeID,uint8_t* dataPtr, uint8_t data_type);
typedef struct {
	ZclReportTableSt g_stZclReportPermitList[ENDPOINT_MAXN * CLUSTER_MAXN];
	uint8_t size;
}ZclClusterReportTableSt;


static ZclClusterReportTableSt g_stZclClusterReportPermitTable;

void kZclOnOffClusterServerOnOffSet(uint8_t endpoint,bool onoff)
{
	WriteServerAttribute(endpoint,CLUSTER_ONOFF_ID,ZCL_ATTRID_ONOFF,(uint8_t*)&onoff,ZCL_DATA_TYPE_BOOLEAN);
//	zcl_onOffAttr_t *pOnOff = zcl_onoffAttrGet(endpoint);
//
//	pOnOff->onOff = onoff;
	zcl_onOffAttr_save();
}
/*	DESP: on-off cluster control operate.
 * 	Auth: dingmz_frc.20191112.
 * */
kk_err_t kZclOnOffClusterServerOnOffControl(uint8_t endpoint, OnOffCtrlEnum ctrlopt )
{
	bool setValue = kZclOnOffClusterServerOnOffGet(endpoint);
	iKonkeAfSelfPrint("OnOffControl: endpoint(%d), opt(%d),last state = %d\r\n", endpoint, ctrlopt,setValue);
	if(ctrlopt == EOOC_TOGGLE)
		setValue = !setValue;
	else if(ctrlopt == EOOC_ON)
		setValue = 1;
	else
		setValue = 0;
	kZclOnOffClusterServerOnOffSet(endpoint,setValue);
	if( kNwkGetCurrentStatus() == ENS_ONLINE ) {
		// check binding table
		kk_err_t err = kNwkClusterBindingObjIsOK(endpoint, CLUSTER_ONOFF_ID);
		if( err == KET_ERR_NON_EXIST ) {
			kNwkClusterBindingTrigger();
		}
	}

	return KET_OK;
}
bool kZclOnOffClusterServerOnOffGet(uint8_t endpoint)
{
	status_t status ;
	zclAttrInfo_t *pOnOff = zcl_attrRead(endpoint,CLUSTER_ONOFF_ID,ZCL_ATTRID_ONOFF,&status);
	return (status == ZCL_STA_SUCCESS )? (pOnOff->data[0]): (0);
}


static status_t WriteServerAttribute(u8 endpoint, u16 clusterId,uint8_t attributeID,uint8_t* dataPtr, uint8_t data_type)
{
	zclWriteRec_t  attr_data;
	attr_data.attrID 	= attributeID;
	attr_data.dataType 	= data_type;
	u16 dataLen = zcl_getAttrSize(data_type, dataPtr);
	attr_data.attrData = dataPtr;

	iKonkeAfSelfPrint("##WriteServerAttribute cluster =%d,attr = %d,len=%d :\r\n",clusterId,attr_data.attrID,dataLen);
	if(dataLen == 1)
		iKonkeAfSelfPrint("##data=%d:\r\n",*dataPtr);
	else
		iKonkeAfSelfPrintBuffer(dataPtr,dataLen);
	return zcl_attrWrite(endpoint,clusterId,&attr_data,true);
}
bool  Get_Model_ID(uint8_t *ModeID)
{
	status_t status ;
	zclAttrInfo_t *pAttrInfo = zcl_attrRead(1,ZCL_CLUSTER_GEN_BASIC,ZCL_ATTRID_BASIC_MODEL_ID,&status);
	if(status == ZCL_STA_SUCCESS )
	{
		uint8_t len = pAttrInfo->data[0];
		if((ModeID)&&(len >0)&&(pAttrInfo->type == ZCL_DATA_TYPE_CHAR_STR))
		{
			uint8_t  temp[16] = {0};
			memcpy(ModeID,&pAttrInfo->data[1],len);			//Get ASCII Data
			if((len >0)&&(len %2 == 0))
			{
				kUtilsStr2Hex(ModeID,temp,len);
				memcpy(ModeID,temp,len/2);
				iKonkeAfSelfPrint("##Trans Data Buffer Len =%d:\r\n",len);
				iKonkeAfSelfPrintBuffer(ModeID,8);
				return true;
			}
		}
	}
	return false;
}



static status_t  Rewrite_Modeid(uint8_t *modeid,uint8_t software,uint8_t hardware)
{
	uint8_t      Modeid_Temp[] = "3AFE101003008611" ;
	uint8_t      temp[6];
	uint8_t      dest_temp[20];
	status_t 	 status = ZCL_STA_SUCCESS;

	temp[0]     = software;
	temp[1]     = hardware;
	temp[2]     = modeid[3];
	temp[3]     = modeid[2];
	temp[4]     = modeid[1];
	temp[5]     = modeid[0];

	for(uint8_t i = 0;i<6;i++)
	{
		uint8_t high_byte = (temp[i]>>4)&0x0f;
		uint8_t low_byte  = temp[i]&0x0f;
		if(  ( high_byte >= 0)&&( high_byte <= 9) )
		{
			dest_temp[2*i] = high_byte +'0';
		}
		else
		{
			dest_temp[2*i] = high_byte -10 +'A';
		}

		if(  (low_byte >= 0)&&(low_byte <= 9) )
		{
			dest_temp[2*i +1] = low_byte +'0';
		}
		else
		{
			dest_temp[2*i +1] = low_byte -10 +'A';
		}
	}
	memcpy(&Modeid_Temp[4],dest_temp,12);
	memmove(&g_tmp_buffer[1],Modeid_Temp,16);										//重写初始化值
	g_tmp_buffer[0] = 16;
	g_tmp_buffer[0+ 2+16] = '0';

	uint8_t *data = MODEL_ID_TOKEN_GET();
	if(!All_Same_Data(&data[1],0xff,32))
		memcpy(g_tmp_buffer,data,data[0]+1);

	status = WriteServerAttribute(1, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_MODEL_ID ,(uint8_t*)g_tmp_buffer,ZCL_DATA_TYPE_CHAR_STR);
	if(status != ZCL_STA_SUCCESS)
	{
		iKonkeAfSelfPrint("write modeidid failed status = %d :\r\n",status);
		return status;
	}
	status = WriteServerAttribute(1, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_APP_VER ,(uint8_t*)&software,ZCL_DATA_TYPE_UINT8);
	if(status != ZCL_STA_SUCCESS)
	{
		iKonkeAfSelfPrint("write app version failed status = %d :\r\n",status);
		return status;
	}
	//iKonkeAfSelfPrint("write app version status = %d :\r\n",status);
	status = WriteServerAttribute(1, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_HW_VER ,(uint8_t*)&hardware,ZCL_DATA_TYPE_UINT8);
	if(status != ZCL_STA_SUCCESS)
	{
		iKonkeAfSelfPrint("write hard version failed status = %d :\r\n",status);
		return status;
	}
	return status;
}
static status_t Rewrite_ManufactureName(void)
{
	uint8_t manuName[] = MANUFATURE_NAME;
	g_tmp_buffer[0] = strlen((const char *)manuName);
	strcpy((char*)&g_tmp_buffer[1],(const char*)manuName);
	status_t 	 status = ZCL_STA_SUCCESS;
	status =WriteServerAttribute(1, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_MFR_NAME ,(uint8_t*)g_tmp_buffer,ZCL_DATA_TYPE_CHAR_STR);
	if(status != ZCL_STA_SUCCESS)
	{
		iKonkeAfSelfPrint("write mfg name failed status = %d :\r\n",status);
		return status;
	}
	return status;
}

void Update_Local_Attribute_Info(void)
{
	uint32_t modeid = (uint32_t)MODEL_ID;
	Rewrite_Modeid((uint8_t *)&modeid,FIRMWARE_VER,HARDWARE_VERSION);
	Rewrite_ManufactureName();
}

typedef struct{
	zcl_basicAttr_t 	basicAttrs_temp;
	zcl_identifyAttr_t  identifyAttrs_temp;
	zcl_onOffAttr_t 	onOffAttrs_temp;
	zcl_levelAttr_t 	levelAttrs_temp;
}AttrListST;
static AttrListST  Attr_List_Value[MAX_ATTR_CHANGE_SUPPORT_NUMS] ;
void ikkAttributeChangeCallbackRegister(AttributeChangeConfSt *ConfSt,uint8_t num)
{
	for(uint8_t i=0;i<MAX_ATTR_CHANGE_SUPPORT_NUMS;i++)
	{
		if(i > MAX_ATTR_CHANGE_SUPPORT_NUMS)
		{
			iKonkeAfSelfPrint("attribute change callback totals number beyond!!\r\n");
			break;
		}
		switch(ConfSt->cluster)
		{
			case ZCL_CLUSTER_GEN_ON_OFF:
			{
				pOnOffClusterCallback =  ConfSt->change_callback;
			}
				break;
//			case ZCL_CLUSTER_GEN_LEVEL_CONTROL:
//			{
//				pLevelClusterCallback =  ConfSt->change_callback;
//			}
//				break;
			default:
				break;
		}
	}
}

void AttributeValueCheckCallback(void)
{
	for(uint8_t i = 0;i<Support_Endpoint_Num;i++)
	{
		if(memcmp((uint8_t*)&Attr_List_Value[i].onOffAttrs_temp,(uint8_t*)&g_zcl_onOffAttrs[i],sizeof(zcl_onOffAttr_t)) )
		{
			if(Attr_List_Value[i].onOffAttrs_temp.onOff != g_zcl_onOffAttrs[i].onOff)
			{
				if(pOnOffClusterCallback)
					pOnOffClusterCallback(i+1,ZCL_CLUSTER_GEN_ON_OFF,ZCL_ATTRID_ONOFF,(uint8_t *)&g_zcl_onOffAttrs[i].onOff,ZCL_DATA_TYPE_BOOLEAN);
			}
			memcpy((uint8_t*)&Attr_List_Value[i].onOffAttrs_temp,(uint8_t*)&g_zcl_onOffAttrs[i],sizeof(zcl_onOffAttr_t));
		}
	}
}


bool kIsKonkeRemoteGateway(void)
{
	return MFG_Code_Get() == MANUFACTURER_CODE_TELINK;
}


bool kkClusterGetReportingPeriod(uint8_t endpoint, uint16_t cluster_id, uint16_t attribute_id,uint16_t *min_interval, uint16_t *max_interval )
{
	uint8_t index = 0;

	for(index = 0; index < ZCL_REPORTING_TABLE_NUM; index++){
		reportCfgInfo_t *pEntry = &reportingTab.reportCfgInfo[index];
		if( pEntry->used&&
			pEntry->endPoint== endpoint &&
			pEntry->clusterID == cluster_id &&
			pEntry->attrID== attribute_id )
		{
			*min_interval = pEntry->minInterval;
			*max_interval = pEntry->maxInterval;
			return true;
		}
	}
	return false;
}
//table init
void kZclClusterPermitReportTableInit(BindObjSt clusterBindList[], uint8_t size)
{
	if (clusterBindList == NULL || size == 0 || size >= ENDPOINT_MAXN * CLUSTER_MAXN){
		iKonkeAfSelfPrint("###clusterBindList is invalid size(%d)\r\n", size);
		return;
	}

	for (uint8_t i = 0; i < size; i++){
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].endpoint = clusterBindList[i].endpoint;
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].cluster = clusterBindList[i].cluster;
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].reportEnable = false;
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].sceneRecallFlg = false;
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].reportGatewayEnable = false;
		g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].reportChildDeviceEnable = false;
	}
	g_stZclClusterReportPermitTable.size = size;
}


//index get
static uint8_t kZclClusterPermitTableIndexGetByClusterAndEp(uint8_t endpoint, uint16_t cluster)
{
	uint8_t index = 0xFF;
	for (uint8_t i = 0; i < g_stZclClusterReportPermitTable.size; i++){
		if (g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].endpoint == endpoint \
				&& g_stZclClusterReportPermitTable.g_stZclReportPermitList[i].cluster == cluster){
			index = i;
			break;
		}
	}
	return index;
}
//permit info get
bool kZclClusterGetPermitReportInfo(uint8_t endpoint, uint16_t cluster, ZclReportTableSt *zclReportPermit)
{
	if (endpoint > ENDPOINT_MAXN){
		iKonkeAfSelfPrint("###11Endpoint %d is too large\r\n", endpoint);
		return false;
	}
	uint8_t index = kZclClusterPermitTableIndexGetByClusterAndEp(endpoint , cluster);
	if (index != 0xFF){
		zclReportPermit->endpoint = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].endpoint;
		zclReportPermit->cluster = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].cluster;
		zclReportPermit->reportEnable = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportEnable;
		zclReportPermit->sceneRecallFlg = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].sceneRecallFlg;
		zclReportPermit->reportGatewayEnable = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportGatewayEnable;
		zclReportPermit->reportChildDeviceEnable = g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportChildDeviceEnable;
		return true;
	}

	return false;
}
//permit info set
bool kZclClusterSetPermitReportInfo(uint8_t endpoint, uint16_t cluster, bool reportEnable, bool sceneRecallFlg, bool reportGatewayEnable, bool reportChildDeviceEnable, uint8_t *maskEui64Addr,uint8_t dest_endpoint)
{
	bool ret = false;

	if (endpoint > ENDPOINT_MAXN && endpoint != 0xFF){
		iKonkeAfSelfPrint("###22Endpoint %d is too large\r\n", endpoint);
		return false;
	}

	uint8_t index = 0;
	if (endpoint != 0xFF){
		index = kZclClusterPermitTableIndexGetByClusterAndEp(endpoint , cluster);
		if (index != 0xFF){
			g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportEnable = reportEnable;
			g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].sceneRecallFlg = sceneRecallFlg;
			g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportGatewayEnable = reportGatewayEnable;
			g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportChildDeviceEnable = reportChildDeviceEnable;
			if (maskEui64Addr != NULL){
				memcpy(g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].maskEui64Addr, maskEui64Addr, 8);
			} else {
				memset(g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].maskEui64Addr, 0xFF, 8);
			}
			g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].dest_endpoint = dest_endpoint;
			ret =  true;
		}
	}else { //广播
		for (uint8_t i = 0; i < g_stZclClusterReportPermitTable.size; i++){
			for (uint8_t j = 1; j < ENDPOINT_MAXN; j++){
				index = kZclClusterPermitTableIndexGetByClusterAndEp(j , cluster);
				if (index != 0xFF){
					g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportEnable = reportEnable;
					g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].sceneRecallFlg = sceneRecallFlg;
					g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportGatewayEnable = reportGatewayEnable;
					g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].reportChildDeviceEnable = reportChildDeviceEnable;
					if (maskEui64Addr != NULL){
						memcpy(g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].maskEui64Addr, maskEui64Addr, 8);
					} else {
						memset(g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].maskEui64Addr, 0xFF, 8);
					}
					g_stZclClusterReportPermitTable.g_stZclReportPermitList[index].dest_endpoint = dest_endpoint;
					ret = true;
				}
			}
		}
	}

	iKonkeAfSelfPrint("######kZclClusterSetPermitReportInfo index(%d)ep(%d) cluster(%x)enable(%d)scene(%d)\r\n",
			index, endpoint, cluster, reportEnable, sceneRecallFlg );
	return ret;
}

kk_err_t kZclOnOffClusterServerInit(pOnOffClusterOnOffStatusCallback pOnOffStatusCallback )
{
	g_pfOnOffStatusCallback = pOnOffStatusCallback;

	return KET_OK;
}

void ON_OFF_Cmd_Handler(uint8_t endpoint,bool onoff)
{
	OnOffStatusEnum on_off_ctr = (OnOffStatusEnum)onoff;
	kZclClusterSetPermitReportInfo(endpoint,ZCL_CLUSTER_GEN_ON_OFF,true,false,true,true,NULL,RESET_ENDPOINT);
	kZclOnOffClusterServerOnOffControl(endpoint, on_off_ctr);
}

/*	DESP: Modified attribute reporting interface, reference from SDK callback[emberAfReportingAttributeChangeCallback].
 * 	Auth: dingmz_frc.20190701.
 * */
void kOptTunnelReportingPlagiarizeOriginal(uint8_t endpoint, uint16_t clusterId, uint16_t attributeId )
{
	if(!zb_bindingTblSearched(clusterId,endpoint)){
		return;
	}
	iKonkeAfSelfPrint("######kOptTunnelReportingPlagiarizeOriginal111111\r\n");
	for(u8 i = 0; i < ZCL_REPORTING_TABLE_NUM; i++){
		reportCfgInfo_t *pEntry = &reportingTab.reportCfgInfo[i];
		if( pEntry->endPoint == endpoint
			&& pEntry->clusterID == clusterId
			&& pEntry->attrID == attributeId) {
			epInfo_t dstEpInfo;
			TL_SETSTRUCTCONTENT(dstEpInfo, 0);

			dstEpInfo.dstAddrMode = APS_DSTADDR_EP_NOTPRESETNT;
			dstEpInfo.profileId = pEntry->profileID;
			zclAttrInfo_t *pAttrEntry = zcl_findAttribute(pEntry->endPoint, pEntry->clusterID, pEntry->attrID);
			if(!pAttrEntry){
				//should not happen.
				ZB_EXCEPTION_POST(SYS_EXCEPTTION_ZB_ZCL_ENTRY);
				return;
			}

			u16 len = zcl_getAttrSize(pAttrEntry->type, pAttrEntry->data);

			len = (len>8) ? (8):(len);

			//store for next compare
			memcpy(pEntry->prevData, pAttrEntry->data, len);

			zcl_sendReportCmd(pEntry->endPoint, &dstEpInfo,  TRUE, ZCL_FRAME_SERVER_CLIENT_DIR,
							  pEntry->clusterID, pAttrEntry->id, pAttrEntry->type, pAttrEntry->data);
		}
	}
}

void kOptTunnelReportingStatus(uint8_t endpoint, uint16_t clusterId, uint16_t attributeId )
{
	if(!zb_bindingTblSearched(clusterId,endpoint)){
		return;
	}
	iKonkeAfSelfPrint("######kOptTunnelReportingStatus111111\r\n");
	for(u8 i = 0; i < ZCL_REPORTING_TABLE_NUM; i++){
		reportCfgInfo_t *pEntry = &reportingTab.reportCfgInfo[i];
		if( pEntry->endPoint == endpoint
			&& pEntry->clusterID == clusterId
			&& pEntry->attrID == attributeId) {
			epInfo_t dstEpInfo;
			TL_SETSTRUCTCONTENT(dstEpInfo, 0);

			dstEpInfo.dstAddrMode = APS_SHORT_DSTADDR_WITHEP;
			dstEpInfo.dstEp = 0x01;
			dstEpInfo.dstAddr.shortAddr = 0x0000;
			dstEpInfo.profileId = pEntry->profileID;
			dstEpInfo.txOptions	= 0;
			dstEpInfo.radius = 0;

			zclAttrInfo_t *pAttrEntry = zcl_findAttribute(pEntry->endPoint, pEntry->clusterID, pEntry->attrID);
			if(!pAttrEntry){
				//should not happen.
				ZB_EXCEPTION_POST(SYS_EXCEPTTION_ZB_ZCL_ENTRY);
				return;
			}
			u16 len = zcl_getAttrSize(pAttrEntry->type, pAttrEntry->data);

			len = (len>8) ? (8):(len);

			//store for next compare
			memcpy(pEntry->prevData, pAttrEntry->data, len);
			zcl_sendReportCmd(pEntry->endPoint, &dstEpInfo,  TRUE, ZCL_FRAME_SERVER_CLIENT_DIR,
							  pEntry->clusterID, pAttrEntry->id, pAttrEntry->type, pAttrEntry->data);
		}
	}
}
