/******************************************************************************
 *
 *  Copyright (C) 2010-2014 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/


/******************************************************************************
 *
 *  This file contains source code for some utility functions to help parse
 *  and build NFC Data Exchange Format (NDEF) messages for Connection
 *  Handover
 *
 ******************************************************************************/

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

/*******************************************************************************
**
** Static Local Functions
*/
static UINT8 *ndef_get_bt_oob_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                      char *p_id_str);

/*******************************************************************************
**
** Static data
*/

/* Handover Request Record Type */
static UINT8 hr_rec_type[HR_REC_TYPE_LEN] = { 0x48, 0x72 }; /* "Hr" */

/* Handover Select Record Type */
static UINT8 hs_rec_type[HS_REC_TYPE_LEN] = { 0x48, 0x73 }; /* "Hs" */

/* Handover Carrier recrod Type */
static UINT8 hc_rec_type[HC_REC_TYPE_LEN] = { 0x48, 0x63 }; /* "Hc" */

/* Collision Resolution Record Type */
static UINT8 cr_rec_type[CR_REC_TYPE_LEN] = { 0x63, 0x72 }; /* "cr" */

/* Alternative Carrier Record Type */
static UINT8 ac_rec_type[AC_REC_TYPE_LEN] = { 0x61, 0x63 }; /* "ac" */

/* Error Record Type */
static UINT8 err_rec_type[ERR_REC_TYPE_LEN] = { 0x65, 0x72, 0x72 }; /* "err" */

/* Bluetooth OOB Data Type */
static UINT8 *p_bt_oob_rec_type = (UINT8 *)"application/vnd.bluetooth.ep.oob";

/* Wifi WSC Data Type */
static UINT8 *p_wifi_wsc_rec_type = (UINT8 *)"application/vnd.wfa.wsc";

/*******************************************************************************
**
** Function         NDEF_MsgCreateWktHr
**
** Description      This function creates Handover Request Record with version.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgCreateWktHr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                  UINT8 version )
{
    tNDEF_STATUS    status;

    NDEF_MsgInit (p_msg, max_size, p_cur_size);

    /* Add record with version */
    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, hr_rec_type, HR_REC_TYPE_LEN,
                             NULL, 0, &version, 1);

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgCreateWktHs
**
** Description      This function creates Handover Select Record with version.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgCreateWktHs (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                  UINT8 version )
{
    tNDEF_STATUS    status;

    NDEF_MsgInit (p_msg, max_size, p_cur_size);

    /* Add record with version */
    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, hs_rec_type, HS_REC_TYPE_LEN,
                             NULL, 0, &version, 1);

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddWktHc
**
** Description      This function adds Handover Carrier Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddWktHc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                               char  *p_id_str, UINT8 ctf,
                               UINT8 carrier_type_len, UINT8 *p_carrier_type,
                               UINT8 carrier_data_len, UINT8 *p_carrier_data)
{
    tNDEF_STATUS    status;
    UINT8           payload[256], *p, id_len;
    UINT32          payload_len;

    if (carrier_type_len + carrier_data_len + 2 > 256)
    {
        return (NDEF_MSG_INSUFFICIENT_MEM);
    }

    p = payload;

    UINT8_TO_STREAM (p, (ctf & 0x07));
    UINT8_TO_STREAM (p, carrier_type_len);
    ARRAY_TO_STREAM (p, p_carrier_type, carrier_type_len);
    ARRAY_TO_STREAM (p, p_carrier_data, carrier_data_len);

    payload_len = (UINT32)carrier_type_len + carrier_data_len + 2;

    id_len = (UINT8)strlen (p_id_str);

    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, hc_rec_type, HC_REC_TYPE_LEN,
                             (UINT8*)p_id_str, id_len, payload, payload_len);
    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddWktAc
