#include "ikk-network.h"
#include "../driver/ikk-led.h"
#include "../driver/ikk-button.h"
#include "ikk-tick-handler.h"
#include "ikk-token.h"
#include "tl_common.h"
#include "../../../zigbee/aps/aps_api.h"
#include "../../../zigbee/common/includes/zb_common.h"
/**********************************************************************
 * INCLUDES
 */

#define 	DELAY_RESTORE_NWK_STEERING_TIME_MS			(3 * 1000)

#define		DELAY_NWK_DETECT_POWER_ON_TIME_MS			(3 * 1000)

//global value
addrExt_t g_Eui64GatewayAddr = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

BindObjSt 	g_lstBindingClusterConf[BINDING_CLUSTER_MAXN] = { {0},{0},{0},{0},{0},{0},{0},{0} };


//static value
static bool 		g_bIsNwkJoiningFlg 							= false;

static uint32_t 	g_u32NwkExitCountDownCounter 				= 0;

static uint32_t 	g_u32NwkDetectCountDownCounter 				= 0;

static uint32_t g_u32NwkJoiningCountdownCounter  				= 0;

// copy current network status.
static NwkStatusEnum g_eCurrentNetworkStatus 					= ENS_UNKNOW;
//add by maozj 20200225
static NwkStatusEnum g_eLastNetworkStatus 						= ENS_UNKNOW;

static NwkSteeringStatusEnum NetworkSteeringState 				= NWK_STEERING_STATUS_FAILED;
//joining scan channel 1 round failed flag
static bool g_bNwkSteeringCompleteFlg = false;

static int g_u32BindingResultCheckCounter = 0;


// network status notify callback define.
pNetworkStatusNotifyCallback g_pNetworkStatusNotifyCallback = NULL;
//add bby maozj 20200511
//network exit complete callback define
pNetworkExitCompleteCallback g_pNetworkExitCompleteCallback = NULL;

pJoinCompleteCallback g_pJoinCompleteCallback = NULL;


/*Get Steerint Nwk State*/
static NwkSteeringStatusEnum Get_Nwk_Steering_Status(void)
{
	return NetworkSteeringState;
}

void Set2NwkJoiningCountdownCounter(u32 time_ms)
{
	g_u32NwkJoiningCountdownCounter = MS2COUNT(time_ms);
}
bool IsSteeringNwkTimeout(void)
{
	return (g_u32NwkJoiningCountdownCounter == 0);
}
/*Set Steerint Nwk State*/
void Set_Nwk_Steering_Status(NwkSteeringStatusEnum Status)
{
	NetworkSteeringState = Status;
}
/*	DESP: 高精度随机值接口，取值[minNum, maxNum].
	Auth: dingmz_frc.20190701.
*/
uint32_t kGetRandNum(uint32_t minNum, uint32_t maxNum )
{
	if( minNum >= maxNum ) {
		return 0;
	}
	//halCommonGetInt32uMillisecondTick();

	uint32_t randNum  =  minNum + drv_u32Rand() % (maxNum - minNum + 1);

	iKonkeAfSelfPrint(" xxx rand num = %d\r\n", randNum);

	return randNum;
}
/*	DESP: Check whether the specified cluster is binded.
 * 	RETURN[KET_OK]: is binded!
 * 	RETURN[KET_ERR_NON_EXIST]: is unbinded!
 * 	RETURN[KET_ERR_NO_PERMISSIONS]: this binding item is not configured!
 * 	Auth: dingmz_frc.20191112.
 * */
