/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
/*

 Filename: get_prog_config.cpp

------------------------------------------------------------------------------
 INPUT AND OUTPUT DEFINITIONS

 Inputs:
    pVars      = pointer to the structure that holds all information for
                 this instance of the library. pVars->prog_config is directly
                 used, and pVars->mc_info, pVars->prog_config, pVars->winmap,
                 pVars->SFBWidth128 are needed indirectly for calling
                 set_mc_info. Data type  pointer to tDec_Int_File structure.

    pScratchPCE = pointer to a temporary ProgConfig structure to be used
                  to read in the program configuration element.

 Local Stores/Buffers/Pointers Needed: None

 Global Stores/Buffers/Pointers Needed: None

 Outputs:
    status     = zero if no error was found, non-zero otherwise.

 Pointers and Buffers Modified:
    pVars->prog_config contents are updated with the PCE read in.
    pVars->mc_info contents are updated with channel information.
    pVars->winmap contents are updated with window information.
    pVars->SFBWidth128 contents are updated with scale factor band width data.

 Local Stores Modified: None

 Global Stores Modified: None

------------------------------------------------------------------------------
 FUNCTION DESCRIPTION

 This function reads from the input stream to memory for a temporary
 program configuration element (PCE). If the PCE read is the first
 encountered it is saved. Or, if the tag of the PCE read matches the tag of
 the first PCE encounted, it is saved as well. This is a mechanism for
 changing the sampling rate.

------------------------------------------------------------------------------
 REQUIREMENTS

 This function shall not use static or global variables.

------------------------------------------------------------------------------
 REFERENCES

 (1) ISO/IEC 13818-7:1997 Titled "Information technology - Generic coding
   of moving pictures and associated audio information - Part 7: Advanced
   Audio Coding (AAC)", Table 6.21 - Syntax of program_config_element(),
   page 16, and section 8.5 "Program Config Element (PCE)", page 30.

 (2) MPEG-2 NBC Audio Decoder
   "This software module was originally developed by AT&T, Dolby
   Laboratories, Fraunhofer Gesellschaft IIS in the course of development
   of the MPEG-2 NBC/MPEG-4 Audio standard ISO/IEC 13818-7, 14496-1,2 and
   3. This software module is an implementation of a part of one or more
   MPEG-2 NBC/MPEG-4 Audio tools as specified by the MPEG-2 NBC/MPEG-4
   Audio standard. ISO/IEC gives users of the MPEG-2 NBC/MPEG-4 Audio
   standards free license to this software module or modifications thereof
   for use in hardware or software products claiming conformance to the
   MPEG-2 NBC/MPEG-4 Audio  standards. Those intending to use this software
   module in hardware or software products are advised that this use may
   infringe existing patents. The original developer of this software
   module and his/her company, the subsequent editors and their companies,
   and ISO/IEC have no liability for use of this software module or
   modifications thereof in an implementation. Copyright is not released
   for non MPEG-2 NBC/MPEG-4 Audio conforming products.The original
   developer retains full right to use the code for his/her own purpose,
   assign or donate the code to a third party and to inhibit third party
   from using the code for non MPEG-2 NBC/MPEG-4 Audio conforming products.
   This copyright notice must be included in all copies or derivative
   works."
   Copyright(c)1996.


------------------------------------------------------------------------------
 PSEUDO-CODE

    status          = SUCCESS;
    pInputStream   = &(pVars->inputStream);


    CALL getbits(
        neededBits = LEN_TAG,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( tag = returnValue )

    CALL getbits(
        neededBits = LEN_PROFILE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( pScratchPCE->profile = returnValue )

    CALL getbits(
        neededBits = LEN_PROFILE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( pScratchPCE->sampling_rate_idx = returnValue )

    CALL getbits(
        neededBits = LEN_NUM_ELE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->front.num_ele = temp;

    CALL getbits(
        neededBits = LEN_NUM_ELE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->side.num_ele = temp;

    CALL getbits(
        neededBits = LEN_NUM_ELE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->back.num_ele = temp;

    CALL getbits(
        neededBits = LEN_NUM_LFE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->lfe.num_ele = temp;

    CALL getbits(
        neededBits = LEN_NUM_DAT,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->data.num_ele = temp;

    CALL getbits(
        neededBits = LEN_NUM_CCE,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( temp = returnValue )

    pScratchPCE->coupling.num_ele = temp;

    CALL getbits(
        neededBits = LEN_MIX_PRES,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( flag = returnValue )

    pScratchPCE->mono_mix.present = flag;

    IF (flag != FALSE)
    THEN
        CALL getbits(
            neededBits = LEN_TAG,
            pInputStream = pInputStream )
        MODIFYING( pInputStream )
        RETURNING( temp = returnValue )

        pScratchPCE->mono_mix.ele_tag = temp;

    ENDIF

    CALL getbits(
        neededBits = LEN_MIX_PRES,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( flag = returnValue )

    pScratchPCE->stereo_mix.present = flag;

    IF (flag != FALSE)
    THEN

        CALL getbits(
            neededBits = LEN_TAG,
            pInputStream = pInputStream )
        MODIFYING( pInputStream )
        RETURNING( temp = returnValue )

        pScratchPCE->stereo_mix.ele_tag = temp;

    ENDIF

    CALL getbits(
        neededBits = LEN_MIX_PRES,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( flag = returnValue )

    flag =
        getbits(
            LEN_MIX_PRES,
            pInputStream);

    pScratchPCE->matrix_mix.present = flag;

    IF (flag != FALSE)
    THEN
        CALL getbits(
            neededBits = LEN_MMIX_IDX,
            pInputStream = pInputStream )
        MODIFYING( pInputStream )
        RETURNING( temp = returnValue )

        pScratchPCE->matrix_mix.ele_tag = temp;

        CALL getbits(
            neededBits = LEN_PSUR_ENAB,
            pInputStream = pInputStream )
        MODIFYING( pInputStream )
        RETURNING( temp = returnValue )

        pScratchPCE->matrix_mix.pseudo_enab = temp;

    ENDIF


    CALL get_ele_list(
        pElementList = &pScratchPCE->front,
        pInputStream = pInputStream,
        enableCPE    = TRUE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->front )
    RETURNING( nothing )

    CALL get_ele_list(
        pElementList = &pScratchPCE->side,
        pInputStream = pInputStream,
        enableCPE    = TRUE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->side )
    RETURNING( nothing )

    CALL get_ele_list(
        pElementList = &pScratchPCE->back,
        pInputStream = pInputStream,
        enableCPE    = TRUE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->back )
    RETURNING( nothing )

    CALL get_ele_list(
        pElementList = &pScratchPCE->lfe,
        pInputStream = pInputStream,
        enableCPE    = FALSE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->lfe )
    RETURNING( nothing )

    CALL get_ele_list(
        pElementList = &pScratchPCE->data,
        pInputStream = pInputStream,
        enableCPE    = FALSE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->data )
    RETURNING( nothing )

    CALL get_ele_list(
        pElementList = &pScratchPCE->coupling,
        pInputStream = pInputStream,
        enableCPE    = TRUE )
    MODIFYING( pInputStream )
    MODIFYING( pScratchPCE->coupling )
    RETURNING( nothing )


    CALL byte_align(
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( nothing )

    CALL getbits(
        neededBits = LEN_COMMENT_BYTES,
        pInputStream = pInputStream )
    MODIFYING( pInputStream )
    RETURNING( numChars = returnValue )

    FOR (i = numChars; i > 0; i--)

        CALL getbits(
            neededBits = LEN_COMMENT_BYTES,
            pInputStream = pInputStream )
        MODIFYING( pInputStream )
        RETURNING( nothing )

    ENDFOR

    IF (pVars->current_program < 0)
    THEN
        pVars->current_program = tag;
    ENDIF


    IF (tag == pVars->current_program)
    THEN

        CALL pv_memcpy(
            to = &pVars->prog_config,
            from = pScratchPCE,
            n = sizeof(ProgConfig))
        MODIFYING( pVars->prog_config )
        RETURNING( nothing )

        CALL set_mc_info(
            pMC_Info = &pVars->mc_info,
            objectType = pVars->prog_config.profile + 1,
            samplin_rate_idx = pVars->prog_config.sampling_rate_idx,
            tag = pVars->prog_config.front.ele_tag[0],
            is_cpe = pVars->prog_config.front.ele_is_cpe[0],
            pWinSeqInfo = pVars->winmap,
            pSfbwidth128 = pVars->SFBWidth128)
        MODIFYING( pVars->mc_info )
        MODIFYING( pVars->winmap )
        MODIFYING( pVars->SFBWidth128 )
        RETURN( status = return_value )

    ENDIF

    MODIFY( pVars->mc_info )
    MODIFY( pVars->winmap )
    MODIFY( pVars->SFBWidth128 )
    RETURN (status)

------------------------------------------------------------------------------
*/


