/*****************************************************************************
 *
 * MODULE:             Smart Energy
 *
 * COMPONENT:          ZCL
 *
 * AUTHOR:             we1
 *
 * DESCRIPTION:       Attribute Discovery
 *
 * $ $
 *
 * $Revision:  $
 *
 * $LastChangedBy: we1 $
 *
 * $LastChangedDate:  $
 *
 * $Id:  $
 *
 ****************************************************************************
 *
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5179].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2016. All rights reserved
 ****************************************************************************/

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
#include <jendefs.h>

#include "zcl.h"
#include "zcl_customcommand.h"
#include "zcl_common.h"
#include "zcl_internal.h"

#include "pdum_apl.h"
#include "zps_apl.h"
#include "zps_apl_af.h"


/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/
PRIVATE teZCL_Status eZCL_SearchForClosestAttributeEntry(
                        uint16                      u16AttributeEnum,
                        bool_t                      bManufacturerSpecific,
                        tsZCL_ClusterInstance      *psClusterInstance,
                        uint16                     *pu16FirstAttributeId,
                        uint16                     *pu16attributeIndex);

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/

/****************************************************************************/
/***        Public Functions                                              ***/
/****************************************************************************/

/****************************************************************************
 **
 ** NAME:       vZCL_HandleAttributesDiscoverExtendedRequest
 **
 ** DESCRIPTION:
 ** Handles an attribute discovery request
 **
 ** PARAMETERS:               Name                      Usage
 ** ZPS_tsAfEvent            *pZPSevent                 Zigbee Stack Event
 ** tsZCL_EndPointDefinition *psZCL_EndPointDefinition  EP structure
 ** tsZCL_ClusterInstance    *psClusterInstance         Cluster structure
 **
 ** RETURN:
 ** None
 **
 ****************************************************************************/