kk_err_t kNwkClusterBindingObjIsOK(uint8_t endpoint, uint16_t cluster )
{
	aps_binding_table_t aps_binding_tbl;
	memcpy(&aps_binding_tbl,aps_bindingTblGet(),sizeof(aps_binding_tbl));
	for(int index = 0; index < APS_BINDING_TABLE_NUM ;index++ ) {
		uint16_t cluster_temp = aps_binding_tbl.table[index].src_table.cluster_id;
		uint8_t src_ep = aps_binding_tbl.table[index].src_table.src_ep;
		iKonkeAfSelfPrint("###kNwkClusterBindingObjIsOK : endpoint(%d), cluster(%2x)\r\n", src_ep, cluster_temp);
		if(src_ep == endpoint && cluster_temp == cluster ) {
			return KET_OK;
		}
	}
	// If binding table is not set, query whether it has been configured.
	for(int index = 0; index < BINDING_CLUSTER_MAXN; ++index ) {
		if( g_lstBindingClusterConf[index].endpoint == endpoint &&
			g_lstBindingClusterConf[index].cluster == cluster ) {
			return KET_ERR_NON_EXIST;
		}
	}

	return KET_ERR_NO_PERMISSIONS;
}
// on binding-table set <index> <cluster> <local ep> <remote ep> <EUI>
static u8 kNwkClusterBindingObjSet(uint8_t endpoint, uint16_t cluster_id, addrExt_t eui64 )
{
	zdo_bind_req_t req;
	memset(&req, 0, sizeof(zdo_bind_req_t));
	u8 bindStatus = ZDO_SUCCESS;

	ZB_IEEE_ADDR_COPY(req.src_addr, NIB_IEEE_ADDRESS());
	req.src_endpoint = endpoint;
	iKonkeAfSelfPrint("### src addr : endpoint(%d), cluster(%2x)\r\n", endpoint, cluster_id);
	Print_Ieee_Addr(NIB_IEEE_ADDRESS());

	req.dst_addr_mode = LONG_EXADDR_DSTENDPOINT;

	ZB_IEEE_ADDR_COPY(req.dst_ext_addr, eui64);
	req.dst_endpoint = 1;
	iKonkeAfSelfPrint("### des addr : endpoint(%d), cluster(%2x)\r\n", req.dst_endpoint);
	Print_Ieee_Addr(eui64);

	req.cid16_l = cluster_id&0xff;
	req.cid16_h = cluster_id>>8;

	u8 sn = 0;
	bindStatus = zb_zdoBindUnbindReq(TRUE, &req, &sn, NULL);

	iKonkeAfSelfPrint("----set bind result[%d]: endpoint(%d), cluster(%2x)\r\n", bindStatus, endpoint, cluster_id);
	return bindStatus;
}

kk_err_t kNwkClusterBindingInspect(void )
{
	uint8_t ALL_FF[8] = {0};
	memset(ALL_FF,0xff,sizeof(ALL_FF));
	kNwkGetGateWayEui64(g_Eui64GatewayAddr);

	if(!memcmp(g_Eui64GatewayAddr,ALL_FF,sizeof(g_Eui64GatewayAddr)) ){
		return KET_ERR_NO_PERMISSIONS;
	}

	for(int ix = 0; ix < BINDING_CLUSTER_MAXN; ++ix ) {
		if( g_lstBindingClusterConf[ix].cluster != CLUSTER_UNKNOW ) {
			kk_err_t err = kNwkClusterBindingObjIsOK(g_lstBindingClusterConf[ix].endpoint, g_lstBindingClusterConf[ix].cluster);
			if( err == KET_ERR_NON_EXIST ) {
				kNwkClusterBindingObjSet(g_lstBindingClusterConf[ix].endpoint, g_lstBindingClusterConf[ix].cluster, g_Eui64GatewayAddr);
			}
		}else {
			break;
		}
	}

	return KET_OK;
}
/*	DESP: device cluster binding trigger interface.
 * 	Auth: dingmz_frc.20191112.
 * */
