/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright (C) 2014 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/
/*******************************************************************************
* File Name   : dec_thread.c
* Description : decoder thread
*******************************************************************************/

/*******************************************************************************
Includes   <System Includes> , "Project Includes"
*******************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>

#include "cmsis_os.h"
#include "r_typedefs.h"
#include "fat_fsi.h"

#include "usb_haudio_rza1.h"
#include "ff.h"

#include "dec.h"
#include "dec_local.h"
#include "stream_decoder.h"
#include "drv_scux.h"

#include "usb_audio.h"
#include "dec_scux.h"

/*******************************************************************************
Typedef definitions
*******************************************************************************/

/*******************************************************************************
Macro definitions
*******************************************************************************/
#define MAX_DEC_MAILQ               (8UL)

#define READ_BUFF_SIZE              (8192UL)
#define MAX_BLOCK_SIZE              (4608UL)
#define PCM_BUFF_SIZE               MAX_BLOCK_SIZE * DEC_CHANNEL_STEREO		/* max block size 4608 * 2 ch */

#define DEC_WAIT_TIME               (1UL)

#define SMPRATE_8000                (8000UL)
#define SMPRATE_11025               (11025UL)
#define SMPRATE_12000               (12000UL)
#define SMPRATE_16000               (16000UL)
#define SMPRATE_22050               (22050UL)
#define SMPRATE_24000               (24000UL)
#define SMPRATE_32000               (32000UL)
#define SMPRATE_44100               (44100UL)
#define SMPRATE_48000               (48000UL)
#define SMPRATE_64000               (64000UL)
#define SMPRATE_88200               (88200UL)
#define SMPRATE_96000               (96000UL)
#define MAX_SMPRATE_LIST            (12UL)

#if (STRM_OUT_SAMPLE_RATE == USB_USER_SAMPLE_44_1KHZ)
#define STRM_OUT_FS                 FS_44100
#elif (STRM_OUT_SAMPLE_RATE == USB_USER_SAMPLE_48_0KHZ)
#define STRM_OUT_FS                 FS_48000
#elif (STRM_OUT_SAMPLE_RATE == USB_USER_SAMPLE_96_0KHZ)
#define STRM_OUT_FS                 FS_96000
#endif
/*******************************************************************************
Imported global variables and functions (from other files)
*******************************************************************************/
/* Main Thread */
static void dec_thread (void const *arg);

osMailQId dec_mailq_id;
osMailQDef(dec_mailq, MAX_DEC_MAILQ, msg_dec_t);
osThreadId dec_thread_id;
osThreadDef(dec_thread, osPriorityAboveNormal, 1, 0);

/*******************************************************************************
Exported global variables and functions (to be accessed by other files)
*******************************************************************************/

/*******************************************************************************
Private global variables and functions
*******************************************************************************/
/* decoder information */
typedef struct{
    int32_t             mode;               /* internal mode */
    uint8_t             *read_buf;
    uint8_t             *pre_read_buf;
    uint32_t            read_buf_size;
    uint32_t            read_pos;
    uint32_t            file_size;
    int32_t             *write_buf;
    uint32_t            write_buf_size;
    uint32_t            write_size;
    uint32_t            sample_rate;
    uint32_t            channels;
    uint32_t            bps;                /* bits per sample */
    uint32_t            pre_read_buf_cnt;
    uint32_t            pre_read_size;
    FLAC__StreamDecoder *decoder;           /* decoder information */
}DEC_INFO;
static DEC_INFO decoder_info;

typedef struct {
    uint32_t smp_rate;
    scux_fs_t input_fs;
} DEC_conv_t;


static const DEC_conv_t dec_smprate_convert[] =
{
    {SMPRATE_8000, FS_8000}
   ,{SMPRATE_11025, FS_11025}
   ,{SMPRATE_12000, FS_12000}
   ,{SMPRATE_16000, FS_16000}
   ,{SMPRATE_22050, FS_22050}
   ,{SMPRATE_24000, FS_24000}
   ,{SMPRATE_32000, FS_32000}
   ,{SMPRATE_44100, FS_44100}
   ,{SMPRATE_48000, FS_48000}
   ,{SMPRATE_64000, FS_64000}
   ,{SMPRATE_88200, FS_88200}
   ,{SMPRATE_96000, FS_96000}
};


/* decoder in buffer */
static uint8_t dec_read_buff[READ_BUFF_SIZE] __attribute__ ((section ("NC_DATA")));
static uint8_t dec_pre_read_buff[READ_BUFF_SIZE] __attribute__ ((section ("NC_DATA")));

/* decoder out buffer */
static uint32_t  dec_pcm_buff_1[PCM_BUFF_SIZE]  __attribute__ ((section ("NC_DATA")));
static uint32_t  dec_pcm_buff_2[PCM_BUFF_SIZE]  __attribute__ ((section ("NC_DATA")));

