/*
  AAC Player support code

  This part of code is based on the Andreas Schewarz's
  "ARM MP3/AAC player" project.

    Ref: http://embdev.net/articles/ARM_MP3/AAC_Player

  The project's source code is distributed with following notice.

  ----------
  Copyright (C) 2006 Andreas Schwarz <andreas@andreas-s.net>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/

#include <t_services.h>
#include "nxpmp3.h"
#include <string.h>
#include "kernel_id.h"
#include "ff.h"
#include "player.h"
#include <aaccommon.h>
#include "mempool.h"

#define	BUFF_THRESS	(MUSIC_BUFSIZE - 512)

static AACFrameInfo frame_info;

static AACDecInfo *aacdec;

static void aac_reset(Music_Data *aacd)
{
    pool_init();
    aacd->bytes_left = 0;
    aacd->nFrames = 0;
    aacd->read_ptr = aacd->music_buffer;
    aacd->offset = 0;
}

/*
 * Note that get_aac_info is called BEFORE aacdecoder_task is initiated.
 */
int get_aac_info(Player_Status *pst)
{
    long offset;

    /* Initialize frame_info with default values */
    memset(&frame_info, 0, sizeof(AACFrameInfo));
    frame_info.nChans = 2;
    frame_info.sampRateCore = 44100;
    frame_info.profile = AAC_PROFILE_LC;
    /* Get song info from MPEG4 file and overwrite frame_info if necessary */
    offset = mpeg4_decode(pst, &frame_info);
    /* File is now seeked at beginning of AAC data */
    if (offset == 0)
        return -1;	/* Error */
    return FR_OK;
}

#ifdef DEBUG
void aac_setup_raw()
{
    memset(&frame_info, 0, sizeof(AACFrameInfo));
    frame_info.nChans = 2;
    frame_info.sampRateCore = 44100;
    frame_info.profile = AAC_PROFILE_LC;
    AACSetRawBlockParams(aacdec, 0, &frame_info);
}
#endif

static int aac_fill_inbuffer(FIL *aacfile, Music_Data *aacd, int try)
{
    int bytes_to_read, bytes_read;
    int result;

#ifdef OLD_CODE
    if ((try == 0) && (aacd->bytes_left > (MUSIC_BUFSIZE - BUFF_THRESS)))
        return 0;
#endif
    if (aacd->bytes_left > 0) {
        memmove(aacd->music_buffer, aacd->read_ptr, aacd->bytes_left);
    }
    bytes_to_read = MUSIC_BUFSIZE - aacd->bytes_left;
#ifdef OLD_CODE
    bytes_to_read /= 512;
    bytes_to_read *= 512;
#endif
#ifdef XDEBUG
    cons_printf("%d\n", bytes_to_read);
    dly_tsk(1);
#endif

    result = f_read(aacfile, aacd->music_buffer + aacd->bytes_left, bytes_to_read, &bytes_read);
    if (result != FR_OK) {
        syslog(LOG_NOTICE, "fill_in f_read: %d", result);
        return -1;
    }

#ifdef XDEBUG
    cons_printf("read %d\n", bytes_read);
#endif
    if (bytes_read > 0) {
        aacd->read_ptr = aacd->music_buffer;
        aacd->bytes_left += bytes_read;
        aacd->offset += bytes_read;
    } else {
#ifdef DEBUG
        cons_printf("\r\033[KMaybe EOF? (%d --> %d)\n", bytes_to_read, bytes_read);
#endif
        return -1;
    }
    return 0;
}

