//*****************************************************************************
//
//! @file am_vos_ble.c
//!
//! @brief BLE APIs for audio streaming
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, Inc.
// 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 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 the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// 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.
//
// This is part of revision ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

//*****************************************************************************
//
// Global includes for this project.
//
//*****************************************************************************
#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

//*****************************************************************************
//
// WSF standard includes.
//
//*****************************************************************************
#include "wsf_types.h"
#include "wsf_buf.h"

//*****************************************************************************
//
// Includes for operating the ExactLE stack.
//
//*****************************************************************************
#include "hci_handler.h"
#include "hci_drv_apollo.h"

//*****************************************************************************
//
// Includes for the VoS.
//
//*****************************************************************************
#include "am_vos_utils.h"

#include "am_vos_task.h"
#include "am_vos_init.h"
#include "am_vos_audio.h"
#include "am_vos_codec.h"
#include "am_vos_ble.h"

#include "amvos_api.h"

#if configUSE_AMVOS_AMA
#include "am_vos_ama.h"
#endif // configUSE_AMVOS_AMA

#if configUSE_AMVOS_ATVV
#include "am_vos_atvv.h"
#endif // configUSE_AMVOS_ATVV

//*****************************************************************************
//
// Short Description.
//
//*****************************************************************************

// triggers a ble task event to send data
// return true if failed
#if configUSE_BLE
bool am_vos_ble_cmd_send(uint8_t *buf, uint32_t length)
{
    sRadioCmdQueue_t bleMessageCmd;

    memset(bleMessageCmd.cmd_buf, 0, length);
    memcpy(bleMessageCmd.cmd_buf, buf, length);

    bleMessageCmd.len = length;

    if(xQueueSend(g_sVosBle.hRadioCmdQueue, &bleMessageCmd, 0) == NULL)
    {
        AM_VOS_LOG_WARNING("queue send fail in tx!\r\n");
        return false;
    }

    WsfTaskSetReady(0,0);

    return true;

}

bool am_vos_ble_stream_send(void)
{
    sRadioQueue_t bleStreamTrigger;

    //fill audio data header
    bleStreamTrigger.vos_buf[0] = 0x00;

    bleStreamTrigger.len = 0;

    if(xQueueSend(g_sVosBle.hRadioQueue, &bleStreamTrigger, 0) == NULL)
    {
        return true;
    }

    WsfTaskSetReady(0,0);
    return false;

}

sRadioQueue_t gRadioQueue;       // queue element defined globally to avoid using the stack
sRadioCmdQueue_t gRadioCmdQueue;       // queue element defined globally to avoid using the stack

bool am_vos_ble_nextdata_check(uint8_t** buf, uint32_t* len, bool *bCmdFlag)
{
    if(xQueueReceive(g_sVosBle.hRadioCmdQueue, &gRadioCmdQueue, 0))
    {
        //
        // check command queue first for higher priority
        //
        *buf = gRadioCmdQueue.cmd_buf;
        *len = gRadioCmdQueue.len;
        *bCmdFlag = true;
        return true;
    }
    else if(xQueueReceive(g_sVosBle.hRadioQueue, &gRadioQueue, 0))
    {
        if((gRadioQueue.len == 0) && (gRadioQueue.vos_buf[0] == 0x00))
        {
                return false;
        }
        else
        {
            // use queued data
            *buf = gRadioQueue.vos_buf;
            *len = gRadioQueue.len;
            *bCmdFlag = false;
            return true;
        }
    }
    else
    {
        return false;
    }
}
#endif // configUSE_BLE

void am_vos_streaming_start(void)
{
    if(g_sVosSys.bWwdEnabled == true)
    {
#if configUSE_AAD
        am_vos_aad_handler(NULL);
#endif // configUSE_AAD
        AM_VOS_LOG_INFO("[AM-VoS] am_vos_streaming_start\n");
        am_vos_audio_reset_flag_and_buffer();
#if (!configUSE_VS_CMD)
        am_vos_audio_wwd_disable();
#endif
    }
}