static FIL file_pointer __attribute__ ((section ("NC_DATA")));


/* application message */
static int32_t dec_event_open (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_open_err (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_start_play (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_rel_pause (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_pause (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_stop (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_end (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_err_strm (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_invalid (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_dummy (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_event_pre_read (msg_dec_t *msg, DEC_INFO *dec_info);

/* internal function */
static int32_t dec_event_decode (msg_dec_t *msg, DEC_INFO *dec_info);
static int32_t dec_decode (uint32_t buff_no, DEC_INFO *dec_info);
static int32_t dec_end_wait (msg_dec_t *msg, DEC_INFO *dec_info);
static void dec_err_stop (msg_dec_t *msg, DEC_INFO *dec_info, int32_t detail);
static void dec_close (DEC_INFO *dec_info);
static void dec_callback (uint32_t event, int32_t detail);
/* FLAC decoder */
FLAC__StreamDecoderReadStatus read_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__byte buffer[],
                                       size_t *bytes,
                                       void *client_data);
FLAC__StreamDecoderSeekStatus seek_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__uint64 absolute_byte_offset,
                                       void *client_data);
FLAC__StreamDecoderTellStatus tell_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__uint64 *absolute_byte_offset,
                                       void *client_data);
FLAC__StreamDecoderLengthStatus length_cb (const FLAC__StreamDecoder *decoder,
                                           FLAC__uint64 *stream_length,
                                           void *client_data);
FLAC__bool eof_cb (const FLAC__StreamDecoder *decoder,
                   void *client_data);
FLAC__StreamDecoderWriteStatus write_cb (const FLAC__StreamDecoder *decoder,
                                         const FLAC__Frame *frame,
                                         const FLAC__int32 *const buffer[],
                                         void *client_data);
void meta_cb (const FLAC__StreamDecoder *decoder,
              const FLAC__StreamMetadata *metadata,
              void *client_data);
void error_cb (const FLAC__StreamDecoder *decoder,
               FLAC__StreamDecoderErrorStatus status,
               void *client_data);
static int32_t dec_init_flac (DEC_INFO *dec_info);
static int32_t dec_decode_flac (DEC_INFO *dec_info, uint32_t *buff_addr, uint32_t *bytes);
static int32_t dec_decode_get_state (DEC_INFO *dec_info);
static void dec_close_flac (DEC_INFO *dec_info);


/* ==== internal mode ==== */

static int32_t (* const dec_proc[DEC_MODE_DUMMY][DEC_MSG_DUMMY])(msg_dec_t *msg, DEC_INFO *dec_info)={
{   /*  ---- IDLE mode #0---- */
    dec_event_open          /* 0 DEC_MSG_OPEN */
,   dec_event_invalid       /* 1 DEC_MSG_PLAY */
,   dec_event_invalid       /* 2 DEC_MSG_PAUSE */
,   dec_event_invalid       /* 3 DEC_MSG_STOP */
,   dec_event_dummy         /* 4 DEC_MSG_END */
,   dec_event_dummy         /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_dummy         /* 7 DEC_MSG_TIMEOUT */
},
{   /* ---- OPEN mode #1---- */
    dec_event_open_err      /* 0 DEC_MSG_OPEN */
,   dec_event_start_play    /* 1 DEC_MSG_PLAY */
,   dec_event_pause         /* 2 DEC_MSG_PAUSE */
,   dec_event_stop          /* 3 DEC_MSG_STOP */
,   dec_event_dummy         /* 4 DEC_MSG_END */
,   dec_event_dummy         /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_dummy         /* 7 DEC_MSG_TIMEOUT */
},
{   /* ---- PLAY mode #2---- */
    dec_event_open_err      /* 0 DEC_MSG_OPEN */
,   dec_event_invalid       /* 1 DEC_MSG_PLAY */
,   dec_event_pause         /* 2 DEC_MSG_PAUSE */
,   dec_event_stop          /* 3 DEC_MSG_STOP */
,   dec_event_dummy         /* 4 DEC_MSG_END */
,   dec_event_decode        /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_pre_read      /* 7 DEC_MSG_TIMEOUT */
},
{   /* ---- PAUSE mode #3---- */
    dec_event_open_err      /* 0 DEC_MSG_OPEN */
,   dec_event_rel_pause     /* 1 DEC_MSG_PLAY */
,   dec_event_invalid       /* 2 DEC_MSG_PAUSE */
,   dec_event_stop          /* 3 DEC_MSG_STOP */
,   dec_event_dummy         /* 4 DEC_MSG_END */
,   dec_event_decode        /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_pre_read      /* 7 DEC_MSG_TIMEOUT */
},
{   /* ---- STOP mode #4---- */
    dec_event_dummy         /* 0 DEC_MSG_OPEN */
,   dec_event_dummy         /* 1 DEC_MSG_PLAY */
,   dec_event_dummy         /* 2 DEC_MSG_PAUSE */
,   dec_event_invalid       /* 3 DEC_MSG_STOP */
,   dec_event_dummy         /* 4 DEC_MSG_END */
,   dec_event_dummy         /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_dummy         /* 7 DEC_MSG_TIMEOUT */
},
{   /* ---- END WAIT mode #5---- */
    dec_event_dummy         /* 0 DEC_MSG_OPEN */
,   dec_event_dummy         /* 1 DEC_MSG_PLAY */
,   dec_event_dummy         /* 2 DEC_MSG_PAUSE */
,   dec_event_dummy         /* 3 DEC_MSG_STOP */
,   dec_event_end           /* 4 DEC_MSG_END */
,   dec_end_wait            /* 5 DEC_MSG_REQ_DEC */
,   dec_event_err_strm      /* 6 DEC_MSG_ERR_STRM */
,   dec_event_dummy         /* 7 DEC_MSG_TIMEOUT */
}
};

/******************************************************************************
* Function Name: dec_mail_put
* Description  : 
* Arguments    : 
* Return Value : 
******************************************************************************/
int32_t dec_mail_put(uint32_t msg_id,
                     uint32_t param1,
                     uint32_t param2,
                     uint32_t param3,
                     uint32_t timeout)
{
    int32_t ret = -1;
    msg_dec_t * mptr;
    osStatus os_status;

    if (dec_mailq_id != 0){

        mptr = osMailAlloc (dec_mailq_id, timeout);
        if (mptr != NULL)
        {
            mptr->msg_id = msg_id;
            mptr->param1 = param1;
            mptr->param2 = param2;
            mptr->param3 = param3;
            os_status = osMailPut (dec_mailq_id, mptr);
            if (os_status == osOK)
            {
                ret = 0;
            }
            else
            {
                (void)printf ("DEC SndMsg error: osMailPut error %d\n",ret);
            }
        }
        else
        {
            /* DO NOTHING */
        }
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_create_thread
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
void dec_create_thread (void)
{
    msg_dec_t *rptr;
    osEvent evt;

    if (dec_mailq_id == 0)
    {
        /* create decoder mail queue */
        dec_mailq_id = osMailCreate (osMailQ (dec_mailq), NULL);
    }
    else
    {
        do
        {
            evt = osMailGet (dec_mailq_id, 0);
            if (evt.status == osEventMail)
            {
                rptr = evt.value.p;
                (void)osMailFree (dec_mailq_id, rptr);
            }
        }
        while (evt.status == osEventMail);
    }
    /* create decoder thread */
    dec_thread_id = osThreadCreate ( osThread(dec_thread), (void *)0 );

}


/*******************************************************************************
* Function Name: dec_terminate_thread
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
void dec_terminate_thread (void)
{
    /* ==== scux stop ==== */
    (void)SCUX_Stop ();

    if (osThreadTerminate (dec_thread_id) != osOK)
    {
        (void)printf ("osThreadTerminate error\n");
    }
    else
    {
        dec_thread_id = 0;
    }
}


/*******************************************************************************
* Function Name: dec_thread
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
void dec_thread (void const *arg)
{
    DEC_INFO *dec_info;
    msg_dec_t *rptr;
    msg_dec_t msg;
    osEvent evt;
    uint32_t timeout = DEC_WAIT_TIME;

    arg;    /* for compiler warning message */
    /* ==== task initialize ==== */
    dec_info = &decoder_info;
    dec_info->mode      = DEC_MODE_IDLE;
    (void)memset (&msg, 0x00, sizeof(msg));
    msg.msg_id = DEC_MSG_DUMMY;

    /* ==== task main ==== */
    while (1)
    {
        evt = osMailGet (dec_mailq_id, timeout);
        if (evt.status == osEventMail)
        {
            rptr = evt.value.p;
            msg.msg_id = rptr->msg_id;
            msg.param1 = rptr->param1;
            msg.param2 = rptr->param2;
            msg.param3 = rptr->param3;
            (void)osMailFree (dec_mailq_id, rptr);
        }
        else if (evt.status == osEventTimeout)
        {
            msg.msg_id = DEC_MSG_TIMEOUT;
            msg.param1 = 0;
            msg.param2 = 0;
            msg.param3 = 0;
        }
        else
        {
            /* DO NOTHING */
        }

        if ((dec_info->mode < DEC_MODE_DUMMY) && (msg.msg_id < DEC_MSG_DUMMY))
        {
            dec_info->mode = (*dec_proc[dec_info->mode][msg.msg_id])(&msg, dec_info);
        }
    }
}


/*******************************************************************************
* Function Name: dec_event_open
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_open (msg_dec_t *msg, DEC_INFO *dec_info)
{
    int32_t ret;

    /* ==== decoder initialize ==== */
    (void)memcpy (&file_pointer, (FIL *)msg->param3, sizeof(FIL));

    ret = dec_init_flac (dec_info);
    if (ret != DEC_OK)
    {
        /* ==== decoder close ==== */
        dec_close (dec_info);
        /* ==== reply error status ==== */
        dec_callback (
                     DEC_RES_OPEN,
                     DEC_ERR_INFO
                      );
        return DEC_MODE_IDLE;
    }

    /* ==== reply open prosess finished ==== */
    dec_callback (
                 DEC_RES_OPEN,
                 DEC_OK
                 );

    return DEC_MODE_OPEN;
}


/*******************************************************************************
* Function Name: dec_event_open_err
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_open_err (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    (void)printf ("dec_event_open_err : file duplicate open error\n");

    /* ==== reply error status ==== */
    dec_callback (
                 DEC_RES_OPEN,
                 DEC_ERR_OPEN
                 );

    return dec_info->mode;
}


/*******************************************************************************
* Function Name: dec_event_start_play
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_start_play (msg_dec_t *msg, DEC_INFO *dec_info)
{
    int32_t ret = DEC_OK;
    uint32_t bytes = 0;
    uint32_t *buff_addr;
    uint32_t loop_count;
    scux_fs_t input_fs = FS_44100;
    msg;    /* for compiler warning message */

    (void)printf ("Start Play.\n");

    /* ==== initialize Audio out ==== */
    AOUT_Init_AudioOut ();

    /* ==== initialize pcm buffer ==== */
    (void)memset ((uint8_t *)dec_pcm_buff_1, 0x00, sizeof(dec_pcm_buff_1));
    (void)memset ((uint8_t *)dec_pcm_buff_2, 0x00, sizeof(dec_pcm_buff_2));

    /* ==== initialize SCUX ==== */
    for(loop_count = 0; loop_count < MAX_SMPRATE_LIST; loop_count++)
    {
        if(dec_info->sample_rate == dec_smprate_convert[loop_count].smp_rate)
        {
            input_fs = dec_smprate_convert[loop_count].input_fs;
            break;
        }
    }
    (void)SCUX_Init (input_fs, STRM_OUT_FS);

    /* ==== fill scux input buffer ==== */
    buff_addr = DEC_GetInputBuffAddr (DEC_BUFF_NO_1);
    ret = dec_decode_flac (dec_info, buff_addr, &bytes);
    if (ret != DEC_OK)
    {
        return DEC_MODE_IDLE;
    }

    buff_addr = DEC_GetInputBuffAddr (DEC_BUFF_NO_2);
    ret = dec_decode_flac (dec_info, buff_addr, &bytes);
    if (ret != DEC_OK)
    {
        return DEC_MODE_IDLE;
    }

    /* ==== setup SCUX ==== */
    (void)SCUX_Setup ();

    /* ==== start SCUX ==== */
    (void)SCUX_Play ();

#ifdef DEBUG_PRINT
    (void)printf ("Start %d \n", GetOsTimer());
#endif

    /* ==== reply play prosess finished ==== */
    dec_callback (
                 DEC_RES_PLAY,
                 DEC_OK
                 );

    return DEC_MODE_PLAY;
}


/*******************************************************************************
* Function Name: dec_event_rel_pause
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_rel_pause (msg_dec_t *msg, DEC_INFO *dec_info)
{
    int32_t ret;
    msg;    /* for compiler warning message */

    /* ==== mute on ==== */
    (void)AOUT_Rel_Mute ();

    /* ==== check decoder status ==== */
    ret = dec_decode_get_state (dec_info);
    if ((ret == DEC_OK) || (ret == DEC_ERR_FILE_END))
    {
        ret = DEC_MODE_PLAY;
        /* ==== reply play prosess finished ==== */
        dec_callback (
                     DEC_RES_PLAY,
                     DEC_OK
                     );

    }
    else
    {
        /* ==== decoder close ==== */
        dec_close (dec_info);
        /* ==== reply error status ==== */
        dec_callback (
                     DEC_RES_PLAY,
                     DEC_ERR
                     );
        ret = DEC_MODE_IDLE;
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_event_pause
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_pause (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    /* ==== mute on ==== */
    (void)AOUT_Mute ();

    /* ==== reply pause prosess finished ==== */
    dec_callback (
                 DEC_RES_PAUSE,
                 DEC_OK
                 );

    return DEC_MODE_PAUSE;
}


/*******************************************************************************
* Function Name: dec_event_stop
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_stop (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    /* ==== decoder close ==== */
    dec_close (dec_info);

    /* ==== scux stop ==== */
    (void)SCUX_Stop ();

    /* ==== Mute on ==== */
    (void)AOUT_Mute ();

    /* ==== reply stop prosess finished ==== */
    dec_callback (
                 DEC_RES_STOP,
                 DEC_OK
                 );

    return DEC_MODE_IDLE;
}

/*******************************************************************************
* Function Name: dec_event_end
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_end (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    (void)printf ("End Play.\n");

    /* ==== decoder colose ==== */
    dec_close (dec_info);

    /* ==== scux stop ==== */
    (void)SCUX_Stop ();

    /* ==== mute on ==== */
    (void)AOUT_Mute ();

    /* ==== reply end prosess finished ==== */
    dec_callback (
                 DEC_RES_END,
                 DEC_OK
                 );

    return DEC_MODE_IDLE;
}


/*******************************************************************************
* Function Name: dec_event_err_strm
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_err_strm (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    /* ==== decoder colose ==== */
    dec_close (dec_info);

    /* ==== scux stop ==== */
    (void)SCUX_Stop ();

    /* ==== Mute on ==== */
    (void)AOUT_Mute ();

    /* ==== reply error stream prosess finished ==== */
    dec_callback (
                 DEC_RES_ERR,
                 DEC_OK
                 );

    return DEC_MODE_IDLE;
}

/*******************************************************************************
* Function Name: dec_event_invalid
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_invalid (msg_dec_t *msg, DEC_INFO *dec_info)
{
    uint32_t response_event = DEC_RES_NON;

    switch (msg->msg_id)
    {
    case DEC_MSG_PLAY:
        response_event = DEC_RES_PLAY;
        break;
    case DEC_MSG_PAUSE:
        response_event = DEC_RES_PAUSE;
        break;
    case DEC_MSG_STOP:
        response_event = DEC_RES_STOP;
        break;
    default:
        /* DO NOTHING */
        break;
    }

    /* ==== reply error status ==== */
    if (response_event != DEC_RES_NON)
    {
        dec_callback (
                     response_event,
                     DEC_ERR_STATE
                     );
    }

    return dec_info->mode;
}