**
** Description      This function adds Alternative Carrier Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddWktAc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                               UINT8 cps, char *p_carrier_data_ref_str,
                               UINT8 aux_data_ref_count, char *p_aux_data_ref_str[])
{
    tNDEF_STATUS    status;
    UINT32          payload_len;
    UINT8           ref_str_len, xx;
    UINT8 *p_rec, *p;

    /* get payload length first */

    /* CPS, length of carrier data ref, carrier data ref, Aux data reference count */
    payload_len = 3 + (UINT8)strlen (p_carrier_data_ref_str);
    for (xx = 0; xx < aux_data_ref_count; xx++)
    {
        /* Aux Data Reference length (1 byte) */
        payload_len += 1 + (UINT8)strlen (p_aux_data_ref_str[xx]);
    }

    /* reserve memory for payload */
    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, ac_rec_type, AC_REC_TYPE_LEN,
                             NULL, 0, NULL, payload_len);

    if (status == NDEF_OK)
    {
        /* get AC record added at the end */
        p_rec = NDEF_MsgGetLastRecInMsg (p_msg);

        /* get start pointer of reserved payload */
        p = NDEF_RecGetPayload (p_rec, &payload_len);

        /* Add Carrier Power State */
        UINT8_TO_BE_STREAM (p, cps);

        /* Carrier Data Reference length */
        ref_str_len = (UINT8)strlen (p_carrier_data_ref_str);

        UINT8_TO_BE_STREAM (p, ref_str_len);

        /* Carrier Data Reference */
        ARRAY_TO_BE_STREAM (p, p_carrier_data_ref_str, ref_str_len);

        /* Aux Data Reference Count */
        UINT8_TO_BE_STREAM (p, aux_data_ref_count);

        for (xx = 0; xx < aux_data_ref_count; xx++)
        {
            /* Aux Data Reference length (1 byte) */
            ref_str_len = (UINT8)strlen (p_aux_data_ref_str[xx]);

            UINT8_TO_BE_STREAM (p, ref_str_len);

            /* Aux Data Reference */
            ARRAY_TO_BE_STREAM (p, p_aux_data_ref_str[xx], ref_str_len);
        }
    }

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddWktCr
**
** Description      This function adds Collision Resolution Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddWktCr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                               UINT16 random_number )
{
    tNDEF_STATUS    status;
    UINT8           data[2], *p;

    p = data;
    UINT16_TO_BE_STREAM (p, random_number);

    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, cr_rec_type, CR_REC_TYPE_LEN,
                             NULL, 0, data, 2);
    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddWktErr
**
** Description      This function adds Error Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddWktErr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                UINT8 error_reason, UINT32 error_data )
{
    tNDEF_STATUS    status;
    UINT8           payload[5], *p;
    UINT32          payload_len;

    p = payload;

    UINT8_TO_BE_STREAM (p, error_reason);

    if (error_reason == 0x02)
    {
        UINT32_TO_BE_STREAM (p, error_data);
        payload_len = 5;
    }
    else
    {
        UINT8_TO_BE_STREAM (p, error_data);
        payload_len = 2;
    }

    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_WKT, err_rec_type, ERR_REC_TYPE_LEN,
                             NULL, 0, payload, payload_len);
    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddMediaBtOob
**
** Description      This function adds BT OOB Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddMediaBtOob (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                    char *p_id_str, BD_ADDR bd_addr)
{
    tNDEF_STATUS    status;
    UINT8           payload[BD_ADDR_LEN + 2];
    UINT8          *p;
    UINT8           payload_len, id_len;

    p = payload;

    /* length including itself */
    UINT16_TO_STREAM (p, BD_ADDR_LEN + 2);

    /* BD Addr */
    BDADDR_TO_STREAM (p, bd_addr);

    payload_len = BD_ADDR_LEN + 2;
    id_len = (UINT8)strlen (p_id_str);

    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_MEDIA, p_bt_oob_rec_type, BT_OOB_REC_TYPE_LEN,
                             (UINT8*)p_id_str, id_len, payload, payload_len);
    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAppendMediaBtOobCod
**
** Description      This function appends COD EIR data at the end of BT OOB Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAppendMediaBtOobCod (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                          char *p_id_str, DEV_CLASS cod)
{
    tNDEF_STATUS    status;
    UINT8          *p_rec;
    UINT8           eir_data[BT_OOB_COD_SIZE + 2];
    UINT8          *p;
    UINT8           eir_data_len;
    UINT32          oob_data_len;

    /* find record by Payload ID */
    p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);

    if (!p_rec)
        return (NDEF_REC_NOT_FOUND);

    /* create EIR data format for COD */
    p = eir_data;
    UINT8_TO_STREAM (p, BT_OOB_COD_SIZE + 1);
    UINT8_TO_STREAM (p, BT_EIR_OOB_COD_TYPE);
    DEVCLASS_TO_STREAM (p, cod);
    eir_data_len = BT_OOB_COD_SIZE + 2;

    /* append EIR data at the end of record */
    status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
                                   p_rec, eir_data, eir_data_len);

    /* update BT OOB data length, if success */
    if (status == NDEF_OK)
    {
        /* payload length is the same as BT OOB data length */
        p = NDEF_RecGetPayload (p_rec, &oob_data_len);
        UINT16_TO_STREAM (p, oob_data_len);
    }

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAppendMediaBtOobName
**
** Description      This function appends Bluetooth Local Name EIR data
**                  at the end of BT OOB Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAppendMediaBtOobName (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                           char *p_id_str, BOOLEAN is_complete,
                                           UINT8 name_len, UINT8 *p_name)
{
    tNDEF_STATUS    status;
    UINT8          *p_rec;
    UINT8           eir_data[256];
    UINT8          *p;
    UINT8           eir_data_len;
    UINT32          oob_data_len;

    /* find record by Payload ID */
    p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);

    if (!p_rec)
        return (NDEF_REC_NOT_FOUND);

    /* create EIR data format for COD */
    p = eir_data;
    UINT8_TO_STREAM (p, name_len + 1);

    if (is_complete)
    {
        UINT8_TO_STREAM (p, BT_EIR_COMPLETE_LOCAL_NAME_TYPE);
    }
    else
    {
        UINT8_TO_STREAM (p, BT_EIR_SHORTENED_LOCAL_NAME_TYPE);
    }

    ARRAY_TO_STREAM (p, p_name, name_len);
    eir_data_len = name_len + 2;

    /* append EIR data at the end of record */
    status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
                                   p_rec, eir_data, eir_data_len);

    /* update BT OOB data length, if success */
    if (status == NDEF_OK)
    {
        /* payload length is the same as BT OOB data length */
        p = NDEF_RecGetPayload (p_rec, &oob_data_len);
        UINT16_TO_STREAM (p, oob_data_len);
    }

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAppendMediaBtOobHashCRandR
**
** Description      This function appends Hash C and Rand R at the end of BT OOB Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAppendMediaBtOobHashCRandR (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                                 char *p_id_str, UINT8 *p_hash_c, UINT8 *p_rand_r)
{
    tNDEF_STATUS    status;
    UINT8          *p_rec;
    UINT8           eir_data[BT_OOB_HASH_C_SIZE + BT_OOB_RAND_R_SIZE + 4];
    UINT8          *p;
    UINT8           eir_data_len;
    UINT32          oob_data_len;

    /* find record by Payload ID */
    p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);

    if (!p_rec)
        return (NDEF_REC_NOT_FOUND);

    /* create EIR data format */
    p = eir_data;

    UINT8_TO_STREAM   (p, BT_OOB_HASH_C_SIZE + 1);
    UINT8_TO_STREAM   (p, BT_EIR_OOB_SSP_HASH_C_TYPE);
    ARRAY16_TO_STREAM (p, p_hash_c);

    UINT8_TO_STREAM   (p, BT_OOB_RAND_R_SIZE + 1);
    UINT8_TO_STREAM   (p, BT_EIR_OOB_SSP_RAND_R_TYPE);
    ARRAY16_TO_STREAM (p, p_rand_r);

    eir_data_len = BT_OOB_HASH_C_SIZE + BT_OOB_RAND_R_SIZE + 4;

    /* append EIR data at the end of record */
    status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
                                   p_rec, eir_data, eir_data_len);

    /* update BT OOB data length, if success */
    if (status == NDEF_OK)
    {
        /* payload length is the same as BT OOB data length */
        p = NDEF_RecGetPayload (p_rec, &oob_data_len);
        UINT16_TO_STREAM (p, oob_data_len);
    }

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAppendMediaBtOobEirData
**
** Description      This function appends EIR Data at the end of BT OOB Record.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAppendMediaBtOobEirData (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                              char *p_id_str,
                                              UINT8 eir_type, UINT8 data_len, UINT8 *p_data)
{
    tNDEF_STATUS    status;
    UINT8          *p_rec;
    UINT8           eir_data[256];
    UINT8          *p;
    UINT8           eir_data_len;
    UINT32          oob_data_len;

    /* find record by Payload ID */
    p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);

    if (!p_rec)
        return (NDEF_REC_NOT_FOUND);

    /* create EIR data format */
    p = eir_data;
    UINT8_TO_STREAM (p, data_len + 1);
    UINT8_TO_STREAM (p, eir_type);
    ARRAY_TO_STREAM (p, p_data, data_len);
    eir_data_len = data_len + 2;

    /* append EIR data at the end of record */
    status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
                                   p_rec, eir_data, eir_data_len);

    /* update BT OOB data length, if success */
    if (status == NDEF_OK)
    {
        /* payload length is the same as BT OOB data length */
        p = NDEF_RecGetPayload (p_rec, &oob_data_len);
        UINT16_TO_STREAM (p, oob_data_len);
    }

    return (status);
}

