/****************************************************************************
* Function	: eZCB_NodeAddGroup
* Description	: Node Add Group
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_NodeAddGroup(tsZCB_Node *psZCBNode, uint16_t u16GroupAddress)
{
    uint16_t *pu16NewGroups;
    int i;

    user_ZBNetwork_log("Node 0x%04X: Add group 0x%04X\n", psZCBNode->u16ShortAddress, u16GroupAddress);

    if (psZCBNode->pau16Groups)
    {
        for (i = 0; i < psZCBNode->u32NumGroups; i++)
        {
            if (psZCBNode->pau16Groups[i] == u16GroupAddress)
            {
                user_ZBNetwork_log("Node is already in group 0x%04X\n", u16GroupAddress);
                return E_ZCB_OK;
            }
        }
    }

    pu16NewGroups = realloc(psZCBNode->pau16Groups, sizeof(uint16_t) * (psZCBNode->u32NumGroups + 1));

    if (!pu16NewGroups)
    {
        user_ZBNetwork_log("Memory allocation failure allocating groups");
        return E_ZCB_ERROR_NO_MEM;
    }

    psZCBNode->pau16Groups = pu16NewGroups;
    psZCBNode->pau16Groups[psZCBNode->u32NumGroups] = u16GroupAddress;
    psZCBNode->u32NumGroups++;
    return E_ZCB_OK;
}
/****************************************************************************
* Function	: eZCB_NodeAddCommand
* Description	: Node Add Command
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_NodeAddCommand(tsZCB_Node *psZCBNode, uint8_t u8Endpoint, uint16_t u16ClusterID, uint8_t u8CommandID)
{
    int i;
    tsZCB_NodeEndpoint *psEndpoint = NULL;
    tsZCB_NodeCluster  *psCluster = NULL;
    uint8_t *pu8NewCommandList;

    //user_ZBNetwork_log("Node 0x%04X: Add Command 0x%02X to cluster 0x%04X on Endpoint %d\n",
    //                   psZCBNode->u16ShortAddress, u8CommandID, u16ClusterID, u8Endpoint);

    for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
    {
        if (psZCBNode->pasEndpoints[i].u8Endpoint == u8Endpoint)
        {
            psEndpoint = &psZCBNode->pasEndpoints[i];
        }
    }
    if (!psEndpoint)
    {
        user_ZBNetwork_log("Endpoint not found\n");
        return E_ZCB_UNKNOWN_ENDPOINT;
    }

    for (i = 0; i < psEndpoint->u32NumClusters; i++)
    {
        if (psEndpoint->pasClusters[i].u16ClusterID == u16ClusterID)
        {
            psCluster = &psEndpoint->pasClusters[i];
        }
    }
    if (!psCluster)
    {
        user_ZBNetwork_log("Cluster not found\n");
        return E_ZCB_UNKNOWN_CLUSTER;
    }

    for (i = 0; i < psCluster->u32NumCommands; i++)
    {
        if (psCluster->pau8Commands[i] == u8CommandID)
        {
            user_ZBNetwork_log("Duplicate Command ID\n");
            return E_ZCB_ERROR;
        }
    }

    pu8NewCommandList = realloc(psCluster->pau8Commands, sizeof(uint8_t) * (psCluster->u32NumCommands + 1));

    if (!pu8NewCommandList)
    {
        user_ZBNetwork_log("Memory allocation failure allocating commands");
        return E_ZCB_ERROR_NO_MEM;
    }
    psCluster->pau8Commands = pu8NewCommandList;

    psCluster->pau8Commands[psCluster->u32NumCommands] = u8CommandID;
    psCluster->u32NumCommands++;

    return E_ZCB_OK;
}
/****************************************************************************
* Function	: eZCB_NodeAddEndpoint
* Description	: Node Add Endpoint
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_NodeAddEndpoint(tsZCB_Node *psZCBNode, uint8_t u8Endpoint, uint16_t u16ProfileID, tsZCB_NodeEndpoint **ppsEndpoint)
{
    tsZCB_NodeEndpoint *psNewEndpoint;
    int i;

    //user_ZBNetwork_log("Add Endpoint %d, profile 0x%04X to node 0x%04X", u8Endpoint, u16ProfileID, psZCBNode->u16ShortAddress);

    for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
    {
        if (psZCBNode->pasEndpoints[i].u8Endpoint == u8Endpoint)
        {
            user_ZBNetwork_log("Duplicate Endpoint");
            if (u16ProfileID)
            {
                user_ZBNetwork_log("Set Endpoint %d profile to 0x%04X", u8Endpoint, u16ProfileID);
                psZCBNode->pasEndpoints[i].u16ProfileID = u16ProfileID;
            }
            return E_ZCB_OK;
        }
    }

    //user_ZBNetwork_log("Creating new endpoint %d", u8Endpoint);

    psNewEndpoint = realloc(psZCBNode->pasEndpoints, sizeof(tsZCB_NodeEndpoint) * (psZCBNode->u32NumEndpoints+1));

    if (!psNewEndpoint)
    {
        user_ZBNetwork_log("Memory allocation failure allocating endpoint");
        return E_ZCB_ERROR_NO_MEM;
    }

    psZCBNode->pasEndpoints = psNewEndpoint;

    memset(&psZCBNode->pasEndpoints[psZCBNode->u32NumEndpoints], 0, sizeof(tsZCB_NodeEndpoint));
    psNewEndpoint = &psZCBNode->pasEndpoints[psZCBNode->u32NumEndpoints];
    psZCBNode->u32NumEndpoints++;

    psNewEndpoint->u8Endpoint = u8Endpoint;
    psNewEndpoint->u16ProfileID = u16ProfileID;


    if (ppsEndpoint)
    {
        *ppsEndpoint = psNewEndpoint;
    }

    return E_ZCB_OK;
}
/****************************************************************************
* Function	: vZCB_NodeUpdateComms
* Description	: Node Update Comms
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
void vZCB_NodeUpdateComms(tsZCB_Node *psZCBNode, teZcbStatus eStatus)
{
    if (!psZCBNode)
    {
        return;
    }

    if (eStatus == E_ZCB_OK)
    {
        //gettimeofday(&psZCBNode->sComms.sLastSuccessful, NULL);
        psZCBNode->sComms.u16SequentialFailures = 0;
    }
    else if (eStatus == E_ZCB_COMMS_FAILED)
    {
        psZCBNode->sComms.u16SequentialFailures++;
    }
    else
    {

    }
#if DBG_ZBNETWORK
    {
        time_t nowtime;
        struct tm *nowtm;
        char tmbuf[64], buf[64];

        nowtime = psZCBNode->sComms.sLastSuccessful.tv_sec;
        nowtm = localtime(&nowtime);
        strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
        snprintf(buf, sizeof buf, "%s.%06d", tmbuf, (int)psZCBNode->sComms.sLastSuccessful.tv_usec);
        user_ZBNetwork_log("Node 0x%04X: %d sequential comms failures. Last successful comms at %s\n",
                           psZCBNode->u16ShortAddress, psZCBNode->sComms.u16SequentialFailures, buf);
    }
#endif /* DBG_ZBNETWORK */
}
/****************************************************************************
* Function	: eZCB_NodeAddCluster
* Description	: Node Add Cluster
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_NodeAddCluster(tsZCB_Node *psZCBNode, uint8_t u8Endpoint, uint16_t u16ClusterID)
{
    int i;
    tsZCB_NodeEndpoint *psEndpoint = NULL;
    tsZCB_NodeCluster  *psNewClusters;

    //user_ZBNetwork_log("Node 0x%04X: Add cluster 0x%04X to Endpoint %d", psZCBNode->u16ShortAddress, u16ClusterID, u8Endpoint);

    for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
    {
        if (psZCBNode->pasEndpoints[i].u8Endpoint == u8Endpoint)
        {
            psEndpoint = &psZCBNode->pasEndpoints[i];
            break;
        }
    }
    if (!psEndpoint)
    {
        user_ZBNetwork_log("Endpoint not found");
        return E_ZCB_UNKNOWN_ENDPOINT;
    }

    for (i = 0; i < psEndpoint->u32NumClusters; i++)
    {
        if (psEndpoint->pasClusters[i].u16ClusterID == u16ClusterID)
        {
            //user_ZBNetwork_log("Duplicate %d th Cluster ID:%d",i,u16ClusterID);

            return E_ZCB_OK;
        }
    }

    psNewClusters = realloc(psEndpoint->pasClusters, sizeof(tsZCB_NodeCluster) * (psEndpoint->u32NumClusters+1));
    if (!psNewClusters)
    {
        user_ZBNetwork_log("Memory allocation failure allocating clusters");
        return E_ZCB_ERROR_NO_MEM;
    }
    psEndpoint->pasClusters = psNewClusters;

    memset(&psEndpoint->pasClusters[psEndpoint->u32NumClusters], 0, sizeof(tsZCB_NodeCluster));
    psEndpoint->pasClusters[psEndpoint->u32NumClusters].u16ClusterID = u16ClusterID;
    psEndpoint->u32NumClusters++;

    return E_ZCB_OK;
}
/****************************************************************************
* Function	: DBG_PrintDetailNode
* Description	: 打印节点详细信息
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
void DBG_PrintDetailNode(tsZCB_Node *psNode)
{
    int i, j, k;
    //tsZCB_Node *currentNodep = psNode;

    while(psNode != NULL)
    {
        user_ZBNetwork_log("Node Short Address: 0x%04X, IEEE Address: 0x%016llX MAC Capability 0x%02X Device ID 0x%04X",
                           psNode->u16ShortAddress,
                           (unsigned long long int)psNode->u64IEEEAddress,
                           psNode->u8MacCapability,
                           psNode->u16DeviceID
                          );

        for (i = 0; i < psNode->u32NumEndpoints; i++)
        {
            const char *pcProfileName = NULL;
            tsZCB_NodeEndpoint  *psEndpoint = &psNode->pasEndpoints[i];

            switch (psEndpoint->u16ProfileID)
            {
                case (0x0104):
                    pcProfileName = "ZHA";
                    break;
                case (0xC05E):
                    pcProfileName = "ZLL";
                    break;
                default:
                    pcProfileName = "Unknown";
                    break;
            }

            user_ZBNetwork_log("  Endpoint %d - Profile 0x%04X (%s)",
                               psEndpoint->u8Endpoint,
                               psEndpoint->u16ProfileID,
                               pcProfileName
                              );

            for (j = 0; j < psEndpoint->u32NumClusters; j++)
            {
                tsZCB_NodeCluster *psCluster = &psEndpoint->pasClusters[j];
                user_ZBNetwork_log("    Cluster ID 0x%04X", psCluster->u16ClusterID);

                user_ZBNetwork_log("      Attributes:");
                for (k = 0; k < psCluster->u32NumAttributes; k++)
                {
                    user_ZBNetwork_log("        Attribute ID 0x%04X", psCluster->pau16Attributes[k]);
                }

                user_ZBNetwork_log("      Commands:");
                for (k = 0; k < psCluster->u32NumCommands; k++)
                {
                    user_ZBNetwork_log("        Command ID 0x%02X", psCluster->pau8Commands[k]);
                }
            }
        }

        psNode = psNode->psNext;
    }
}
/****************************************************************************
* Function	: psZCB_FindNodeShortAddress
* Description	: Find Node
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
tsZCB_Node *psZCB_FindNodeShortAddress(uint16_t u16ShortAddress)
{
    tsZCB_Node *psZCBNode = &sZCB_Network.sNodes;

    mico_rtos_lock_mutex(&sZCB_Network.sLock);

    while (psZCBNode)
    {
        if (psZCBNode->u16ShortAddress == u16ShortAddress)
        {
            int iLockAttempts = 0;

            user_ZBNetwork_log("Short address 0x%04X found in network", u16ShortAddress);
            DBG_PrintNode(psZCBNode);

            while (++iLockAttempts < 5)
            {
                if (mico_rtos_lock_mutex(&psZCBNode->sLock) == kNoErr)
                {
                    break;
                }
                else
                {
                    mico_rtos_unlock_mutex(&sZCB_Network.sLock);

                    if (iLockAttempts == 5)
                    {
                        user_ZBNetwork_log("\n\nError: Could not get lock on node!!");
                        return NULL;
                    }

                    mico_thread_msleep(1000);
                    mico_rtos_lock_mutex(&sZCB_Network.sLock);
                }
            }
            break;
        }
        psZCBNode = psZCBNode->psNext;
    }

    mico_rtos_unlock_mutex(&sZCB_Network.sLock);
    return psZCBNode;
}
/****************************************************************************
* Function	: psZCB_FindNodeIEEEAddress
* Description	: Find Node
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
tsZCB_Node *psZCB_FindNodeIEEEAddress(uint64_t u64IEEEAddress)
{
    tsZCB_Node *psZCBNode = &sZCB_Network.sNodes;

    mico_rtos_lock_mutex(&sZCB_Network.sLock);

    while (psZCBNode)
    {
        if (psZCBNode->u64IEEEAddress == u64IEEEAddress)
        {
            int iLockAttempts = 0;

            user_ZBNetwork_log("IEEE address 0x%016llX found in network", (unsigned long long int)u64IEEEAddress);
            DBG_PrintNode(psZCBNode);

            while (++iLockAttempts < 5)
            {
                if (mico_rtos_lock_mutex(&psZCBNode->sLock) == kNoErr)
                {
                    break;
                }
                else
                {
                    mico_rtos_unlock_mutex(&sZCB_Network.sLock);

                    if (iLockAttempts == 5)
                    {
                        user_ZBNetwork_log("\nError: Could not get lock on node!!");
                        return NULL;
                    }

                    mico_thread_msleep(1000);
                    mico_rtos_lock_mutex(&sZCB_Network.sLock);
                }
            }
            break;
        }
        psZCBNode = psZCBNode->psNext;
    }

    mico_rtos_unlock_mutex(&sZCB_Network.sLock);
    return psZCBNode;
}
/****************************************************************************
* Function	: eZCB_NodeClearGroups
* Description	: Node Clear Groups
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_NodeClearGroups(tsZCB_Node *psZCBNode)
{
    user_ZBNetwork_log("Node 0x%04X: Clear groups\n", psZCBNode->u16ShortAddress);

    if (psZCBNode->pau16Groups)
    {
        free(psZCBNode->pau16Groups);
        psZCBNode->pau16Groups = NULL;
    }

    psZCBNode->u32NumGroups = 0;
    return E_ZCB_OK;
}
/****************************************************************************
* Function	: psZCB_NodeFindEndpoint
* Description	: Node Find Endpoint
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
tsZCB_NodeEndpoint *psZCB_NodeFindEndpoint(tsZCB_Node *psZCBNode, uint16_t u16ClusterID)
{
    int i, j;
    tsZCB_NodeEndpoint *psEndpoint = NULL;

    user_ZBNetwork_log("Node 0x%04X: Find cluster 0x%04X", psZCBNode->u16ShortAddress, u16ClusterID);

    for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
    {
        psEndpoint = &psZCBNode->pasEndpoints[i];

        for (j = 0; j < psEndpoint->u32NumClusters; j++)
        {
            if (psEndpoint->pasClusters[j].u16ClusterID == u16ClusterID)
            {
                user_ZBNetwork_log("Found Cluster ID on Endpoint %d", psEndpoint->u8Endpoint);
                return psEndpoint;
            }
        }
    }
    user_ZBNetwork_log("Cluster 0x%04X not found on node 0x%04X", u16ClusterID, psZCBNode->u16ShortAddress);
    return NULL;
}
/****************************************************************************
* Function	: psZCB_NodeFindEndpoint
* Description	: Node Find Endpoint
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
tsZCB_NodeEndpoint *psZCB_NodeFindEndpointID(tsZCB_Node *psZCBNode, uint8_t u8EndpointID)
{
    int i;
    tsZCB_NodeEndpoint *psEndpoint = NULL;

    user_ZBNetwork_log("Node 0x%04X: Find endpoint 0x%04X", psZCBNode->u16ShortAddress, u8EndpointID);

    for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
    {
        psEndpoint = &psZCBNode->pasEndpoints[i];

        if(psEndpoint->u8Endpoint == u8EndpointID)
        {
			return psEndpoint;
        }
        else
        {
			continue;
        }
    }
    user_ZBNetwork_log("Endpoint 0x%04X not found on node 0x%04X", u8EndpointID, psZCBNode->u16ShortAddress);
    return NULL;
}
/****************************************************************************
* Function	: eZCB_RemoveNode
* Description	: Remove Node
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_RemoveNode(tsZCB_Node *psZCBNode)
{
    teZcbStatus eStatus = E_ZCB_ERROR;
    tsZCB_Node *psZCBCurrentNode = &sZCB_Network.sNodes;
    int iNodeFreeable = 0;

    /* lock the list mutex and node mutex in the same order as everywhere else to avoid deadlock */

    mico_rtos_unlock_mutex(&psZCBNode->sLock);

    mico_rtos_lock_mutex(&sZCB_Network.sLock);

    mico_rtos_lock_mutex(&psZCBNode->sLock);

    if (psZCBNode == &sZCB_Network.sNodes)
    {
        eStatus = E_ZCB_OK;
        iNodeFreeable = 0;
    }
    else
    {
        while (psZCBCurrentNode)
        {
            if (psZCBCurrentNode->psNext == psZCBNode)
            {
                user_ZBNetwork_log("Found node to remove\n");
                DBG_PrintNode(psZCBNode);

                psZCBCurrentNode->psNext = psZCBCurrentNode->psNext->psNext;
                eStatus = E_ZCB_OK;
                iNodeFreeable = 1;
                break;
            }
            psZCBCurrentNode = psZCBCurrentNode->psNext;
        }
    }

    if (eStatus == E_ZCB_OK)
    {
        int i, j;
        for (i = 0; i < psZCBNode->u32NumEndpoints; i++)
        {
            user_ZBNetwork_log("Free endpoint %d\n", psZCBNode->pasEndpoints[i].u8Endpoint);
            if (psZCBNode->pasEndpoints[i].pasClusters)
            {
                for (j = 0; j < psZCBNode->pasEndpoints[i].u32NumClusters; j++)
                {
                    user_ZBNetwork_log("Free cluster 0x%04X\n", psZCBNode->pasEndpoints[i].pasClusters[j].u16ClusterID);
                    free(psZCBNode->pasEndpoints[i].pasClusters[j].pau16Attributes);
                    free(psZCBNode->pasEndpoints[i].pasClusters[j].pau8Commands);
                }
                free(psZCBNode->pasEndpoints[i].pasClusters);

            }
        }
        free(psZCBNode->pasEndpoints);

        free(psZCBNode->pau16Groups);

        /* Unlock the node first so that it may be free'd */
        mico_rtos_unlock_mutex(&psZCBNode->sLock);
        mico_rtos_deinit_mutex(&psZCBNode->sLock);
        if (iNodeFreeable)
        {
            free(psZCBNode);
        }
    }
    mico_rtos_unlock_mutex(&sZCB_Network.sLock);
    return eStatus;
}
/****************************************************************************
* Function	: eZCB_AddNode
* Description	: Add Node
* Input Para	:
* Output Para	:
* Return Value:
****************************************************************************/
teZcbStatus eZCB_AddNode(uint16_t u16ShortAddress, uint64_t u64IEEEAddress, uint16_t u16DeviceID, uint8_t u8MacCapability, tsZCB_Node **ppsZCBNode)
{
    teZcbStatus eStatus = E_ZCB_OK;
    tsZCB_Node *psZCBNode = &sZCB_Network.sNodes;
	user_ZBNetwork_log("add node");
    mico_rtos_lock_mutex(&sZCB_Network.sLock);

    while (psZCBNode->psNext)
    {
        if (u64IEEEAddress)
        {
            if (psZCBNode->psNext->u64IEEEAddress == u64IEEEAddress)
            {
                user_ZBNetwork_log("IEEE address already in network - update short address");
                mico_rtos_lock_mutex(&psZCBNode->psNext->sLock);
                psZCBNode->psNext->u16ShortAddress = u16ShortAddress;

                if (ppsZCBNode)
                {
                    *ppsZCBNode = psZCBNode->psNext;
                }
                else
                {
                    mico_rtos_unlock_mutex(&psZCBNode->psNext->sLock);
                }
                goto done;
            }
            if (psZCBNode->psNext->u16ShortAddress == u16ShortAddress)
            {
                user_ZBNetwork_log("Short address already in network - update IEEE address");
                mico_rtos_lock_mutex(&psZCBNode->psNext->sLock);
                psZCBNode->psNext->u64IEEEAddress = u64IEEEAddress;

                if (ppsZCBNode)
                {
                    *ppsZCBNode = psZCBNode->psNext;
                }
                else
                {
                    mico_rtos_unlock_mutex(&psZCBNode->psNext->sLock);
                }
                goto done;
            }
        }
        else
        {
            if (psZCBNode->psNext->u16ShortAddress == u16ShortAddress)
            {
                user_ZBNetwork_log("Short address already in network");
                mico_rtos_lock_mutex(&psZCBNode->psNext->sLock);

                if (ppsZCBNode)
                {
                    *ppsZCBNode = psZCBNode->psNext;
                }
                else
                {
                    mico_rtos_unlock_mutex(&psZCBNode->psNext->sLock);
                }
                goto done;
            }

        }
        psZCBNode = psZCBNode->psNext;
    }

    psZCBNode->psNext = malloc(sizeof(tsZCB_Node));

    if (!psZCBNode->psNext)
    {
        user_ZBNetwork_log("Memory allocation failure allocating node");
        eStatus = E_ZCB_ERROR_NO_MEM;
        goto done;
    }

    memset(psZCBNode->psNext, 0, sizeof(tsZCB_Node));

    /* Got to end of list without finding existing node - add it at the end of the list */
    mico_rtos_init_mutex(&psZCBNode->psNext->sLock);
    psZCBNode->psNext->u16ShortAddress  = u16ShortAddress;
    psZCBNode->psNext->u64IEEEAddress   = u64IEEEAddress;
    psZCBNode->psNext->u8MacCapability  = u8MacCapability;
    psZCBNode->psNext->u16DeviceID      = u16DeviceID;

    user_ZBNetwork_log("Created new Node");
    DBG_PrintNode(psZCBNode->psNext);

    if (ppsZCBNode)
    {
        mico_rtos_lock_mutex(&psZCBNode->psNext->sLock);
        *ppsZCBNode = psZCBNode->psNext;
    }

done:
    mico_rtos_unlock_mutex(&sZCB_Network.sLock);
    return eStatus;
}