/*******************************************************************************
* Function Name: dec_event_dummy
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_dummy (msg_dec_t *msg, DEC_INFO *dec_info)
{
    msg;    /* for compiler warning message */

    return dec_info->mode;
}


/*******************************************************************************
* Function Name: dec_event_pre_read
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_pre_read (msg_dec_t *msg, DEC_INFO *dec_info)
{
    FRESULT  result;
    UINT  read_byte;
    DEC_INFO *p = dec_info;
    msg;    /* for compiler warning message */

    if (p->pre_read_buf_cnt == 0)
    {
        result = f_read (&file_pointer, (int8_t *)p->pre_read_buf, p->read_buf_size, &read_byte);
        if (result == FR_OK)
        {
            p->pre_read_size = read_byte;
            p->pre_read_buf_cnt++;
        }
    }
    else
    {
        /* DO NOTHING */
    }

    return dec_info->mode;
}


/*******************************************************************************
* Function Name: dec_event_decode
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_event_decode (msg_dec_t *msg, DEC_INFO *dec_info)
{
    int32_t err;
    int32_t ret;
    uint32_t buff_no = msg->param1;

    if ((buff_no == (uint32_t)DEC_BUFF_NO_1) || (buff_no == (uint32_t)DEC_BUFF_NO_2))
    {
        /* ==== decode ==== */
        err = dec_decode (buff_no, dec_info);
    }
    else
    {
        err = DEC_ERR;
    }

    if (err == DEC_OK)
    {
        ret = dec_info->mode;
    }
    else if (err == DEC_ERR_FILE_END)
    {
        if (dec_info->mode == DEC_MODE_PLAY)
        {
            ret = DEC_MODE_END_WAIT;
        }
        else
        {
            ret = dec_info->mode;
        }
    }
    else
    {
        dec_err_stop (msg, dec_info, DEC_ERR_STATE);
        ret = DEC_MODE_IDLE;
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_decode
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_decode (uint32_t buff_no, DEC_INFO *dec_info)
{
    uint32_t bytes = 0;
    int32_t    ercd;
    uint32_t *buff_addr;

    /* ==== Start SCUX ==== */
    (void)SCUX_DmaFixedToScuxContinue ();

    /* ==== decode ==== */
    buff_addr = DEC_GetInputBuffAddr (buff_no);
    ercd = dec_decode_flac (dec_info, buff_addr, &bytes);

    if ((ercd == DEC_OK) && (bytes != 0UL))
    {
        (void)SCUX_DmaFixedToScuxSetQueue ((uint8_t *)buff_addr, bytes);
    }
    else if (ercd == DEC_ERR_FILE_END)
    {
        AOUT_Set_Mute_Buff ();
    }
    else
    {
        /* DO NOTHING */
    }

    return ercd;
}


/*******************************************************************************
* Function Name: dec_err_stop
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static void dec_err_stop (msg_dec_t *msg, DEC_INFO *dec_info, int32_t error)
{

    uint32_t response_event = 0;

    /* ==== decoder close ==== */
    dec_close (dec_info);

    switch (msg->msg_id)
    {
    case DEC_MSG_OPEN:
        response_event = DEC_RES_OPEN;
        break;
    case DEC_MSG_PLAY:
        response_event = DEC_RES_PLAY;
        break;
    case DEC_MSG_PAUSE:
        response_event = DEC_RES_PAUSE;
        break;
    case DEC_MSG_STOP:
        response_event = DEC_RES_STOP;
        break;
    default:
        return;
    }

    /* ==== reply error status ==== */
    dec_callback (
                 response_event,
                 error
                 );
}