PUBLIC   void vZCL_HandleAttributesDiscoverExtendedRequest(
                                             ZPS_tsAfEvent              *pZPSevent,
                                             tsZCL_EndPointDefinition   *psZCL_EndPointDefinition,
                                             tsZCL_ClusterInstance      *psClusterInstance)
{
    uint16 i, j;

    uint16 u16inputOffset, u16outputOffset, u16BufferOverflowFlagOffset, u16Index = 0;

    tsZCL_HeaderParams sZCL_HeaderParams;

    bool_t bDiscoveryComplete;

    uint16 u16AttributeId, u16FirstAttributeId, u16TempAttrId;


    tsZCL_Address sZCL_Address;

    uint8 u8MaximumNumberOfIdentifiers;
    uint16 u16payloadSize;
    uint16 u16responseBufferSize;


    PDUM_thAPduInstance myPDUM_thAPduInstance;

    uint16 u16FirstAttributeIndex=0;
    bool_t bFoundAttribute;
    
    uint8 u8TempAttributeFlagwithMask;

    // build command packet for response
    // read incoming request
    u16inputOffset = u16ZCL_ReadCommandHeader(pZPSevent->uEvent.sApsDataIndEvent.hAPduInst,
                                              &sZCL_HeaderParams);


    // payload investigation
    u16payloadSize = PDUM_u16APduInstanceGetPayloadSize(pZPSevent->uEvent.sApsDataIndEvent.hAPduInst);
    // size of outgoing buffer
    u16responseBufferSize = u16ZCL_GetTxPayloadSize(pZPSevent->uEvent.sApsDataIndEvent.uSrcAddress.u16Addr);

    // size sanity checks
    if((u16payloadSize < u16inputOffset) || ((u16payloadSize - u16inputOffset)!=3))
    {
        // send response if possible
        eZCL_SendDefaultResponse(pZPSevent, E_ZCL_CMDS_MALFORMED_COMMAND);
        return;
    }

    // get buffer to write the response in
    myPDUM_thAPduInstance = hZCL_AllocateAPduInstance();
    // no buffers - return. we can make this descision a bit more exotic using the incoming buffer to store the
    // outgoing command but for the moment we are implementing the KISS principle.
    if(myPDUM_thAPduInstance == PDUM_INVALID_HANDLE)
    {
        return;
    }

    if(u16responseBufferSize < (u16inputOffset+3))
    {
        // send response if possible
        eZCL_SendDefaultResponse(pZPSevent, E_ZCL_CMDS_SOFTWARE_FAILURE);
        // free buffer and return
        PDUM_eAPduFreeAPduInstance(myPDUM_thAPduInstance);
        return;
    }

    // modify and write back
    u16outputOffset = u16ZCL_WriteCommandHeader(myPDUM_thAPduInstance,
                                           sZCL_HeaderParams.eFrameType,
                                           sZCL_HeaderParams.bManufacturerSpecific,
                                           sZCL_HeaderParams.u16ManufacturerCode,
                                           !sZCL_HeaderParams.bDirection,
                                           psZCL_EndPointDefinition->bDisableDefaultResponse,
                                           sZCL_HeaderParams.u8TransactionSequenceNumber,
                                           E_ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE);

    // read input attribute Id
    u16inputOffset += u16ZCL_APduInstanceReadNBO(
                        pZPSevent->uEvent.sApsDataIndEvent.hAPduInst, u16inputOffset, E_ZCL_ATTRIBUTE_ID, &u16AttributeId);
    u16inputOffset += u16ZCL_APduInstanceReadNBO(
                        pZPSevent->uEvent.sApsDataIndEvent.hAPduInst, u16inputOffset, E_ZCL_UINT8, &u8MaximumNumberOfIdentifiers);

    u16BufferOverflowFlagOffset = u16outputOffset;
    // parse the cluster, read each attribute from the device and write into the outgoing buffer
    i=0;
    j=0;
    // step over discovery complete flag slot
    u16outputOffset++;

    bFoundAttribute = FALSE;
    if(eZCL_SearchForClosestAttributeEntry(u16AttributeId, sZCL_HeaderParams.bManufacturerSpecific, psClusterInstance, &u16FirstAttributeId, &u16FirstAttributeIndex) == E_ZCL_SUCCESS)
    {
        bFoundAttribute = TRUE;
        j = u16FirstAttributeIndex;
        u16Index = u16FirstAttributeIndex;

        u16TempAttrId = u16FirstAttributeId;

        while((j < psClusterInstance->psClusterDefinition->u16NumberOfAttributes) &&
              (i < u8MaximumNumberOfIdentifiers)                                  &&
              (u16responseBufferSize >= (u16outputOffset +4))
        )
        {
            // check attribute flag matches
            if(
                  (bZCL_CheckManufacturerSpecificAttributeFlagMatch(&psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index], sZCL_HeaderParams.bManufacturerSpecific)) &&
                  (bZCL_CheckAttributeDirectionFlagMatch(&psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index],!(sZCL_HeaderParams.bDirection)))
              )
            {
                // write Attribute Id and type into outgoing buffer
                u16outputOffset += u16ZCL_APduInstanceWriteNBO(myPDUM_thAPduInstance, u16outputOffset, E_ZCL_ATTRIBUTE_ID,
                &u16TempAttrId);
                u16outputOffset += u16ZCL_APduInstanceWriteNBO(myPDUM_thAPduInstance, u16outputOffset, E_ZCL_UINT8,
                &psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].eAttributeDataType);
                /*Mask all other bits to 0 in the payload*/
                u8TempAttributeFlagwithMask = psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u8AttributeFlags & 0x07;
                u16outputOffset += u16ZCL_APduInstanceWriteNBO(myPDUM_thAPduInstance, u16outputOffset, E_ZCL_UINT8,
                &u8TempAttributeFlagwithMask);
                // increment attribute count
                i++;
            }

            // increment loop count
            j++;
            if((psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeArrayLength != 0)&&
               ( (u16TempAttrId - psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeEnum) < psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeArrayLength))
            {
                u16TempAttrId++;
            }
            else
            {
                u16Index++;
                u16TempAttrId = psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeEnum;
            }
        }
    }

    // we need to check if there are any valid attributes left to discover, this must take into account the MS flag
    bDiscoveryComplete = TRUE;
    while(bFoundAttribute && (j < psClusterInstance->psClusterDefinition->u16NumberOfAttributes) && (bDiscoveryComplete != FALSE))
    {
        // check attribute flag matches
        if(bZCL_CheckManufacturerSpecificAttributeFlagMatch(&psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index], sZCL_HeaderParams.bManufacturerSpecific))
        {
            bDiscoveryComplete = FALSE;
        }

        // increment loop count
        j++;
        if((psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeArrayLength != 0)&&
        ((u16TempAttrId - psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeEnum) < psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeArrayLength))
        {
            u16TempAttrId++;
        }
        else
        {
            u16Index++;
            u16TempAttrId = psClusterInstance->psClusterDefinition->psAttributeDefinition[u16Index].u16AttributeEnum;
        }
    }

    // write discovery result in first byte
    u16ZCL_APduInstanceWriteNBO(myPDUM_thAPduInstance, u16BufferOverflowFlagOffset, E_ZCL_BOOL, &bDiscoveryComplete);

    // transmit
    // NTS testing Nov 09. Don't send a good default response here as we have a unicast response that is valid.

    // build address structure
    eZCL_BuildTransmitAddressStructure(pZPSevent, &sZCL_Address);
    // transmit request
    eZCL_TransmitDataRequest(myPDUM_thAPduInstance,
                                u16outputOffset,
                                pZPSevent->uEvent.sApsDataIndEvent.u8DstEndpoint,
                                pZPSevent->uEvent.sApsDataIndEvent.u8SrcEndpoint,
                                pZPSevent->uEvent.sApsDataIndEvent.u16ClusterId,
                                &sZCL_Address);
}