/*******************************************************************************
**
** Function         NDEF_MsgAddMediaWifiWsc
**
** Description      This function adds Wifi Wsc Record header.
**
** Returns          NDEF_OK if all OK
**
*******************************************************************************/
tNDEF_STATUS NDEF_MsgAddMediaWifiWsc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                    char *p_id_str, UINT8 *p_payload, UINT32 payload_len)
{
    tNDEF_STATUS    status;
    UINT8           id_len = 0;

    if (p_id_str)
        id_len = (UINT8)strlen (p_id_str);

    status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
                             NDEF_TNF_MEDIA, p_wifi_wsc_rec_type, WIFI_WSC_REC_TYPE_LEN,
                             (UINT8*)p_id_str, id_len, p_payload, payload_len);
    return (status);
}

/*******************************************************************************
**
**              Static Local Functions
**
*******************************************************************************/
/*******************************************************************************
**
** Function         ndef_get_bt_oob_record
**
** Description      This function returns BT OOB record which has matched payload ID
**
** Returns          pointer of record if found, otherwise NULL
**
*******************************************************************************/
static UINT8 *ndef_get_bt_oob_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                      char *p_id_str)
{
    UINT8  *p_rec, *p_type;
    UINT8   id_len, tnf, type_len;

    /* find record by Payload ID */
    id_len = (UINT8)strlen (p_id_str);
    p_rec = NDEF_MsgGetFirstRecById (p_msg, (UINT8*)p_id_str, id_len);

    if (!p_rec)
        return (NULL);

    p_type = NDEF_RecGetType (p_rec, &tnf, &type_len);

    /* check type if this is BT OOB record */
    if ((!p_rec)
      ||(tnf != NDEF_TNF_MEDIA)
      ||(type_len != BT_OOB_REC_TYPE_LEN)
      ||(memcmp (p_type, p_bt_oob_rec_type, BT_OOB_REC_TYPE_LEN)))
    {
        return (NULL);
    }

    return (p_rec);
}