void am_vos_streaming_provide_speech(void)
{
    AM_VOS_LOG_INFO("[AM-VoS] am_vos_streaming_provide_speech\n");
    am_vos_streaming_start();
    g_sVosSys.ui8ProvideSpeechFlag = 1;
    AM_VOS_LOG_DEBUG("[AM-VoS] KWD = %d, PTT = %d PVS = %d\n", g_sVosSys.ui8KwdDetectedFlag, g_sVosSys.ui8PushTalkFlag, g_sVosSys.ui8ProvideSpeechFlag);
}

//*****************************************************************************
//
//! @brief am_vos_streaming_push_to_talk - Manual trigger and start voice command streamming.
//! 
//! 
//! 
//! @return None.
//*****************************************************************************
void am_vos_streaming_push_to_talk(void)
{
    AM_VOS_LOG_INFO("\n[AM-VoS] Push to talk!\n");
    am_vos_streaming_start();
    g_sVosSys.ui8PushTalkFlag = 1;
}

//*****************************************************************************
//
//! @brief am_kwd_streaming_stop - Force stop voice command streamming.
//! 
//! 
//! 
//! @return None.
//*****************************************************************************
void am_vos_streaming_stop(void)
{
    am_vos_audio_reset_flag_and_buffer();
    AM_VOS_LOG_INFO("[AM-VoS] am_vos_streaming_stop\n");
    // Lewis : Need to confirm later.
}

void am_vos_ble_task(void *pvParameters)
{
    //
    // Initialize the main ExactLE stack.
    //
    exactle_stack_init();

    g_sVosBle.hRadioQueue = xQueueCreate(10, sizeof( sRadioQueue_t ));          //4 * 15ms = 60 ms
    g_sVosBle.hRadioCmdQueue = xQueueCreate(10, sizeof( sRadioCmdQueue_t ));
  
    //
    // Start the "AmVos" profile.
    //
#if configUSE_BLE
    AmVosStart();
#endif // configUSE_BLE
#if configUSE_AMVOS_AMA
    am_vos_ama_evt_cback_register(am_vos_ama_event_callback);
#elif configUSE_AMVOS_ATVV
    am_vos_atvv_evt_cback_register(am_vos_atvv_event_callback);
#endif // configUSE_AMVOS_AMA

    while (1)
    {
#if configUSE_BLE && configUSE_AUDIO_CODEC
        uint8_t* pBuf;
        uint32_t len;
        bool bCmdFlag = false;
        if(am_vos_is_tx_ready())
        {
            uint32_t data_len = am_vos_get_ring_buffer_status(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED]));
            
            if(am_vos_ble_nextdata_check(&pBuf, &len, &bCmdFlag))
            {
                if(bCmdFlag)
                {
                    am_vos_cmd_packet_tx(pBuf, len);
                }
                else
                {
                    am_vos_audio_packet_tx(pBuf, len);
                }
            }
#if configUSE_AMVOS_ATVV
            else if((data_len > g_sVosBle.ui32TxPktSize / 2) && (g_sVosSys.ui8ProvideSpeechFlag))
            {
                uint8_t ui8AmVosTxBuf[BLE_DATA_BUFFER_SIZE];
                uint16_t ui16TxLength = 0;
                if(++g_sVosSys.ui8FragmentCount == 7 && g_sVosBle.ui32TxPktSize == 20)
                {
                    ui16TxLength = 14;
                    g_sVosSys.ui8FragmentCount = 0;
                }
                else if(data_len >= g_sVosBle.ui32TxPktSize)
                {
                    ui16TxLength = g_sVosBle.ui32TxPktSize;
                }
                else
                {
                    ui16TxLength = data_len;
                }

                am_vos_ble_tx_packet_encap(ui8AmVosTxBuf, ui16TxLength);
#if configUSE_ADPCM
                am_vos_audio_packet_tx(ui8AmVosTxBuf, ui16TxLength);
#endif
            }
#else
            else if(data_len >= BLE_DATA_BUFFER_SIZE)
            {
                uint8_t ui8AmVosTxBuf[BLE_DATA_BUFFER_SIZE + 3];

                am_vos_ble_tx_packet_encap(ui8AmVosTxBuf, BLE_DATA_BUFFER_SIZE);
                am_vos_audio_packet_tx(ui8AmVosTxBuf, BLE_DATA_BUFFER_SIZE + 3);
            }
#endif
        }