/****************************************************************************
 **
 ** NAME:       eZCL_SearchForClosestAttributeEntry
 **
 ** DESCRIPTION:
 ** Searches For An Attribute Definition In The Endpoint
 ** assumes EP has already been validiated
 **
 ** PARAMETERS:                 Name                        Usage
 ** uint16                      u16AttributeEnum            Attribute Id
 ** bool_t                      bIsManufacturerSpecific     Attribute manufacturer specific
 ** tsZCL_ClusterInstance      *psClusterInstance           Cluster Structure
 ** tsZCL_AttributeDefinition **ppsAttributeDefinition      Attribute Structure
 ** uint16                     *pu16attributeIndex          Index of attribute definition
 **
 ** RETURN:
 ** teZCL_Status
 **
 ****************************************************************************/

PRIVATE teZCL_Status eZCL_SearchForClosestAttributeEntry(
                        uint16                      u16AttributeEnum,
                        bool_t                      bManufacturerSpecific,
                        tsZCL_ClusterInstance      *psClusterInstance,
                        uint16                     *pu16FirstAttributeId,
                        uint16                     *pu16attributeIndex)
{

    tsZCL_AttributeDefinition  *psAttributeDefinition;
    uint16 u16TempAttrId;
    if (psClusterInstance == NULL)
    {
        return(E_ZCL_ERR_ATTRIBUTE_NOT_FOUND);
    }

    psAttributeDefinition = psClusterInstance->psClusterDefinition->psAttributeDefinition;
    u16TempAttrId = psAttributeDefinition->u16AttributeEnum;
    // not found
    for(*pu16attributeIndex=0; *pu16attributeIndex < psClusterInstance->psClusterDefinition->u16NumberOfAttributes; (*pu16attributeIndex)++)
    {
        if((u16TempAttrId >= u16AttributeEnum) && bZCL_CheckManufacturerSpecificAttributeFlagMatch(psAttributeDefinition, bManufacturerSpecific))
        {
            *pu16FirstAttributeId = u16TempAttrId;
            return(E_ZCL_SUCCESS);
        }

        if((psAttributeDefinition->u16AttributeArrayLength != 0)&&((u16TempAttrId  - psAttributeDefinition->u16AttributeEnum) < psAttributeDefinition->u16AttributeArrayLength))
        {
            u16TempAttrId++;
        }
        else
        {
            psAttributeDefinition++;
            u16TempAttrId = psAttributeDefinition->u16AttributeEnum;
        }
    }

    return(E_ZCL_ERR_ATTRIBUTE_NOT_FOUND);
}

/****************************************************************************/
/***        END OF FILE                                                   ***/
/****************************************************************************/
