/**
 * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
 *
 */
/* Disclaimer: This client implementation of the Apple Notification Center Service can and will be changed at any time by Nordic Semiconductor ASA.
 * Server implementations such as the ones found in iOS can be changed at any time by Apple and may cause this client implementation to stop working.
 */

 #include "nrf_ble_ancs_c.h"
 #include "ancs_attr_parser.h"
 #include "nrf_log.h"


static bool all_req_attrs_parsed(ble_ancs_c_t * p_ancs)
{
    if (p_ancs->parse_info.expected_number_of_attrs == 0)
    {
        return true;
    }
    return false;
}

static bool attr_is_requested(ble_ancs_c_t * p_ancs, ble_ancs_c_attr_t attr)
{
    if (p_ancs->parse_info.p_attr_list[attr.attr_id].get == true)
    {
        return true;
    }
    return false;
}


/**@brief Function for parsing command id and notification id.
 *        Used in the @ref parse_get_notif_attrs_response state machine.
 *
 * @details UID and command ID will be received only once at the beginning of the first
 *          GATTC notification of a new attribute request for a given iOS notification.
 *
 * @param[in] p_ancs     Pointer to an ANCS instance to which the event belongs.
 * @param[in] p_data_src Pointer to data that was received from the Notification Provider.
 * @param[in] index      Pointer to an index that helps us keep track of the current data to be parsed.
 *
 * @return The next parse state.
 */
static ble_ancs_c_parse_state_t command_id_parse(ble_ancs_c_t  * p_ancs,
                                                           const uint8_t * p_data_src,
                                                           uint32_t      * index)
{
    ble_ancs_c_parse_state_t parse_state;

    p_ancs->parse_info.command_id = (ble_ancs_c_cmd_id_val_t) p_data_src[(*index)++];

    switch (p_ancs->parse_info.command_id)
    {
        case BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES:
            p_ancs->evt.evt_type = BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE;
            p_ancs->parse_info.p_attr_list  = p_ancs->ancs_notif_attr_list;
            p_ancs->parse_info.nb_of_attr   = BLE_ANCS_NB_OF_NOTIF_ATTR;
            parse_state                     = NOTIF_UID;
            break;

        case BLE_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES:
            p_ancs->evt.evt_type = BLE_ANCS_C_EVT_APP_ATTRIBUTE;
            p_ancs->parse_info.p_attr_list  = p_ancs->ancs_app_attr_list;
            p_ancs->parse_info.nb_of_attr   = BLE_ANCS_NB_OF_APP_ATTR;
            parse_state                     = APP_ID;
            break;

        default:
            //no valid command_id, abort the rest of the parsing procedure.
            NRF_LOG_DEBUG("Invalid Command ID");
            parse_state = DONE;
            break;
    }
    return parse_state;
}


static ble_ancs_c_parse_state_t notif_uid_parse(ble_ancs_c_t  * p_ancs,
                                                const uint8_t * p_data_src,
                                                uint32_t      * index)
{
     p_ancs->evt.notif_uid = uint32_decode(&p_data_src[*index]);
     *index               += sizeof(uint32_t);
     return ATTR_ID;
}

static ble_ancs_c_parse_state_t app_id_parse(ble_ancs_c_t  * p_ancs,
                                             const uint8_t * p_data_src,
                                             uint32_t      * index)
{
    p_ancs->evt.app_id[p_ancs->parse_info.current_app_id_index] = p_data_src[(*index)++];

    if (p_ancs->evt.app_id[p_ancs->parse_info.current_app_id_index] != '\0')
    {
        p_ancs->parse_info.current_app_id_index++;
        return APP_ID;
    }
    else
    {
        return ATTR_ID;
    }
}

/**@brief Function for parsing the id of an iOS attribute.
 *        Used in the @ref parse_get_notif_attrs_response state machine.
 *
 * @details We only request attributes that are registered with @ref ble_ancs_c_attr_add
 *          once they have been reveiced we stop parsing.
 *
 * @param[in] p_ancs     Pointer to an ANCS instance to which the event belongs.
 * @param[in] p_data_src Pointer to data that was received from the Notification Provider.
 * @param[in] index      Pointer to an index that helps us keep track of the current data to be parsed.
 *
 * @return The next parse state.
 */