#endif // configUSE_BLE && configUSE_AUDIO_CODEC


        //
        // Calculate the elapsed time from our free-running timer, and update
        // the software timers in the WSF scheduler.
        //
        wsfOsDispatcher();
    }

}

int am_vos_ble_rx_handler(uint8_t *data, uint16_t len)
{
    if(len)
    {
        AM_VOS_LOG_INFO("[RX data]: ");
        for(int i = 0; i < len; i++)
          AM_VOS_LOG_INFO("0x%x ", data[i]);
        AM_VOS_LOG_INFO("\n");
    }

    return true;
}

void am_vos_ble_queue_reset(void)
{
    xQueueReset(g_sVosBle.hRadioQueue);
    xQueueReset(g_sVosBle.hRadioCmdQueue);
}

void am_vos_ble_tx_packet_encap(uint8_t *buf, uint16_t len)
{
    AM_CRITICAL_BEGIN_VOS;
#if configUSE_AMVOS_AMA
    buf[0] = 0x10;
    buf[1] = 0x80;  //AMA_TRANSPORT_DATA_TYPE_VOICE
    buf[2] = len;
    am_audio_buffer_pop(AM_AUDIO_BUFFER_ENCODED, &buf[3], len);
#elif configUSE_AMVOS_ATVV
    am_audio_buffer_pop(AM_AUDIO_BUFFER_ENCODED, buf, len);
#else // configUSE_AMVOS_AMA
    buf[0] = 0x00;
    buf[1] = g_sVosSys.ui8SeqNumber++;
    buf[2] = len;
    am_audio_buffer_pop(AM_AUDIO_BUFFER_ENCODED, &buf[3], len);
#endif // configUSE_AMVOS_AMA
    AM_CRITICAL_END_VOS;
}

bool am_vos_is_connected(void)
{
#if configUSE_BLE
#if configUSE_AMVOS_AMA
    if(am_vos_ama_isready() && g_sVosBle.bComSecured && g_sVosBle.bCccSetupFlag)
#elif configUSE_AMVOS_ATVV
    if(am_vos_atvv_isready() && g_sVosBle.bComSecured && g_sVosBle.bCccSetupFlag)
#else // configUSE_AMVOS_AMA
    if(g_sVosBle.ui8AmVosConnId != DM_CONN_ID_NONE)
#endif // configUSE_AMVOS_AMA
    {
        return true;
    }
    else
        return false;
#else // configUSE_BLE
    return false;
#endif // configUSE_BLE
}

bool am_vos_is_tx_ready(void)
{
#if configUSE_BLE
    if(g_sVosBle.ui8VosTxBusy == 0)
        return true;
#endif // configUSE_BLE
    return false;
}

#if configUSE_AMVOS_ATVV
bool am_vos_ble_hid_search_send(void)
{
#if configUSE_AMVOS_HID
    hidAppRemoteReportEvent(REMOTE_AC_SEARCH);
    hidAppRemoteReportEvent(REMOTE_USAGE_NONE);
#endif // configUSE_AMVOS_HID
    return true;
}

bool am_vos_ble_hid_btn_release_send(void)
{
#if configUSE_AMVOS_HID
    hidAppRemoteReportEvent(REMOTE_USAGE_NONE);
#endif // configUSE_AMVOS_HID
    return true;
}

bool am_vos_ble_hid_back_send(void)
{
#if configUSE_AMVOS_HID
    hidAppRemoteReportEvent(REMOTE_AC_BACKWARD);
    hidAppRemoteReportEvent(REMOTE_USAGE_NONE); 
#endif // configUSE_AMVOS_HID
    return true;
}
#endif