/*******************************************************************************
* Function Name: dec_close
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static void dec_close (DEC_INFO *dec_info)
{
    dec_close_flac (dec_info);
}

/*******************************************************************************
* Function Name: dec_end_wait
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_end_wait (msg_dec_t *msg, DEC_INFO *dec_info)
{
    int32_t ret = DEC_MODE_END_WAIT;
    uint32_t buff_no = msg->param1;

    if ((buff_no == (uint32_t)DEC_BUFF_NO_1) || (buff_no == (uint32_t)DEC_BUFF_NO_2))
    {
        AOUT_Set_Mute_Buff ();
    }
    else
    {
        /* DO NOTHING */
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_callback
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static void dec_callback (uint32_t event, int32_t detail)
{
    int32_t ret;

    ret = MAIN_SndMsg (event, detail, 0);
    if (ret != 0L){
        (void)printf ("dec_callback error: Sample_MainSndMsg error %d\n",ret);
    }
}


/*******************************************************************************
* FLAC decoder
*******************************************************************************/
/* ==== Callback functions ==== */
/*******************************************************************************
* Function Name: read_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__StreamDecoderReadStatus read_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__byte buffer[],
                                       size_t *bytes,
                                       void *client_data)
{
    FRESULT  result;
    int32_t remain;
    int32_t size;
    UINT  read_byte;
    static DEC_INFO *p;

    size = (uint32_t)*bytes;
    p    = (DEC_INFO*)client_data;
#ifdef DEBUG_PRINT
    (void)printf ("read_cb\n");
    (void)printf ("read size: %d / read offset : %d\n",(uint32_t)*bytes, (uint32_t)p->read_pos);
#endif
    if (size > 0)
    {
        /* adjust requested size into buffer size */
        if (size > p->read_buf_size)
        {
            size = p->read_buf_size;
        }

        /* calculate remaining file size */
        remain = p->file_size - p->read_pos;
        if (remain <= 0)
        {
            return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
        }
        else if (size > remain)
        {
            size = remain;
        }
        else
        {
            /* DO NOTHING */
        }
        if (p->pre_read_buf_cnt == 0)
        {
            result = f_read (&file_pointer, (int8_t *)p->read_buf, size, &read_byte);
            if (result != FR_OK)
            {
                return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
            }
            else
            {
                if (read_byte == 0)
                {
                    return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
                }
                else
                {
                    (void)memcpy (buffer, p->read_buf, read_byte);
                    *bytes = (uint32_t)read_byte;
                    p->read_pos += *bytes;
                }
                return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
            }
        }
        else
        {
            read_byte = p->pre_read_size;
            if (read_byte > size)
            {
                if (f_tell (&file_pointer) <= (read_byte - size))
                {
                    return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
                }
                else
                {
                    result = f_lseek (&file_pointer, (int32_t)(f_tell (&file_pointer) - (read_byte - size)));
                    if (result != FR_OK)
                    {
                        return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
                    }
                    else
                    {
                        read_byte = size;
                    }
                }
            }
            else
            {
                /* DO NOTHING */
            }
            (void)memcpy (buffer, p->pre_read_buf, read_byte);
            *bytes = (uint32_t)read_byte;
            p->read_pos += *bytes;
            p->pre_read_buf_cnt--;
            return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
        }
    }
    else
    {
        return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
    }
}