static ble_ancs_c_parse_state_t attr_id_parse(ble_ancs_c_t  * p_ancs,
                                              const uint8_t * p_data_src,
                                              uint32_t      * index)
{
        p_ancs->evt.attr.attr_id     = p_data_src[(*index)++];

        if (p_ancs->evt.attr.attr_id >= p_ancs->parse_info.nb_of_attr)
        {
            NRF_LOG_DEBUG("Attribute ID Invalid.");
            return DONE;
        }
        p_ancs->evt.attr.p_attr_data = p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].p_attr_data;

        if (all_req_attrs_parsed(p_ancs))
        {
            NRF_LOG_DEBUG("All requested attributes received. ");
            return DONE;
        }
        else
        {
            if (attr_is_requested(p_ancs, p_ancs->evt.attr))
            {
                p_ancs->parse_info.expected_number_of_attrs--;
            }
            NRF_LOG_DEBUG("Attribute ID %i ", p_ancs->evt.attr.attr_id);
            return ATTR_LEN1;
        }
}


/**@brief Function for parsing the length of an iOS attribute.
 *        Used in the @ref parse_get_notif_attrs_response state machine.
 *
 * @details The Length is 2 bytes. Since there is a chance we reveice the bytes in two different
 *          GATTC notifications, we parse only the first byte here and then set the state machine
 *          ready to parse the next byte.
 *
 * @param[in] p_ancs     Pointer to an ANCS instance to which the event belongs.
 * @param[in] p_data_src Pointer to data that was received from the Notification Provider.
 * @param[in] index      Pointer to an index that helps us keep track of the current data to be parsed.
 *
 * @return The next parse state.
 */
static ble_ancs_c_parse_state_t attr_len1_parse(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
    p_ancs->evt.attr.attr_len = p_data_src[(*index)++];
    return ATTR_LEN2;
}

/**@brief Function for parsing the length of an iOS attribute.
 *        Used in the @ref parse_get_notif_attrs_response state machine.
 *
 * @details Second byte of the length field. If the length is zero, it means that the attribute is not
 *          present and the state machine is set to parse the next attribute.
 *
 * @param[in] p_ancs     Pointer to an ANCS instance to which the event belongs.
 * @param[in] p_data_src Pointer to data that was received from the Notification Provider.
 * @param[in] index      Pointer to an index that helps us keep track of the current data to be parsed.
 *
 * @return The next parse state.
 */
static ble_ancs_c_parse_state_t attr_len2_parse(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
    p_ancs->evt.attr.attr_len |= (p_data_src[(*index)++] << 8);
    p_ancs->parse_info.current_attr_index = 0;

    if (p_ancs->evt.attr.attr_len != 0)
    {
        //If the attribute has a length but there is no allocated space for this attribute
        if ((p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len == 0) ||
           (p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].p_attr_data == NULL))
        {
            return ATTR_SKIP;
        }
        else
        {
            return ATTR_DATA;
        }
    }
    else
    {

        NRF_LOG_DEBUG("Attribute LEN %i ", p_ancs->evt.attr.attr_len);
        if (attr_is_requested(p_ancs, p_ancs->evt.attr))
        {
            p_ancs->evt_handler(&p_ancs->evt);
        }
        if (all_req_attrs_parsed(p_ancs))
        {
            return DONE;
        }
        else
        {
            return ATTR_ID;
        }
    }
}


/**@brief Function for parsing the data of an iOS attribute.
 *        Used in the @ref parse_get_notif_attrs_response state machine.
 *
 * @details Read the data of the attribute into our local buffer.
 *
 * @param[in] p_ancs     Pointer to an ANCS instance to which the event belongs.
 * @param[in] p_data_src Pointer to data that was received from the Notification Provider.
 * @param[in] index      Pointer to an index that helps us keep track of the current data to be parsed.
 *
 * @return The next parse state.
 */