kk_err_t kNwkClusterBindingTrigger(void )
{
	uint8_t ALL_FF[8] = {0};
	kk_err_t err = KET_ERR_UNKNOW;
	if( g_u32BindingResultCheckCounter > 0 ) {
		return KET_ERR_OPRATE_IN_PROGRESS;
	}

#ifdef ZCL_USING_IAS_ZONE_CLUSTER_SERVER
	g_u32EnrollResultCheckCounter = MS2COUNT(ENROLL_SCHD_DURATION);
#endif

	memset(ALL_FF,0xff,sizeof(ALL_FF));
	kNwkGetGateWayEui64(g_Eui64GatewayAddr);

	iKonkeAfSelfPrint("##########GetEui64FromFlash %x%x%x%x%x%x%x%x --\r\n",g_Eui64GatewayAddr[7], g_Eui64GatewayAddr[6], \
			g_Eui64GatewayAddr[5], g_Eui64GatewayAddr[4], g_Eui64GatewayAddr[3], g_Eui64GatewayAddr[2], g_Eui64GatewayAddr[1],g_Eui64GatewayAddr[0]);

	iKonkeAfSelfPrint("Gateway IEEE have existed\r\n");

	if(!memcmp(g_Eui64GatewayAddr,ALL_FF,sizeof(g_Eui64GatewayAddr)) ){
		return KET_ERR_NO_PERMISSIONS;
	}

	for(int ix = 0; ix < BINDING_CLUSTER_MAXN; ++ix ) {
		if( g_lstBindingClusterConf[ix].cluster != CLUSTER_UNKNOW ) {
			kk_err_t err1 = kNwkClusterBindingObjIsOK(g_lstBindingClusterConf[ix].endpoint, g_lstBindingClusterConf[ix].cluster);
			if( err1 == KET_ERR_NON_EXIST ) {
				if(kNwkClusterBindingObjSet(g_lstBindingClusterConf[ix].endpoint, g_lstBindingClusterConf[ix].cluster, g_Eui64GatewayAddr) == ZDO_SUCCESS)
					err = KET_OK;
			}
		}else {
			break;
		}
	}
	return err;
}

void kNwkGetGateWayEui64(addrExt_t eui64)
{
	uint16_t index;
	tl_zbExtAddrByShortAddr(0x0000,eui64,&index);
}

/*	DESP: If the network status notification callback has been associated, the network status needs to be synchronized.
 * 	Auth: dingmz_frc.20191109.
 * */
void kNwkNetworkStatusNotifyCall(NwkStatusEnum nwkst )
{
	g_eCurrentNetworkStatus = nwkst;

	if( g_eCurrentNetworkStatus != g_eLastNetworkStatus ) {
		g_eLastNetworkStatus = g_eCurrentNetworkStatus;

		if( g_pNetworkStatusNotifyCallback ) {
			g_pNetworkStatusNotifyCallback(g_eCurrentNetworkStatus);
		}
	}
	if( g_eCurrentNetworkStatus == ENS_ONLINE ) {
		g_u32NwkJoiningCountdownCounter= MS2COUNT(0);
		addrExt_t addr;
		kNwkGetGateWayEui64(addr);
		Gateway_IEEE_Addr_Save(addr);
		iKonkeAfSelfPrint("#####kNwkNetworkStatusNotifyCall,Join Network addr=%x%x%x%x-%x%x%x%x!\r\n",
				addr[0],addr[1],addr[2],addr[3],addr[4],addr[5],addr[6],addr[7]);
	}
}
/*	DESP: Network module tick loop handler interface.
 * 	Auth: dingmz_frc.20191106.
 * */