/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/
#include "pv_audio_type_defs.h"
#include "s_bits.h"
#include "s_elelist.h"
#include "s_tdec_int_file.h"
#include "s_tdec_int_chan.h"
#include "e_progconfigconst.h"
#include "ibstream.h"
#include "get_ele_list.h"
#include "aac_mem_funcs.h"
#include "set_mc_info.h"
#include "get_prog_config.h"

/*----------------------------------------------------------------------------
; MACROS
; Define module specific macros here
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; DEFINES
; Include all pre-processor statements here. Include conditional
; compile variables also.
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL FUNCTION DEFINITIONS
; Function Prototype declaration
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL VARIABLE DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL FUNCTION REFERENCES
; Declare functions defined elsewhere and referenced in this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/

Int get_prog_config(
    tDec_Int_File *pVars,
    ProgConfig    *pScratchPCE)
{
    Int    i;
    UInt    tag;
    Int    numChars;
    UInt    temp;
    Bool   flag;
    Int    status          = SUCCESS;
    BITS  *pInputStream   = &(pVars->inputStream);


    /*
     * The tag is used at the very end to see if this PCE is
     * the one to be used. Otherwise it does not need to be saved for the
     * the simple configurations to be used in this version of an AAC
     * decoder.
     *
     * All of the bits of this PCE must be read even if this PCE will not
     * be used. They are read into a temporary PCE, then later it is decided
     * whether to keep this PCE.
     *
     * To allow quick removal of the fields from the ProgConfig structure
     * that will probably not be used at a later date,
     * while still advancing the bitstream pointer,the return value of
     * getbits is saved into a temporary variable, then transfered to
     * the structure item.
     */
    tag =
        get9_n_lessbits(
            LEN_TAG,
            pInputStream);

    pScratchPCE->profile =
        get9_n_lessbits(
            LEN_PROFILE,
            pInputStream);

    pScratchPCE->sampling_rate_idx =
        get9_n_lessbits(
            LEN_SAMP_IDX,
            pInputStream);

    if (!pVars->adif_test && pScratchPCE->sampling_rate_idx != pVars->prog_config.sampling_rate_idx)
    {
        /* rewind the pointer as implicit channel configuration maybe the case */
        pInputStream->usedBits -= (LEN_TAG + LEN_PROFILE + LEN_SAMP_IDX);

        return (1); /*  mismatch cannot happen */
    }


    /*
     * Retrieve the number of element lists for each of
     * front, side, back, lfe, data, and coupling.
     *
     * For two-channel stereo or mono, only the data in the front needs
     * to be saved. However, ALL fields need to be skipped over in some
     * fashion. Also, the number of elements needs to be temporarily saved
     * to call get_ele_list(). If that function was changed to pass in
     * the number of points to be read, the memory set aside inside the
     * ProgConfig structure could be removed.
     */

    /*
     * The next six function calls could be combined into one, then use
     * shifts and masks to retrieve the individual fields.
     */
    temp =
        get9_n_lessbits(
            LEN_NUM_ELE,
            pInputStream);

    pScratchPCE->front.num_ele = temp;

    /* Needed only to read in the element list. */
    temp =
        get9_n_lessbits(
            LEN_NUM_ELE,
            pInputStream);

    pScratchPCE->side.num_ele = temp;

    /* Needed only to read in the element list. */
    temp =
        get9_n_lessbits(
            LEN_NUM_ELE,
            pInputStream);

    pScratchPCE->back.num_ele = temp;

    /* Needed only to read in the element list. */
    temp =
        get9_n_lessbits(
            LEN_NUM_LFE,
            pInputStream);

    pScratchPCE->lfe.num_ele = temp;

    /* Needed only to read in the element list. */
    temp =
        get9_n_lessbits(
            LEN_NUM_DAT,
            pInputStream);
    pScratchPCE->data.num_ele = temp;

    /* Needed only to read in the element list. */
    temp =
        get9_n_lessbits(
            LEN_NUM_CCE,
            pInputStream);

    pScratchPCE->coupling.num_ele = temp;

    /*
     * Read in mix down data.
     *
     * Whether these fields can be removed and have proper operation
     * will be determined at a later date.
     */

    /* Read presence of mono_mix */
    flag =
        get1bits(/*            LEN_MIX_PRES,*/
            pInputStream);

    pScratchPCE->mono_mix.present = flag;

    if (flag != FALSE)
    {
        temp =
            get9_n_lessbits(
                LEN_TAG,
                pInputStream);

        pScratchPCE->mono_mix.ele_tag = temp;

    } /* end if (flag != FALSE) */

    /* Read presence of stereo mix */
    flag =
        get1bits(/*            LEN_MIX_PRES,*/
            pInputStream);

    pScratchPCE->stereo_mix.present = flag;

    if (flag != FALSE)
    {
        temp =
            get9_n_lessbits(
                LEN_TAG,
                pInputStream);

        pScratchPCE->stereo_mix.ele_tag = temp;

    } /* end if (flag != FALSE) */

    /* Read presence of matrix mix */
    flag =
        get1bits(/*            LEN_MIX_PRES,*/
            pInputStream);

    pScratchPCE->matrix_mix.present = flag;

    if (flag != FALSE)
    {
        temp =
            get9_n_lessbits(
                LEN_MMIX_IDX,
                pInputStream);

        pScratchPCE->matrix_mix.ele_tag = temp;

        temp =
            get1bits(/*                LEN_PSUR_ENAB,*/
                pInputStream);

        pScratchPCE->matrix_mix.pseudo_enab = temp;

    } /* end if (flag != FALSE) */

    /*
     * Get each of the element lists. Only the front information will be
     * used for the PV decoder, but the usedBits field of pInputStream must
     * be advanced appropriately.
     *
     * This could be optimized by advancing the bit stream for the
     * elements that do not need to be read.
     */
    get_ele_list(
        &pScratchPCE->front,
        pInputStream,
        TRUE);

    get_ele_list(
        &pScratchPCE->side,
        pInputStream,
        TRUE);

    get_ele_list(
        &pScratchPCE->back,
        pInputStream,
        TRUE);

    get_ele_list(
        &pScratchPCE->lfe,
        pInputStream,
        FALSE);

    get_ele_list(
        &pScratchPCE->data,
        pInputStream,
        FALSE);

    get_ele_list(
        &pScratchPCE->coupling,
        pInputStream,
        TRUE);

    /*
     * The standard requests a byte alignment before reading in the
     * comment. This can be done because LEN_COMMENT_BYTES == 8.
     */
    byte_align(pInputStream);

    numChars =
        get9_n_lessbits(
            LEN_COMMENT_BYTES, pInputStream);

    /*
     * Ignore the comment - it requires 65 bytes to store (or worse on DSP).
     * If this field is restored, make sure to append a trailing '\0'
     */
    for (i = numChars; i > 0; i--)
    {
        pScratchPCE->comments[i] = (Char) get9_n_lessbits(LEN_BYTE,
                                   pInputStream);

    } /* end for */

    if (pVars->current_program < 0)
    {
        /*
         * If this is the first PCE, it becomes the current, regardless of
         * its tag number.
         */
        pVars->current_program = tag;

    } /* end if (pVars->current_program < 0) */


    if (tag == (UInt)pVars->current_program)
    {
        /*
         * This branch is reached under two conditions:
         * 1) This is the first PCE found, it was selected in the above if
         *    block. In all encoders found thus far, the tag value has been
         *    zero.
         * 2) A PCE has been sent by the encoder with a tag that matches the
         *    the first one sent. It will then be re-read. No encoder found
         *    thus far re-sends a PCE, when looking at ADIF files.
         *
         * Regardless, the temporary PCE will now be copied into the
         * the one official program configuration.
         */
        pv_memcpy(
            &pVars->prog_config,
            pScratchPCE,
            sizeof(ProgConfig));

        /* enter configuration into MC_Info structure */
        status =
            set_mc_info(
                &pVars->mc_info,
                (tMP4AudioObjectType)(pVars->prog_config.profile + 1),
                pVars->prog_config.sampling_rate_idx,
                pVars->prog_config.front.ele_tag[0],
                pVars->prog_config.front.ele_is_cpe[0],
                pVars->winmap,
                pVars->SFBWidth128);

    } /* end if (tag == pVars->current_program) */

    return (status);
}