/*******************************************************************************
* Function Name: seek_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__StreamDecoderSeekStatus seek_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__uint64 absolute_byte_offset,
                                       void *client_data)
{
    FRESULT  result;
    DEC_INFO *p = (DEC_INFO*)client_data;

    if (absolute_byte_offset > p->file_size)
    {
        return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
    }
    else
    {
        p->pre_read_buf_cnt = 0;    /* pre-read canceled if seeked. */
        result = f_lseek (&file_pointer, (int32_t)absolute_byte_offset);
        if (result < 0)
        {
            return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
        }
        else
        {
            p->read_pos = (uint32_t)absolute_byte_offset;
            return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
        }
    }
}

/*******************************************************************************
* Function Name: tell_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__StreamDecoderTellStatus tell_cb (const FLAC__StreamDecoder *decoder,
                                       FLAC__uint64 *absolute_byte_offset,
                                       void *client_data)
{
    DEC_INFO *p = (DEC_INFO*)client_data;
    p->read_pos = f_tell (&file_pointer);
    *absolute_byte_offset = (FLAC__uint64)p->read_pos;

    return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}

/*******************************************************************************
* Function Name: length_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__StreamDecoderLengthStatus length_cb (const FLAC__StreamDecoder *decoder,
                                           FLAC__uint64 *stream_length,
                                           void *client_data)
{
    DEC_INFO *p = (DEC_INFO*)client_data;

    *stream_length = (FLAC__uint64)p->file_size;
    return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}

/*******************************************************************************
* Function Name: eof_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__bool eof_cb (const FLAC__StreamDecoder *decoder,
                   void *client_data)
{
    DEC_INFO *p = (DEC_INFO*)client_data;

    if (p->read_pos >= p->file_size)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/*******************************************************************************
* Function Name: write_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
FLAC__StreamDecoderWriteStatus write_cb (const FLAC__StreamDecoder *decoder,
                                         const FLAC__Frame *frame,
                                         const FLAC__int32 *const buffer[],
                                         void *client_data)
{
    uint32_t i;
    DEC_INFO *p = (DEC_INFO*)client_data;
    uint32_t *wr_buffer = (uint32_t *)p->write_buf;

    uint32_t shift_bit = 0;

#ifdef DEBUG_PRINT
    (void)printf ("write_cb\n");
    (void)printf ("block size:%d\n", frame->header.blocksize);
#endif
    if (!((p->channels == DEC_CHANNEL_MONO) || (p->channels == DEC_CHANNEL_STEREO)))
    {
        (void)printf ("unsupported channels: %u\n", p->channels);
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }
    if (p->bps < DEC_32BIT_PER_SAMPLE)
    {
        shift_bit = DEC_32BIT_PER_SAMPLE - p->bps;
    }

    if ((frame->header.blocksize * p->channels) > p->write_buf_size)
    {
        (void)printf ("write error %d requested\n", frame->header.blocksize * p->channels);
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }

    if(p->channels == DEC_CHANNEL_STEREO)
    {
        for (i = 0; i < frame->header.blocksize; i++)
        {
                *(wr_buffer)++ = buffer[0][i] << shift_bit;
                *(wr_buffer)++ = buffer[1][i] << shift_bit;
        }
    }
    else
    {
        for (i = 0; i < frame->header.blocksize; i++)
        {
                *(wr_buffer)++ = buffer[0][i] << shift_bit;
                *(wr_buffer)++ = buffer[0][i] << shift_bit;
        }
    }
    p->write_size = frame->header.blocksize * DEC_CHANNEL_STEREO;

   return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

/*******************************************************************************
* Function Name: meta_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
void meta_cb (const FLAC__StreamDecoder *decoder,
              const FLAC__StreamMetadata *metadata,
              void *client_data)
{
    DEC_INFO *p = (DEC_INFO*)client_data;
    uint32_t i;
    uint32_t num_comments;

    if (metadata != NULL)
    {
        switch (metadata->type)
        {
            case FLAC__METADATA_TYPE_STREAMINFO:
                p->sample_rate      = metadata->data.stream_info.sample_rate;
                p->channels         = metadata->data.stream_info.channels;
                p->bps              = metadata->data.stream_info.bits_per_sample;

                (void)printf ("sample_rate: %u\n", p->sample_rate);
                (void)printf ("channels: %u\n", p->channels);
                (void)printf ("bps: %u\n", p->bps);
                break;

            case FLAC__METADATA_TYPE_VORBIS_COMMENT:
                num_comments = metadata->data.vorbis_comment.num_comments;
                (void)printf ("VENDER: %s\n", metadata->data.vorbis_comment.vendor_string.entry);
                for (i = 0; i < num_comments; i++)
                {
                    (void)printf ("TAG: %s\n", metadata->data.vorbis_comment.comments[i].entry);
                }
                break;

            case FLAC__METADATA_TYPE_SEEKTABLE:
                (void)printf ("SEEK POINT NUM: %d\n", metadata->data.seek_table.num_points);
                break;

            case FLAC__METADATA_TYPE_CUESHEET:
                (void)printf ("CUE NUM: %d\n", metadata->data.cue_sheet.num_tracks);
                break;
            default:
                /* DO NOTHING */
                break;
        }
    }
}