void kNwkModuleActionDetectCallback(void )
{
	//static EmberNetworkStatus s_old_network_st = EMBER_NO_NETWORK;
	static uint8_t ucRestoreSteeringCount = 0;

	// joining procedure
	if( g_u32NwkJoiningCountdownCounter > 0 ) {
		--g_u32NwkJoiningCountdownCounter;

		if((g_u32NwkJoiningCountdownCounter > 0) && (!zb_isDeviceJoinedNwk())) {
			if( (g_bNwkSteeringCompleteFlg == true)&&(Get_Nwk_Steering_Status() == NWK_STEERING_STATUS_FAILED))
			{

				/*begin modify by maozj 20191127 repair issue that Difficult to network when gateway network permission joining is enabled with delay*/
				ucRestoreSteeringCount = 0;
				g_bNwkSteeringCompleteFlg = false;
				u8 status = bdb_networkSteerStart();
				//emberAfPluginNetworkSteeringStart();
				/*end modify by maozj 20191127*/

				// network status notify.
				kNwkNetworkStatusNotifyCall(ENS_JOINING);
				//add by maozj 20200321 for get joining network status
				g_bIsNwkJoiningFlg = true;
				iKonkeAfSelfPrint("$$$$$$$$$$ Network Steering Start,status = %d!\r\n",status);
			}else {
				//other situation,must restart steering
				if ( ucRestoreSteeringCount++ > MS2COUNT(DELAY_RESTORE_NWK_STEERING_TIME_MS)){
					ucRestoreSteeringCount = 0;
					g_bNwkSteeringCompleteFlg = true;
					Set_Nwk_Steering_Status(NWK_STEERING_STATUS_FAILED);
					iKonkeAfSelfPrint("#########Steering Again...\r\n");
				}
			}
		}
		else if( g_u32NwkJoiningCountdownCounter == 0 )
		{
			iKonkeAfSelfPrint("$$$$$$$$$$ Network Steering Stop!\r\n");
			g_bNwkSteeringCompleteFlg = false;

			g_bIsNwkJoiningFlg = false;//add by maozj 20200327

			//start get mac and self bind cluster
			kNwkClusterBindingTrigger();

			if( zb_isDeviceJoinedNwk())
			{
				kNwkNetworkStatusNotifyCall(ENS_ONLINE );
			}

			if( g_pJoinCompleteCallback )
			{
				iKonkeAfSelfPrint("777777g_pJoinCompleteCallback succeed\r\n");
				g_pJoinCompleteCallback(EJC_JOIN_SUCCEED);
			}
			else
			{
				if( g_pJoinCompleteCallback )
				{
					iKonkeAfSelfPrint("888888g_pJoinCompleteCallback succeed\r\n");
					g_pJoinCompleteCallback(EJC_JOIN_FAILED);
				}
			}
		}
	}

	if (g_u32NwkExitCountDownCounter > 0){
		if (--g_u32NwkExitCountDownCounter == 0){
			if (g_pNetworkExitCompleteCallback){
				g_pNetworkExitCompleteCallback();
			}
		}
	}
	if (g_u32NwkDetectCountDownCounter > 0){
		if (--g_u32NwkDetectCountDownCounter == 0){
			//上电3s 后推送网络状态
			if( zb_isDeviceJoinedNwk())
				kNwkNetworkStatusNotifyCall(ENS_ONLINE);
			else
				g_eCurrentNetworkStatus = ENS_LEAVED;
		}
	}


}
///*	DESP: network module initialize interface.
// * 	PARAM[item_list] the cluster objects requiring auto binding.
// * 	PARAM[nwk_led_id] Associate a light to indicate network status:
// * 		S1[offline] led off;
// * 		S2[joining]	led blink;
// * 		S3[online] led on for route device ,and led off for sleepy device.
// * 		Note: If this parameter is LED_UNKNOW_ID, it means that the network indicator is not associated!!
// * 	PARAM[nwk_button_id] Associate a button for network operation.
// * 	Auth: menmeng_li.20210523.
// * */
void kNwkModuleInit(BindObjSt cluster_obj_list[], uint8_t cluster_obj_num, pNetworkStatusNotifyCallback pNSCallback
		, pNetworkExitCompleteCallback pNECallback)
{
	for(int index = 0; index < cluster_obj_num; ++index ) {
		iKonkeAfSelfPrint("Cluster[%02d]: endpoint(%d), cluster_id(%2x)\r\n", index
			, cluster_obj_list[index].endpoint, cluster_obj_list[index].cluster);
	}
	iKonkeAfSelfPrint("-----------------\r\n");

	memset(g_lstBindingClusterConf, 0XFF, sizeof(g_lstBindingClusterConf));

	if( cluster_obj_list ) {
		uint8_t objs_num = (cluster_obj_num < BINDING_CLUSTER_MAXN)?cluster_obj_num:BINDING_CLUSTER_MAXN;
		memcpy(g_lstBindingClusterConf, cluster_obj_list, sizeof(BindObjSt) * objs_num);


	}
	//add by maozj 20200511
	g_pNetworkExitCompleteCallback = pNECallback;

	g_pNetworkStatusNotifyCallback = pNSCallback;
	//add by maozj20200325
	//g_u32JoinNwkTimeMs = (join_nwk_time < NWK_STEERING_DEFAULT_TIMEOUT_MS)?NWK_STEERING_DEFAULT_TIMEOUT_MS : join_nwk_time;

	//kBtnaIRQCallbackRegister(kNwkNetworkButtonAcitonCallback);
	g_u32NwkDetectCountDownCounter = MS2COUNT(DELAY_NWK_DETECT_POWER_ON_TIME_MS);
	//Cannot Detect Network
	if( (kNwkGetCurrentStatus() == ENS_ONLINE )&&(g_pNetworkStatusNotifyCallback)) {
		g_pNetworkStatusNotifyCallback(ENS_ONLINE);
	}

}
/*	DESP: The interface of resource release and reset after the device is restored to the factory, which is executed when the network is removed.
 * 	Auth: dingmz_frc.20191106.
 * */