void aacdecoder_task()
{
    int rbytes, ec, try;
    int samprate;
    pcm_data *pcm;
    Player_Status *pst;
    FIL *aac_file;
    Music_Data *aacd;
    FLGPTN ptn;
    ER err;
    FRESULT result;
    SYSTIM tstart;

    pst = get_player();
    aac_file = &pst->music_file;

    clr_flg(FLG_STOP_REQ, 0);
    clr_flg(FLG_STOP_RES, 0);
#ifdef FREE_DECODER
    if (aacdec) {
        AACFreeDecoder(aacdec);
    }
#endif
    aacd = &music_data;
    aac_reset(aacd);
    aacdec = AACInitDecoder();
    AACSetRawBlockParams(aacdec, 0, &frame_info);

    try = 0;

    ptn = 0;
    while (aac_fill_inbuffer(aac_file, aacd, try) == 0) {
        err = pol_flg(FLG_STOP_REQ, 0x03, TWF_ORW, &ptn);
#ifdef DEBUG
        if (err != E_TMOUT) {
           syslog(LOG_NOTICE, "pol_flg: %d, ptn = %d", err, ptn);
        }
#endif
        if (ptn)
            break;
        /* Decode next aac frame */
        if (tget_mpf(MPF_PCM, (VP)&pcm, 50) != E_OK) {
            syslog(LOG_NOTICE, "get_mpf failed");
            break;
        }
        ec = AACDecode(aacdec, &aacd->read_ptr, &aacd->bytes_left, pcm->raw_buffer);
        if (ec) {
            syslog(LOG_NOTICE, "ERROR (%d)", ec);
            rel_mpf(MPF_PCM, (VP)pcm);
            if (ec == ERR_AAC_INDATA_UNDERFLOW) {
                syslog(LOG_NOTICE, "AAC_INDATA_INDERFLOW, read_ptr: %x, bytes_left: %x",
                        aacd->read_ptr, aacd->bytes_left);
                try = 1;
                continue;
            }
            break;
        }
        try = 0;


        AACGetLastFrameInfo(aacdec, &frame_info);
        if (aacd->nFrames == 0) {
#ifdef VERBOSE
            syslog(LOG_NOTICE, "Bitrate: %d", frame_info.bitRate);
#endif
            if (frame_info.nChans == 2) {
                samprate = frame_info.sampRateOut;
            } else {
                samprate = 0;
                ec = -1;
            }
            if (ec) {
                rel_mpf(MPF_PCM, (VP)pcm);
                break;
            }
#ifdef VERBOSE
            syslog(LOG_NOTICE, "chan: %d, samprate: %d", frame_info.nChans, samprate);
            syslog(LOG_NOTICE, "Bits/Sample: %d", frame_info.bitsPerSample);
            syslog(LOG_NOTICE, "Samples: %d", frame_info.outputSamps);
#endif
            pst->cur_rate = samprate;
            (pst->pdevfuncs[pst->o_device]->rate_set)(samprate);

            get_tim(&tstart);
            pst->play_start = tstart;
        }
        if (ec == 0) {
            pcm->rate = samprate;
            pcm->samples = frame_info.outputSamps;
            aacd->nFrames++;
            if (tsnd_dtq(DTQ_PCM, (VP_INT)pcm, 500) != E_OK) {
                syslog(LOG_NOTICE, "tnsd_dtq failed.");
            }
#ifdef XDEBUG
cons_printf("\r\033[K%d (%d)\r", aacd->nFrames, aacd->offset);
#endif
        }
    }
    /* Send special frame indicating EOF */
    snd_dtq(DTQ_PCM, (VP_INT)NULL);
    syslog(LOG_NOTICE, "NULL sent to DTQ_PCM");
    result = f_close(aac_file);
    if (result != FR_OK)
        syslog(LOG_NOTICE, "f_close: %d", result);
    if (ptn == 0) {
        snd_dtq(DTQ_PCOM, (VP_INT) C_NEXT);
        wai_flg(FLG_STOP_REQ, 0x03, TWF_ORW, &ptn);
    }
}

#ifdef DEBUG_COMMAND
static char *aacfname;
static int dummy_send = 0;
extern char *find_aacfile(char *cp);