static ble_ancs_c_parse_state_t attr_data_parse(ble_ancs_c_t  * p_ancs,
                                                const uint8_t * p_data_src,
                                                uint32_t      * index)
{
    // We have not reached the end of the attribute, nor our max allocated internal size.
    // Proceed with copying data over to our buffer.
    if (   (p_ancs->parse_info.current_attr_index < p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len)
        && (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len))
    {
        //NRF_LOG_DEBUG("Byte copied to buffer: %c", p_data_src[(*index)]); // Un-comment this line to see every byte of an attribute as it is parsed. Commented out by default since it can overflow the uart buffer.
        p_ancs->evt.attr.p_attr_data[p_ancs->parse_info.current_attr_index++] = p_data_src[(*index)++];
    }

    // We have reached the end of the attribute, or our max allocated internal size.
    // Stop copying data over to our buffer. NUL-terminate at the current index.
    if ( (p_ancs->parse_info.current_attr_index == p_ancs->evt.attr.attr_len) ||
         (p_ancs->parse_info.current_attr_index == p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len - 1))
    {
        if (attr_is_requested(p_ancs, p_ancs->evt.attr))
        {
            p_ancs->evt.attr.p_attr_data[p_ancs->parse_info.current_attr_index] = '\0';
        }

        // If our max buffer size is smaller than the remaining attribute data, we must
        // increase index to skip the data until the start of the next attribute.
        if (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len)
        {
            return ATTR_SKIP;
        }
        NRF_LOG_DEBUG("Attribute finished!");
        if (attr_is_requested(p_ancs, p_ancs->evt.attr))
        {
            p_ancs->evt_handler(&p_ancs->evt);
        }
        if (all_req_attrs_parsed(p_ancs))
        {
            return DONE;
        }
        else
        {
            return ATTR_ID;
        }
    }
    return ATTR_DATA;
}


static ble_ancs_c_parse_state_t attr_skip(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
    // We have not reached the end of the attribute, nor our max allocated internal size.
    // Proceed with copying data over to our buffer.
    if (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len)
    {
        p_ancs->parse_info.current_attr_index++;
        (*index)++;
    }
    // At the end of the attribute, determine if it should be passed to event handler and
    // continue parsing the next attribute ID if we are not done with all the attributes.
    if (p_ancs->parse_info.current_attr_index == p_ancs->evt.attr.attr_len)
    {
        if (attr_is_requested(p_ancs, p_ancs->evt.attr))
        {
            p_ancs->evt_handler(&p_ancs->evt);
        }
        if (all_req_attrs_parsed(p_ancs))
        {
            return DONE;
        }
        else
        {
            return ATTR_ID;
        }
    }
    return ATTR_SKIP;
}


void ancs_parse_get_attrs_response(ble_ancs_c_t  * p_ancs,
                                   const uint8_t * p_data_src,
                                   const uint16_t  hvx_data_len)
{
    uint32_t index;

    for (index = 0; index < hvx_data_len;)
    {
        switch (p_ancs->parse_info.parse_state)
        {
            case COMMAND_ID:
                p_ancs->parse_info.parse_state = command_id_parse(p_ancs, p_data_src, &index);
                break;

            case NOTIF_UID:
                p_ancs->parse_info.parse_state = notif_uid_parse(p_ancs, p_data_src, &index);
                break;

            case APP_ID:
                p_ancs->parse_info.parse_state = app_id_parse(p_ancs, p_data_src, &index);
                break;

            case ATTR_ID:
                p_ancs->parse_info.parse_state = attr_id_parse(p_ancs, p_data_src, &index);
                break;

            case ATTR_LEN1:
                p_ancs->parse_info.parse_state = attr_len1_parse(p_ancs, p_data_src, &index);
                break;

            case ATTR_LEN2:
                p_ancs->parse_info.parse_state = attr_len2_parse(p_ancs, p_data_src, &index);
                break;

            case ATTR_DATA:
                p_ancs->parse_info.parse_state = attr_data_parse(p_ancs, p_data_src, &index);
                break;

            case ATTR_SKIP:
                p_ancs->parse_info.parse_state = attr_skip(p_ancs, p_data_src, &index);
                break;

            case DONE:
                NRF_LOG_DEBUG("Parse state: Done ");
                index = hvx_data_len;
                break;

            default:
                // Default case will never trigger intentionally. Go to the DONE state to minimize the consequences.
                p_ancs->parse_info.parse_state = DONE;
                break;
        }
    }
}