void kNwkFactoryReset(bool is_nwk_indicator)
{
	kNwkJoiningStart(0, NULL);

	memset(g_Eui64GatewayAddr, 0xFF, sizeof(g_Eui64GatewayAddr));
	Gateway_IEEE_Addr_Save(g_Eui64GatewayAddr);
//	//add by maozj 20200320
//#if Z30_DEVICE_OTA_ENABLE
//	kSetOTAStatus(OTA_NORMAL);
//#endif
//	EmberNodeId node = 0xFFFF;
//	kNwksaveNodeIdToFlash(node);

	g_u32NwkJoiningCountdownCounter = 0;
//	g_u32EnrollResultCheckCounter   = 0;
//	g_u32RejoinResultCheckCounter   = 0;
//	g_u32BindingResultCheckCounter  = 0;


	//led can  blink when network leaved
	if (is_nwk_indicator == true){

		zb_factoryReset();

		kNwkNetworkStatusNotifyCall(ENS_LEAVED);
		//add by maozj 20200407 for reset count down after network leaved
		g_u32NwkExitCountDownCounter = MS2COUNT(5*1000);
	}
//	kNwkScheduleTaskStop(SCHEDULE_ALLOPT_ID);
//
//	//add by maozj 20191213 need identify whether gateway is made by konke
//	uint8_t isKonkeGateway = 0;
//	halCommonSetToken(TOKEN_IS_KONKE_GATEWAY, &isKonkeGateway);
	iKonkeAfSelfPrint("kNwkFactoryReset\r\n");
}

void NetworkLeaveHandler(nlme_leave_cnf_t *pLeaveCnf)
{
	iKonkeAfSelfPrint("Leave Handler,Nwk State = %d\r\n",kNwkGetCurrentStatus());

	if((pLeaveCnf->status == 0)&&(kNwkGetCurrentStatus() != ENS_JOINING))
	{
		//如果正处于离网中状态，则不执行以下函数
		if(g_u32NwkExitCountDownCounter == 0)
		{
			kNwkJoiningStart(0, NULL);

			g_u32NwkJoiningCountdownCounter = 0;
			memset(g_Eui64GatewayAddr, 0xFF, sizeof(g_Eui64GatewayAddr));
			Gateway_IEEE_Addr_Save(g_Eui64GatewayAddr);

			kNwkNetworkStatusNotifyCall(ENS_LEAVED);
			//add by maozj 20200407 for reset count down after network leaved
			g_u32NwkExitCountDownCounter = MS2COUNT(5*1000);
		}
	}
}


///*	DESP: get the current network status.
// * 	Auth: dingmz_frc.20191109.
// * */
NwkStatusEnum kNwkGetCurrentStatus(void )
{
	NwkStatusEnum status;
	if(zb_isDeviceJoinedNwk()){
		status = ENS_ONLINE;
		g_bIsNwkJoiningFlg = false;
	}
	else if(g_bIsNwkJoiningFlg){
		status = ENS_JOINING;
	}
	else{
		g_bIsNwkJoiningFlg = false;
		status = ENS_LEAVED;
	}
	return status;
}


