/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, 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:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o 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.
 *
 * o Neither the name of Freescale Semiconductor, Inc. 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 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.
 */

#include <string.h>
#include "fsl_clock_manager.h"
#include "fsl_uart_hal.h"
#include "fsl_uart_driver.h"
#include "playsound.h"
#include "shell.h"
#include "PCM_S16B_44100_2C.h"
#include "board.h"

/*******************************************************************************
 * Defination
 ******************************************************************************/
#define WAV_FILE_DECODE_BUFFER_SIZE  (512)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
extern const cmd_tbl_t CommandFun_Help;
static const cmd_tbl_t CommandFun_PlaySound;
static const cmd_tbl_t CommandFun_SetVolume;
static const cmd_tbl_t CommandFun_ShowWaveInfo;
static uint8_t Getchar(void);
static void Putchar(uint8_t ch);

/********************************************************************************
 * Variables
 ******************************************************************************/
static uart_state_t gUARTState;
static uart_user_config_t gShellUARTConfig =
{
    115200,
    kUartParityDisabled,
    kUartOneStopBit,
    kUart8BitsPerChar
};
static shell_io_install_t Shell_IOInstallStruct1 =
{
    .sh_getc = Getchar,
    .sh_putc = Putchar,
};
static uint8_t WavBuffer[WAV_FILE_DECODE_BUFFER_SIZE];
extern sound_card_t g_card;

 /*******************************************************************************
 * Code
 ******************************************************************************/
 /*!
 * @brief get a char
 */
static uint8_t Getchar(void)
{
    uint8_t ch = 0;
    uart_receive_data(&gUARTState, &ch, 1, 1);
    return ch;
}
 /*!
 * @brief put a char
 */
static void Putchar(uint8_t ch)
{
    uart_send_data(&gUARTState, &ch, 1, 1);
}

 /*!
 * @brief Callback function for getting data
 *
 * @param NumBytesReq     request data len
 * @param ppData          Pointer to a buffer memory area
 * @param offest          file offset
 * @return                MUST return the acutal requested btyes, It can be less or equal then NumBytesReq.
 */
uint32_t AppGetDataCallBack(uint32_t NumBytesReq,  uint8_t **ppData, uint32_t offest)
{
    uint32_t NumBytesRead = 0;
    if (NumBytesReq > WAV_FILE_DECODE_BUFFER_SIZE)
    {
        NumBytesReq = WAV_FILE_DECODE_BUFFER_SIZE;
    }
    /* In this demo, we simply copy data form ROM to RAM */
    memcpy(WavBuffer, WAV_DATA_TABLE + offest, NumBytesReq);
    NumBytesRead = NumBytesReq;
    *ppData = (WavBuffer);
    return NumBytesRead;
}


 /*!
 * @brief Main function
 */
uint32_t main(void)
{
    hardware_init();
    clock_manager_set_gate(kClockModuleSAI, 0, 1);
    configure_i2s_pin_mux(0);
    uart_init(BOARD_DEBUG_UART_INSTANCE, &gUARTState, &gShellUARTConfig);
    /* install shell IO */
    shell_io_install(&Shell_IOInstallStruct1);
    /* register user functions */
    shell_register_function(&CommandFun_ShowWaveInfo);
    shell_register_function(&CommandFun_Help);
    shell_register_function(&CommandFun_PlaySound);
    shell_register_function(&CommandFun_SetVolume);
    /* welcome info */
    shell_printf("sai play sound demo ...\r\n");
    shell_printf("initializing sound card ... \r\n");

    PLAYSOUND_Init();
    /* main loop */
    while (1)
    {
        shell_main_loop("SHELL>>");
    }

}

 /*!
 * @brief Shell play audio function
 */
static int Do_PlaySound(int argc, char * const argv[])
{
    if(PLAYSOUND_Play(AppGetDataCallBack))
    {
        shell_printf("play failed!\r\n");
    }
    return 0;
}

 /*!
 * @brief Shell set volume function
 *
 * <volume> should be in the range of 0-150 to indicate 0 - -75db. User should avoid set the volume to large for that it may cause distortion in codec.
 */
