/*
 *  Copyright (C) 2024 Texas Instruments Incorporated
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    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.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  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
 *  OWNER 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.
 */

#include <kernel/dpl/DebugP.h>
#include <kernel/dpl/AddrTranslateP.h>
#include <kernel/dpl/ClockP.h>
#include <kernel/dpl/DebugP.h>
#include <drivers/i2c.h>
#include <drivers/gpio.h>
#include <drivers/mcasp.h>
#include <board/ioexp/ioexp_tca6424.h>
#include "ti_drivers_config.h"
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <drivers/pinmux.h>

#include "FreeRTOS.h"
#include "task.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

/* Audio buffer settings */
#define APP_MCASP_AUDIO_BUFF_COUNT  (4U)
#define APP_MCASP_AUDIO_BUFF_SIZE   (2048U)


#define AUDIO_PROCESSING_TASK_PRI  (configMAX_PRIORITIES-1)

#define AUDIO_PROCESSING_TASK_SIZE (65536U/sizeof(configSTACK_DEPTH_TYPE))

/* ========================================================================== */
/*                           Global Variables                                 */
/* ========================================================================== */

/* Create buffers for transmit and Receive */
uint8_t gMcaspAudioBufferTx[APP_MCASP_AUDIO_BUFF_COUNT][APP_MCASP_AUDIO_BUFF_SIZE] __attribute__((aligned(256)));
uint8_t gMcaspAudioBufferRx[APP_MCASP_AUDIO_BUFF_COUNT][APP_MCASP_AUDIO_BUFF_SIZE] __attribute__((aligned(256)));

/* Create transaction objects for transmit and Receive */
MCASP_Transaction   gMcaspAudioTxnTx[APP_MCASP_AUDIO_BUFF_COUNT] = {0};
MCASP_Transaction   gMcaspAudioTxnRx[APP_MCASP_AUDIO_BUFF_COUNT] = {0};


StackType_t gAudioProcessingTaskStack[AUDIO_PROCESSING_TASK_SIZE] __attribute__((aligned(32)));
StaticTask_t gAudioProcessingTaskObj;
TaskHandle_t gAudioProcessingTask;

SemaphoreP_Object gMcaspTxStartSem;
MCASP_Transaction *current_transaction;

/* ========================================================================== */
/*                        Extern Function Declaration                         */
/* ========================================================================== */
int32_t Board_codecConfig(void);

/**
 * @brief Audio Processing Task - Left Channel Muting Example
 *
 * This function demonstrates real-time stereo audio processing by muting the left channel.
 *
 * Audio Processing Flow:
 * 1. Wait for RX buffer completion (new audio data available)
 * 2. Invalidate cache to ensure fresh data from DMA
 * 3. Process audio samples (mute left channel in this example)
 * 4. Write back cache to make processed data visible to DMA
 * 5. Submit processed buffer for TX (audio output)
 *
 * Stereo Sample Layout in Buffer:
 * - buffer[0], buffer[2], buffer[4]... = Left channel samples (even indices)
 * - buffer[1], buffer[3], buffer[5]... = Right channel samples (odd indices)
 *
 * @param args Task parameters (unused)
 */
void audio_processing(void *args)
{
    MCASP_Handle mcaspHandle;

    mcaspHandle = MCASP_getHandle(CONFIG_MCASP0);
    while(1){
        /* Wait for RX completion signal from interrupt callback */
        SemaphoreP_pend(&gMcaspTxStartSem, SystemP_WAIT_FOREVER);

        /* Cache invalidate: Ensure CPU sees fresh DMA data */
        CacheP_inv(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);

        /* Cast buffer to 32-bit samples and get sample count */
        uint32_t* buffer =(uint32_t*)(current_transaction->buf);
        uint32_t cnt = current_transaction->count;  /* Number of 32-bit samples */
        uint32_t inc = 0;

        /* Process stereo audio: Mute left channel (even indices) */
        for (inc = 0; inc < cnt; inc+=2)
        {
            buffer[inc] = 0;        /* Mute left channel */
            /* buffer[inc+1] unchanged - right channel passes through */
        }

        /* Cache writeback: Make processed data visible to DMA */
        CacheP_wb(current_transaction->buf, APP_MCASP_AUDIO_BUFF_SIZE, CacheP_TYPE_ALL);

        /* Submit processed buffer for audio output */
        MCASP_submitTx(mcaspHandle, current_transaction);
    }
}