bool JoinNetworkHandler(zdo_tc_join_ind_t *join_info)
{
	iKonkeAfSelfPrint("Join Network,short id = %2x!!!!!!!\r\n",join_info->devShortAddr);
	iKonkeAfSelfPrint("Join Network,COO Nwk IEEE Addr id = %x-%x-%x-%x-%x-%x-%x-%x!!!!!!!\r\n",join_info->parentIeeeAddr[0],join_info->parentIeeeAddr[1],join_info->parentIeeeAddr[2],join_info->parentIeeeAddr[3],
																						   join_info->parentIeeeAddr[4],join_info->parentIeeeAddr[5],join_info->parentIeeeAddr[6],join_info->parentIeeeAddr[7]);
	iKonkeAfSelfPrint("Join Network,Device Nwk IEEE Addr id = %x-%x-%x-%x-%x-%x-%x-%x!!!!!!!\r\n",join_info->devIeeeAddr[0],join_info->devIeeeAddr[1],join_info->devIeeeAddr[2],join_info->devIeeeAddr[3],
																							   join_info->devIeeeAddr[4],join_info->devIeeeAddr[5],join_info->devIeeeAddr[6],join_info->devIeeeAddr[7]);
	return 0;
}

static ev_timer_event_t *kUserScheduleTaskEvt = NULL;
extern s32 kUserScheduleTaskHandler(void *arg);
kk_err_t kNwkScheduleTaskRegister(uint32_t TimeOut)
{
	kk_err_t err = KET_FAILED;
	if(!ev_timer_exist(kUserScheduleTaskEvt)) {
		kUserScheduleTaskEvt = TL_ZB_TIMER_SCHEDULE(kUserScheduleTaskHandler, NULL, TimeOut);
		 err = KET_OK;
	}
	return err;
}


void kNwkJoiningStart(uint32_t u32JoiningDuration, pJoinCompleteCallback callback)
{
	if( u32JoiningDuration > 0 )
	{
		if(!zb_isDeviceJoinedNwk())
		{
			iKonkeAfSelfPrint("#####Set Steering Counter\r\n");
			g_u32NwkJoiningCountdownCounter= MS2COUNT(u32JoiningDuration);
			g_pJoinCompleteCallback = callback;
			kTickRunnningTrigger();

			//add by maozj 20191206
			g_bNwkSteeringCompleteFlg = true;
		}
	}
	else
	{
		g_u32NwkJoiningCountdownCounter= MS2COUNT(0);
		g_pJoinCompleteCallback = NULL;
	}
}


status_t General_Report(uint8_t src_ep,uint8_t des_ep,uint16_t cluster,uint16_t Attribute,uint8_t Data_Type,uint8_t *value)
{
	epInfo_t dstEpInfo;
	static ReportAttrData_ST reportInfo;
	uint8_t paload_len = 0;
	TL_SETSTRUCTCONTENT(dstEpInfo, 0);

	dstEpInfo.dstAddrMode = APS_SHORT_DSTADDR_WITHEP;
	dstEpInfo.dstEp = des_ep;
	dstEpInfo.dstAddr.shortAddr = 0x0000;
	dstEpInfo.profileId = HA_PROFILE_ID;
	dstEpInfo.txOptions = 0;
	dstEpInfo.radius = 0;

	reportInfo.attribute = Attribute;
	reportInfo.attri_type = Data_Type;
	if(!zcl_analogDataType(Data_Type)){
		if(Data_Type == ZCL_DATA_TYPE_CHAR_STR)
			paload_len = value[0]+1;
		else
			paload_len = zcl_getDataTypeLen(Data_Type);
		if(paload_len < MAX_PALOAD_LEN)
			memcpy(reportInfo.value, value,paload_len);
	}
	paload_len += 3;

	return zcl_sendCmd(src_ep, &dstEpInfo, cluster, ZCL_CMD_REPORT, false,
			ZCL_FRAME_SERVER_CLIENT_DIR, true, 0, ZCL_SEQ_NUM, (uint16_t )paload_len, (uint8_t *)&reportInfo);
}