/*******************************************************************************
* Function Name: error_cb
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
void error_cb (const FLAC__StreamDecoder *decoder,
               FLAC__StreamDecoderErrorStatus status,
               void *client_data)
{
    (void)printf ("error: %s\n", status);
}


/*******************************************************************************
* Function Name: dec_init_flac
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_init_flac (DEC_INFO *dec_info)
{
    int32_t ret = DEC_OK;
    FLAC__StreamDecoder *p_decoder;

    if (dec_info != NULL)
    {
        /* initialize user data */
        dec_info->read_buf         = (uint8_t *)&dec_read_buff[0];
        dec_info->pre_read_buf     = (uint8_t *)&dec_pre_read_buff[0];
        dec_info->read_buf_size    = READ_BUFF_SIZE;
        dec_info->read_pos         = 0;
        dec_info->file_size        = file_pointer.fsize;
        dec_info->write_buf        = DEC_GetInputBuffAddr(DEC_BUFF_NO_1);
        dec_info->write_buf_size   = PCM_BUFF_SIZE;
        dec_info->write_size       = 0;
        dec_info->sample_rate      = 0;
        dec_info->channels         = 0;
        dec_info->bps              = 0;
        dec_info->pre_read_buf_cnt = 0;
        dec_info->pre_read_size    = 0;

        /* initialize flac decoder */
        p_decoder = FLAC__stream_decoder_new ();
        if (p_decoder == NULL)
        {
            (void)printf ("create error\n");
            ret = DEC_ERR;
        }

        if (ret == DEC_OK)
        {
            /* enable md5 check */
            (void)FLAC__stream_decoder_set_md5_checking (p_decoder, true);

            /* meta data setting */
            (void)FLAC__stream_decoder_set_metadata_respond (p_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);

            /* seek table setting */
            (void)FLAC__stream_decoder_set_metadata_respond (p_decoder, FLAC__METADATA_TYPE_SEEKTABLE);

            /* que sheet setting */
            (void)FLAC__stream_decoder_set_metadata_respond (p_decoder, FLAC__METADATA_TYPE_CUESHEET);

            /* init stream */
            (void)FLAC__stream_decoder_init_stream (p_decoder, read_cb, seek_cb, tell_cb, length_cb,
                                                    eof_cb, write_cb, meta_cb, error_cb, (void *)dec_info);
            /* process metadata */
            (void)FLAC__stream_decoder_process_until_end_of_metadata (p_decoder);

            dec_info->decoder = p_decoder;
        }
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_decode_flac
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_decode_flac (DEC_INFO *dec_info, uint32_t *buff_addr, uint32_t *bytes)
{
    int32_t ret = DEC_ERR;

    if ((dec_info != NULL) && (bytes != NULL))
    {
        dec_info->write_buf = buff_addr;
        dec_info->write_size = 0;

        (void)FLAC__stream_decoder_process_single (dec_info->decoder);

        ret = dec_decode_get_state (dec_info);
        if (ret == DEC_ERR)
        {
            *bytes = 0UL;
        }
        else
        {
            *bytes = dec_info->write_size*sizeof(int32_t);
        }
    }

    return ret;
}

