/*
 *******************************************************************************
 *  Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
 *
 *  +-----------------------------------------------------------------+
 *  | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
 *  | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH  |
 *  | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
 *  | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE   |
 *  | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE   |
 *  | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED.        |
 *  |                                                                 |
 *  | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT   |
 *  | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
 *  | VATICS(KNERON) INC.                                             |
 *  +-----------------------------------------------------------------+
 *
 *******************************************************************************
 */
#ifndef FDK_AAC_PROCESSOR_H
#define FDK_AAC_PROCESSOR_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fdk-aac/aacdecoder_lib.h>


#ifdef __cplusplus
extern "C" {
#endif

typedef struct AAC4_DECODER_INITOPT_T
{
	unsigned int dwChannels;
	unsigned int dwSampleRate;
	unsigned int dwAdts;
	size_t dwSpecConfSize;
	unsigned char *pbySpecConf;

} AAC4_DECODER_INITOPT_T;

typedef struct AAC4_DECODER_DECODED_STREAM_INFO_T
{
	unsigned int dwSampleRate;
	unsigned int dwFrameSize;
	unsigned int dwChannels;
} AAC4_DECODER_DECODED_STREAM_INFO_T;


typedef struct AAC4_DECODER_HANDLE_T
{
	HANDLE_AACDECODER ptAacDecHandle;

	unsigned int dwChannels;
	unsigned int dwSampleRate;
	unsigned int dwAdts;
	size_t dwSpecConfSize;
	unsigned char *pbySpecConf;

} AAC4_DECODER_HANDLE_T;

typedef enum AAC4_DECODER_CONCEAL_METHOD {
    CONCEAL_METHOD_SPECTRAL_MUTING      =  0,
    CONCEAL_METHOD_NOISE_SUBSTITUTION   =  1,
    CONCEAL_METHOD_ENERGY_INTERPOLATION =  2,
    CONCEAL_METHOD_NB,
} AAC4_DECODER_CONCEAL_METHOD;


/**
 * @brief Initializes handle for AAC4 decoder
 *
 * @param[in] channels number of output channels.
 */
static inline AAC4_DECODER_HANDLE_T *Fdk_AAC4_Decoder_Init(AAC4_DECODER_INITOPT_T *init_opt)
{
	AAC4_DECODER_HANDLE_T *handle = NULL;
	AAC_DECODER_ERROR err;
	TRANSPORT_TYPE type = TT_MP4_RAW;

	if (NULL == init_opt)
	{
		printf("(%s, %d) invalid param: init_opt is NULL!\n", __func__, __LINE__);
		return NULL;
	}
	handle = (AAC4_DECODER_HANDLE_T *) calloc(1, sizeof(AAC4_DECODER_HANDLE_T));
	if (!handle)
	{
		goto INIT_FAILED;
	}
	switch(init_opt->dwAdts) {
		case 0:
			type = TT_MP4_RAW;
			break;
		case 1:
			type = TT_MP4_ADTS;
			break;
		case 2:
			type = TT_MP4_ADIF;
			break;
	}
	handle->ptAacDecHandle = aacDecoder_Open(type, 1);
	if (!handle->ptAacDecHandle)
	{
		goto INIT_FAILED;
	}
	handle->dwChannels = init_opt->dwChannels;
	handle->dwSampleRate = init_opt->dwSampleRate;
	handle->dwAdts = init_opt->dwAdts;
	err = aacDecoder_SetParam(handle->ptAacDecHandle, AAC_PCM_MAX_OUTPUT_CHANNELS, init_opt->dwChannels);
	if (err != AAC_DEC_OK)
	{
		printf("(%s, %d) aacDecoder_SetParam(AAC_PCM_MAX_OUTPUT_CHANNELS(%u)) failed with err(%x)\n", __func__, __LINE__, init_opt->dwChannels, err);
		goto INIT_FAILED;
	}
	err = aacDecoder_SetParam(handle->ptAacDecHandle, AAC_CONCEAL_METHOD, CONCEAL_METHOD_NOISE_SUBSTITUTION);
	if (err != AAC_DEC_OK)
	{
		printf("(%s, %d) aacDecoder_SetParam(AAC_CONCEAL_METHOD(CONCEAL_METHOD_NOISE_SUBSTITUTION)) failed with err(%x)\n", __func__, __LINE__, err);
		goto INIT_FAILED;
	}
	if (!init_opt->dwAdts)
	{
		handle->pbySpecConf = (uint8_t *) malloc(init_opt->dwSpecConfSize * sizeof(uint8_t));
		if(handle->pbySpecConf == NULL)
		{
			goto INIT_FAILED;
		}
		handle->dwSpecConfSize = init_opt->dwSpecConfSize;
		memcpy(handle->pbySpecConf, init_opt->pbySpecConf, handle->dwSpecConfSize);

		err = aacDecoder_ConfigRaw(handle->ptAacDecHandle, &init_opt->pbySpecConf, (const UINT *)&init_opt->dwSpecConfSize);
		if (err != AAC_DEC_OK)
		{
			printf("(%s, %d) aacDecoder_ConfigRaw(handle->dwSpecConfSize(%d)) failed with err(%x)\n", __func__, __LINE__, handle->dwSpecConfSize, err);
			goto INIT_FAILED;
		}
	}

	return handle;

INIT_FAILED:
	if (handle)
	{
		if (handle->pbySpecConf)
		{
			free(handle->pbySpecConf);
			handle->pbySpecConf = NULL;
		}
		if (handle->ptAacDecHandle)
		{
			aacDecoder_Close(handle->ptAacDecHandle);
			handle->ptAacDecHandle = NULL;
		}
		free(handle);
		handle = NULL;
	}

	return NULL;
}



/**
 * @brief Decodes AAC4-encoded bit streams to 16-bit linear PCM
 *
 * @param[in] handle AAC4 decoder handle.
 * @param[in] in_buf The input buffer of the AAC4 audio data.
 * @param[in] inbuf_size The input buffer size of the AAC4 audio data.
 * @param[in, out] out_buf The output buffer for the PCM data.
 * @param[in] outbuf_size The total bytes of the output buffer.
 * @return[int] decoded PCM data size
 */
static inline int Fdk_AAC4_Decoder_OneFrame(
	AAC4_DECODER_HANDLE_T *handle,
	uint8_t* in_buf, int inbuf_size,
	uint8_t* out_buf, int outbuf_size)
{
	AAC_DECODER_ERROR err;
	//int ret = 0;
	int size = inbuf_size;
	UINT valid = inbuf_size;

	if (NULL == handle || NULL == handle->ptAacDecHandle)
	{
		printf("(%s, %d) handle(%p)\n", __func__, __LINE__, (void*)handle);
		if (handle)
		{
			printf("(%s, %d) handle->ptAacDecHandle(%p)\n", __func__, __LINE__, (void*)handle->ptAacDecHandle);
		}
		return -1;
	}
	if (NULL == in_buf)
	{
		printf("(%s, %d) in_buf(%p)\n", __func__, __LINE__, (void*)in_buf);
		return -1;
	}
	if (inbuf_size <= 0)
	{
		printf("(%s, %d) invalid inbuf_size(%d)\n", __func__, __LINE__, inbuf_size);
		return -1;
	}
	if (NULL == out_buf)
	{
		printf("(%s, %d) out_buf(%p)\n", __func__, __LINE__, (void*)out_buf);
		return -1;
	}
	if (outbuf_size <= 0)
	{
		printf("(%s, %d) invalid outbuf_size(%d)\n", __func__, __LINE__, outbuf_size);
		return -1;
	}

	err = aacDecoder_Fill(handle->ptAacDecHandle, &in_buf, (const UINT *)&size, &valid);
	if (err != AAC_DEC_OK)
	{
		printf("(%s, %d) aacDecoder_Fill() failed with err(%x)\n", __func__, __LINE__, err);
		return -1;
	}
	err = aacDecoder_DecodeFrame(handle->ptAacDecHandle, (INT_PCM *) out_buf, outbuf_size, AACDEC_INTR);
	if (err == AAC_DEC_NOT_ENOUGH_BITS)
	{
		return 0;
	}
	if (err != AAC_DEC_OK)
	{
		printf("(%s, %d) aacDecoder_DecodeFrame() failed with err(%x)\n", __func__, __LINE__, err);
		return -1;
	}

	return 0;
}

/**
 * @brief Releases handle for AAC4 decoder
 *
 * @param[in] handle AAC4 decoder handle.
 * @param[in, out] stream_info decoded PCM stream information.
 */
static inline int Fdk_AAC4_Decoder_Get_Decoded_Stream_Info(
	AAC4_DECODER_HANDLE_T *handle,
	AAC4_DECODER_DECODED_STREAM_INFO_T *stream_info)
{
	if (NULL == handle || NULL == handle->ptAacDecHandle)
	{
		printf("(%s, %d) invalid handle!\n",  __func__, __LINE__);
		return -1;
	}
	if (NULL == stream_info)
	{
		printf("(%s, %d) invalid param: stream_info is NULL!\n",  __func__, __LINE__);
		return -1;
	}

	CStreamInfo *info = aacDecoder_GetStreamInfo(handle->ptAacDecHandle);
	if (NULL == info)
	{
		printf("(%s, %d) aacDecoder_GetStreamInfo() failed\n",  __func__, __LINE__);
		return -1;
	}
	stream_info->dwChannels = info->numChannels;
	stream_info->dwSampleRate = info->sampleRate;
	stream_info->dwFrameSize = info->frameSize;

	return 0;
}


/**
 * @brief Releases handle for AAC4 decoder
 *
 * @param[in] handle AAC4 decoder handle.
 */
static inline void Fdk_AAC4_Decoder_Release(AAC4_DECODER_HANDLE_T *handle)
{
	if (NULL == handle) return;

	if (handle->pbySpecConf)
	{
		free(handle->pbySpecConf);
		handle->pbySpecConf = NULL;
	}
	if (handle->ptAacDecHandle)
	{
		aacDecoder_Close(handle->ptAacDecHandle);
		handle->ptAacDecHandle = NULL;
	}
	free(handle);
}


/**
 * @brief Releases handle for AAC4 decoder
 *
 * @param[in] handle AAC4 decoder handle.
 */
static inline int Fdk_AAC4_Decoder_Flush(AAC4_DECODER_HANDLE_T *handle)
{
	AAC_DECODER_ERROR err;
	if (NULL == handle || NULL == handle->ptAacDecHandle) return -1;

	err = aacDecoder_SetParam(handle->ptAacDecHandle, AAC_TPDEC_CLEAR_BUFFER, 1);
	if (err != AAC_DEC_OK)
	{
		printf("(%s, %d) aacDecoder_SetParam(AAC_TPDEC_CLEAR_BUFFER) failed with err(%x)\n", __func__, __LINE__, err);
		return -1;
	}

	return 0;
}



#ifdef __cplusplus
}
#endif

#endif