void aac_dummyplayer_task()
{
    int rbytes, ec, try;
    int samprate;
    SYSTIM time1, time2;
    pcm_data *pcm;
    Music_Data *aacd;
    Player_Status *pst;

    pst = get_player();
    get_tim(&time1);

    if (aacdec) {
        AACFreeDecoder(aacdec);
    }
    aacdec = AACInitDecoder();

    if (f_open(&pst->music_file, aacfname, FA_OPEN_EXISTING|FA_READ) != FR_OK) {
        cons_printf("can't open AAC file.\n");
        return;
    }

    aac_setup_raw();
    mpeg4_decode(pst, &frame_info);
    aacd = &music_data;
    aac_reset(aacd);

    try = 0;
    while (aac_fill_inbuffer(&pst->music_file, aacd, try) == 0) {
        /* Decode next aac frame */
        get_mpf(MPF_PCM, (VP)&pcm);
        ec = AACDecode(aacdec, &aacd->read_ptr, &aacd->bytes_left, pcm->raw_buffer);
        if (ec) {
            syslog(LOG_NOTICE, "ERROR (%d)", ec);
            rel_mpf(MPF_PCM, (VP)pcm);
            if (ec == ERR_AAC_INDATA_UNDERFLOW) {
                syslog(LOG_NOTICE, "AAC_INDATA_INDERFLOW, read_ptr: %x, bytes_left: %x",
                    aacd->read_ptr, aacd->bytes_left);
                try = 1;
                continue;
            }
            break;
        }
        try = 0;

        if (aacd->nFrames == 0) {
            AACGetLastFrameInfo(aacdec, &frame_info);
#ifdef VERBOSE
            cons_printf("Bitrate: %d\n", frame_info.bitRate);
#endif
            if (frame_info.nChans == 2) {
                samprate = frame_info.sampRateOut;
            } else {
                samprate = 0;
                ec = -1;
            }
            if (ec) {
                rel_mpf(MPF_PCM, (VP)pcm);
                break;
            }
            cons_printf("chan: %d, samprate: %d\n", frame_info.nChans, samprate);
            cons_printf("Bits/Sample: %d\n", frame_info.bitsPerSample);
            cons_printf("Samples: %d\n", frame_info.outputSamps);
            codec_set_rate(samprate);
        }
        AACGetLastFrameInfo(aacdec, &frame_info);
        if (ec == 0) {
            pcm->rate = samprate;
            pcm->samples = frame_info.outputSamps;
            aacd->nFrames++;
            if (dummy_send)
                rel_mpf(MPF_PCM, (VP)pcm);
            else
                snd_dtq(DTQ_PCM, (VP_INT)pcm);
        }
    }

    get_tim(&time2);
    time2 -= time1;
    cons_printf("\nTotal %d frames, %d.%d sec elapsed.\n", aacd->nFrames, time2/1000, time2%1000);

    f_close(&pst->music_file);
}

void aplay_command(int argc, char **argv)
{
    char *fname;
    Player_Status *pst;

    if (argc > 1) {
        fname = argv[1];
        if (strcmp(fname, "dummy") == 0) {
            dummy_send = 1;
            return;
        } else if (strcmp(fname, "real") == 0) {
            dummy_send = 0;
            return;
        }
    }

    pst = get_player();
    pst->o_device = O_CODEC;
    do_mount();
    fname = "test.aac";
    if (argc > 1) {
        fname = find_aacfile(argv[1]);
        if (fname == NULL) {
            cons_printf("no such file.\n");
            return;
        }
    }
    aacfname = fname;
    aac_dummyplayer_task();
}
#else
void aplay_command(int argc, char **argv)
{
    syslog(LOG_NOTICE, "chan: %d, samprate: %d", frame_info.nChans, frame_info.sampRateOut);
    syslog(LOG_NOTICE, "Bits/Sample: %d", frame_info.bitsPerSample);
    syslog(LOG_NOTICE, "Samples: %d", frame_info.outputSamps);
}
#endif
