/*
 * Class:     com_spoledge_aacdecoder_Decoder
 * Method:    nativeDecode
 * Signature: (I[SI)I
 */
JNIEXPORT jlong JNICALL Java_com_spoledge_aacdecoder_Decoder_nativeDecode
  (JNIEnv *env, jobject thiz, jlong jinfo, jshortArray outBuf, jint outLen)
{
    AACDInfo *info = (AACDInfo*) jinfo;
    info->env = env;

    // prepare internal output buffer :
    jshort *jsamples = aacd_prepare_samples( info, outLen );

    aacd_decode( info, jsamples, outLen );

    // copy samples back to Java heap:
    (*env)->SetShortArrayRegion( env, outBuf, 0, info->round_samples, jsamples );

    aacd_decode_info2java( info );

    info->env = NULL;

    return (jlong) info->round_samples;
}
static long aacd_opencore_start( AACDInfo *info, unsigned char *buffer, unsigned long buffer_size)
{
    AACD_TRACE( "start() buffer=%x size=%d", (*(unsigned long*)buffer), buffer_size );

    AACDOpenCore *oc = (AACDOpenCore*) info->ext;
    tPVMP4AudioDecoderExternal *pExt = oc->pExt;

    pExt->remainderBits             = 0;
    pExt->frameLength               = 0;

    // prepare the first samples buffer:
    //pExt->pOutputBuffer             = malloc(4096 * sizeof(int16_t));
    //pExt->pOutputBuffer_plus        = pExt->pOutputBuffer + 2048;
    pExt->pOutputBuffer             = aacd_prepare_samples( info, 4096 );
    pExt->pOutputBuffer_plus        = pExt->pOutputBuffer + 2048;

    int32_t status;
    int frameDecoded = 0;
    int attempts = 16;

    /* pre-init search adts sync */
    while (pExt->frameLength == 0 && attempts--) {
        pExt->pInputBuffer              = buffer;
        pExt->inputBufferMaxLength      = buffer_size;
        pExt->inputBufferCurrentLength  = buffer_size;
        pExt->inputBufferUsedLength     = 0;

        status = PVMP4AudioDecoderConfig(pExt, oc->pMem);
        AACD_DEBUG( "start() Status[0]: %d", status );

        if (status != MP4AUDEC_SUCCESS) {
            status = PVMP4AudioDecodeFrame(pExt, oc->pMem);
            AACD_DEBUG( "start() Status[1]: %d", status );

            buffer -= pExt->inputBufferUsedLength;
            buffer_size -= pExt->inputBufferUsedLength;

            if (MP4AUDEC_SUCCESS == status) {
                AACD_DEBUG( "start() frameLength: %d\n", pExt->frameLength);
                frameDecoded = 1;
                continue;
            }
        }

        if (buffer_size <= PVMP4AUDIODECODER_INBUFSIZE) break;
    }

    if (!frameDecoded)
    {
        AACD_INFO( "start() No stream info available - trying to decode a frame" );

        if (buffer_size >= PVMP4AUDIODECODER_INBUFSIZE) status = PVMP4AudioDecodeFrame(pExt, oc->pMem);
        else AACD_WARN( "start() Input buffer too small" );
    }

    //free( pExt->pOutputBuffer );

    if (status != MP4AUDEC_SUCCESS)
    {
        AACD_ERROR( "start() init failed status=%d", status );
        return -1;
    }

    AACD_DEBUG( "start() bytesconsumed=%d", pExt->inputBufferUsedLength );

    int streamType  = -1;

    if ((pExt->extendedAudioObjectType == MP4AUDIO_AAC_LC) ||
            (pExt->extendedAudioObjectType == MP4AUDIO_LTP))
    {
        streamType = AAC;
    }
    else if (pExt->extendedAudioObjectType == MP4AUDIO_SBR)
    {
        streamType = AACPLUS;
    }
    else if (pExt->extendedAudioObjectType == MP4AUDIO_PS)
    {
        streamType = ENH_AACPLUS;
    }

    AACD_DEBUG( "start() streamType=%d", streamType );

    if ((AAC == streamType) && (2 == pExt->aacPlusUpsamplingFactor))
    {
        AACD_INFO( "start() DisableAacPlus" );
        PVMP4AudioDecoderDisableAacPlus(pExt, oc->pMem);
    }

    info->samplerate = pExt->samplingRate;
    info->channels = pExt->desiredChannels;

    oc->frameSamplesFactor = pExt->desiredChannels;
    if (2 == pExt->aacPlusUpsamplingFactor) oc->frameSamplesFactor *= 2;

    info->frame_bytesconsumed = pExt->inputBufferUsedLength;
    info->frame_samples = pExt->frameLength * oc->frameSamplesFactor;

    return pExt->inputBufferUsedLength;
}
static long aacd_opencoremp3_start( AACDInfo *info, unsigned char *buffer, unsigned long buffer_size)
{
    AACD_TRACE( "start() buffer=%x size=%d", (*(unsigned long*)buffer), buffer_size );

    AACDOpenCoreMP3 *oc = (AACDOpenCoreMP3*) info->ext;
    tPVMP3DecoderExternal *pExt = oc->pExt;

    // prepare the first samples buffer:
    //pExt->pOutputBuffer             = malloc(4096 * sizeof(int16_t));
    pExt->pOutputBuffer             = aacd_prepare_samples( info, 4096 );
    pExt->outputFrameSize           = 4096;

    pExt->pInputBuffer              = buffer;
    pExt->inputBufferMaxLength      = buffer_size;
    pExt->inputBufferCurrentLength  = buffer_size;
    pExt->inputBufferUsedLength     = 0;

    pExt->crcEnabled                = 0;
    pExt->equalizerType             = flat;
    pvmp3_InitDecoder( oc->pExt, oc->pMem );

    int32_t status;
    int frameDecoded = 0;
    int attempts = 16;
    int totalConsumed = 0;
    pExt->outputFrameSize           = 0;

    /* pre-init search adts sync */
    while (!frameDecoded && attempts--) {
        pExt->pInputBuffer              = buffer;
        pExt->inputBufferMaxLength      = buffer_size;
        pExt->inputBufferCurrentLength  = buffer_size;
        pExt->inputBufferUsedLength     = 0;
        pExt->outputFrameSize           = 4096;

        status = pvmp3_framedecoder(pExt, oc->pMem);
        AACD_DEBUG( "start() Status[0]: %d - consumed %d bytes", status, pExt->inputBufferUsedLength );

        totalConsumed += pExt->inputBufferUsedLength;

        if (status != NO_DECODING_ERROR) {
            AACD_ERROR( "start() frame decode error=%d", status );

            if (!pExt->inputBufferUsedLength) {
                AACD_ERROR( "start() first frame cannot be decoded - trying to sync again" );

                int move = buffer_size < 2048 ? (buffer_size >> 1) : 1024;
                buffer += move;
                buffer_size -= move;
                totalConsumed += move;

                pExt->pInputBuffer              = buffer;
                pExt->inputBufferMaxLength      = buffer_size;
                pExt->inputBufferCurrentLength  = buffer_size;

                ERROR_CODE err = pvmp3_frame_synch( oc->pExt, oc->pMem );

                if (err == SYNCH_LOST_ERROR) {
                    AACD_ERROR( "start() cannot re-sync the stream after next %d bytes, status=%d", move, err );
                }
                else if (err != NO_DECODING_ERROR) {
                    AACD_ERROR( "start() cannot sync the stream status=%d", err );
                    break;
                }
                else {
                    totalConsumed += pExt->inputBufferUsedLength;
                    AACD_INFO( "start() sync was successful - used bytes=%d", totalConsumed );
                }
            }
            buffer -= pExt->inputBufferUsedLength;
            buffer_size -= pExt->inputBufferUsedLength;
        }