static int Do_SetVolume(int argc, char * const argv[])
{
    uint16_t volume = 0;

    if (argc == 2)
    {
        volume = strtoul(argv[1], NULL, 0);
        /* input must be corrent and <= 150(Range:0-150) */
        if (volume > 150)
        {
            printf("Unacceptable Command: <volume>: 0-150\r\n");
            return 0;
        }
        /* codec: min:0  max:180(0xF0-0x3C) */
        volume = (0xF0 - 0x3C) - volume;
        printf("volume -%d dB\r\n", volume/2);
        /* Max 0x3C Min 0xF0 see SGTL5000 CHIP_DAC_VOL */
        volume += 0x3C;
        sgtl_write_reg(g_card.codec.handler,CHIP_DAC_VOL, (volume+(volume*256))); /* follow MISRA not to use << */
    }
    else
    {
        printf("bad input\r\n");
        printf("volume <value>\r\n");
        printf("example: volume 100\r\n");
    }
    return 0;
}

 /*!
 * @brief Shell display wave file information function
 */
static int Do_ShowWavInfo(int argc, char * const argv[])
{
    uint8_t *pData = NULL;
    wave_file_t wavfile1;
    if (AppGetDataCallBack(WAVE_FILE_HEADER_SIZE , &pData, 0) == WAVE_FILE_HEADER_SIZE)
    {
        if (0 == PLAYSOUND_GetWaveFileInfo(pData, &wavfile1))
        {
            printf("Decode Succ\r\n");
            printf("AudioFormat:\r\n");
            if (wavfile1.header.tag == 1)
            {
                printf(" - Uncompressed\r\n");
            }
            else
            {
                printf(" - Compressed\r\n");
            }
            printf("BlockAlign(NumChannels*BitPerSample/8):\r\n");
            printf(" - %d\r\n", wavfile1.header.block_align);

            printf("BitPerSample:\r\n");
            printf(" - %d Bit\r\n", wavfile1.header.bit_samp);

            printf("ByteRate(SampleRate*NumChannels*BitPerSample/8):\r\n");
            printf(" - %d Bytes/Second\r\n", wavfile1.header.byte_rate);

            printf("SampleRate:\r\n");
            printf(" - %d Hz\r\n", wavfile1.header.samp_freq);

            printf("NumChannels:\r\n");
            switch(wavfile1.header.channels)
            {
                case 1:
                    printf(" - One channel(Mono)\r\n");
                break;
                case 2:
                    printf(" - Two channel(Stereo)\r\n");
                    break;
                default:
                    break;
            }

            printf("WaveDataSize:\r\n");
            printf(" - %d Bytes\r\n", wavfile1.header.length);
        }
        else
        {
            printf("Unrecognized file format\r\n");
        }
        return 0;
    }
    else
    {
        printf("Cannot get wave file header data\r\n");
        return 0;
    }
 }

static const cmd_tbl_t CommandFun_ShowWaveInfo =
{
    .name = "wavinfo",
    .maxargs = 1,
    .repeatable = 1,
    .cmd = Do_ShowWavInfo,
    .usage = "Show wave files information",
    .complete = NULL,
    .help = NULL,
};

static const cmd_tbl_t CommandFun_PlaySound =
{
    .name = "play",
    .maxargs = 1,
    .repeatable = 1,
    .cmd = Do_PlaySound,
    .usage = "Play sound",
    .complete = NULL,
    .help = NULL,
};

static const cmd_tbl_t CommandFun_SetVolume =
{
    .name = "volume",
    .maxargs = 2,
    .repeatable = 1,
    .cmd = Do_SetVolume,
    .usage = " - volume <volume> \r\n  - <volume>:0-150",
    .complete = NULL,
    .help = NULL,
};

/*******************************************************************************
 * EOF
 ******************************************************************************/