/*******************************************************************************
* Function Name: dec_decode_get_state
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static int32_t dec_decode_get_state (DEC_INFO *dec_info)
{
    int32_t ret = DEC_OK;
    FLAC__StreamDecoderState state = FLAC__STREAM_DECODER_UNINITIALIZED;

    if (dec_info != NULL)
    {
         state = FLAC__stream_decoder_get_state (dec_info->decoder);
    }

    switch (state)
    {
    case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
    case FLAC__STREAM_DECODER_READ_METADATA:
    case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
    case FLAC__STREAM_DECODER_READ_FRAME:
        ret = DEC_OK;
        break;
    case FLAC__STREAM_DECODER_END_OF_STREAM:
        ret = DEC_ERR_FILE_END;
        break;
    case FLAC__STREAM_DECODER_OGG_ERROR:
    case FLAC__STREAM_DECODER_SEEK_ERROR:
    case FLAC__STREAM_DECODER_ABORTED:
    case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
    case FLAC__STREAM_DECODER_UNINITIALIZED:
        ret = DEC_ERR;
        break;
    default:
        /* DO NOTHING */
        break;
    }

    return ret;
}


/*******************************************************************************
* Function Name: dec_close_flac
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
static void dec_close_flac (DEC_INFO *dec_info)
{
    if (dec_info != NULL)
    {
        FLAC__stream_decoder_delete (dec_info->decoder);
    }
}


/*******************************************************************************
* Function Name: DEC_GetInputBuffAddr
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
uint32_t * DEC_GetInputBuffAddr (uint32_t buff_no)
{
    uint32_t *buff_addr;

    if (buff_no == DEC_BUFF_NO_1)
    {
        buff_addr = &dec_pcm_buff_1[0];
    }
    else
    {
        buff_addr = &dec_pcm_buff_2[0];
    }

    return buff_addr;
}


/*******************************************************************************
* Function Name: DEC_GetInputBuffSize
* Description  : 
* Argument     : 
* Return Value : 
*******************************************************************************/
uint32_t DEC_GetInputBuffSize (void)
{
    return sizeof(dec_pcm_buff_1);
}

/* End of File */