void mcasp_playback_main(void *args)
{
    int32_t     status = SystemP_SUCCESS;
    uint32_t    i;
    MCASP_Handle    mcaspHandle;
    char            valueChar;


#if defined (SOC_AM275X)
    Pinmux_PerCfg_t i2cPinmuxConfig[] =
    {
        {
            PIN_GPIO1_72,
            ( PIN_MODE(1) | PIN_INPUT_ENABLE | PIN_PULL_DIRECTION  )
        },
        {PINMUX_END, 0U}
    };

    Pinmux_config(i2cPinmuxConfig, PINMUX_DOMAIN_ID_MAIN);
#endif

    /* Configure codec */
    status = Board_codecConfig();
    DebugP_assert(status == SystemP_SUCCESS);

    DebugP_log("[MCASP] Audio playback example started.\r\n");

    mcaspHandle = MCASP_getHandle(CONFIG_MCASP0);

    /* Construct Semaphore */
    SemaphoreP_constructBinary(&gMcaspTxStartSem, 0);

    /* Prepare and submit audio transaction transmit objects */
    for (i = 0U; i < APP_MCASP_AUDIO_BUFF_COUNT; i++)
    {
        gMcaspAudioTxnTx[i].buf = (void*) &gMcaspAudioBufferTx[i][0];
        gMcaspAudioTxnTx[i].count = APP_MCASP_AUDIO_BUFF_SIZE/4;
        gMcaspAudioTxnTx[i].timeout = 0xFFFFFF;
        MCASP_submitTx(mcaspHandle, &gMcaspAudioTxnTx[i]);
    }

    /* Prepare and submit audio transaction receive objects */
    for (i = 0U; i < APP_MCASP_AUDIO_BUFF_COUNT; i++)
    {
        gMcaspAudioTxnRx[i].buf = (void*) &gMcaspAudioBufferRx[i][0];
        gMcaspAudioTxnRx[i].count = APP_MCASP_AUDIO_BUFF_SIZE/4;
        gMcaspAudioTxnRx[i].timeout = 0xFFFFFF;
        MCASP_submitRx(mcaspHandle, &gMcaspAudioTxnRx[i]);
    }

    /* Trigger McASP receive operation */
    status = MCASP_startTransferRx(mcaspHandle);
    DebugP_assert(status == SystemP_SUCCESS);

    /* Trigger McASP transmit operation */
    status = MCASP_startTransferTx(mcaspHandle);
    DebugP_assert(status == SystemP_SUCCESS);



    /* This task is created at highest priority, it should create more tasks and then delete itself */
    gAudioProcessingTask = xTaskCreateStatic( audio_processing,   /* Pointer to the function that implements the task. */
                                  "audio_processing", /* Text name for the task.  This is to facilitate debugging only. */
                                  AUDIO_PROCESSING_TASK_SIZE,  /* Stack depth in units of StackType_t typically uint32_t on 32b CPUs */
                                  NULL,            /* We are not using the task parameter. */
                                  AUDIO_PROCESSING_TASK_PRI,   /* task priority, 0 is lowest priority, configMAX_PRIORITIES-1 is highest */
                                  gAudioProcessingTaskStack,  /* pointer to stack base */
                                  &gAudioProcessingTaskObj ); /* pointer to statically allocated task object memory */
    configASSERT(gAudioProcessingTask != NULL);


    DebugP_log("Enter your response on UART terminal");

    do
    {
        DebugP_log("\r\nStop the demo? (y/n) : ");
        status = DebugP_scanf("%c", &valueChar);
        DebugP_assert(status == SystemP_SUCCESS);
    } while (valueChar != 'y');

    vTaskDelete(gAudioProcessingTask);
    MCASP_stopTransferTx(mcaspHandle);
    MCASP_stopTransferRx(mcaspHandle);

    DebugP_log("Exiting demo\r\n");
}

/**
 * @brief McASP TX Completion Callback
 *
 * Called automatically by the McASP driver when a transmit (playback) buffer
 * has been completely sent to the DAC.
 *
 * Function: Maintains continuous audio flow by resubmitting the completed
 * TX buffer as an RX buffer for the next audio capture cycle.
 *
 * @param handle McASP driver handle
 * @param transaction Completed TX transaction to reuse for RX
 */
void mcasp_txcb(MCASP_Handle handle,
                          MCASP_Transaction *transaction)
{
    /* Resubmit completed TX buffer for next RX cycle */
    MCASP_submitRx(handle, transaction);
}

/**
 * @brief McASP RX Completion Callback
 *
 * Called automatically by the McASP driver when a receive (capture) buffer
 * has been filled with fresh audio data from the ADC.
 *
 * Function: Signals the audio processing task that new audio data is available
 * for processing. This is the trigger that starts each audio processing cycle.
 *
 * @param handle McASP driver handle
 * @param transaction Completed RX transaction containing fresh audio data
 */
void mcasp_rxcb(MCASP_Handle handle,
                          MCASP_Transaction *transaction)
{
    /* Store pointer to received buffer for processing task */
    current_transaction = transaction;

    /* Signal audio processing task that new data is ready */
    SemaphoreP_post(&gMcaspTxStartSem);
}
