/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  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.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "gles_input.h"
#include "gles_engine.h"

#ifdef ANDROID
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif


#ifdef ANDROID
static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
#else
static void bufferQueueCallback(SLBufferQueueItf, void *context)
#endif
{
	GlesInput *audioInput = reinterpret_cast<GlesInput*>(context);
	
    audioInput->processBuffer();
}

GlesInput::GlesInput()
: m_recorderObject(0)
, m_recorder(0)
, m_bufferQueue(0)
, m_bufferSize(1600)
, m_currentBuffer(0)
, m_pCallback(NULL)
, m_pUserdata(NULL)
{
#ifdef ANDROID	
	m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
#endif

	for (int i = 0; i < NUM_BUFFERS; i++)
	{
		m_buffers[i] = NULL;
	}
}

GlesInput::~GlesInput()
{
	stop();
}

bool GlesInput::start(int samplerete)
{
	bool ret = false;
	
	if (startRecording(samplerete))
	{
		ret = true;
	}
	else
	{
        log_print(HT_LOG_ERR, "start audio recoding faild\r\n");
		
		stopRecording();		
	}

	return ret;
}

void GlesInput::stop()
{
    stopRecording();
}

bool GlesInput::startRecording(int samplerete)
{
	SLEngineItf engine = GlesEngine::instance()->slEngine();
    if (!engine) 
    {
        log_print(HT_LOG_ERR, "No engine\r\n");
        return false;
    }

    SLresult result;

    // configure audio source
    SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
                                       SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
    SLDataSource audioSrc = { &loc_dev, NULL };

    // configure audio sink
#ifdef ANDROID
    SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                      NUM_BUFFERS };
#else
    SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
#endif

    SLDataFormat_PCM format_pcm;
    
    format_pcm.formatType = SL_DATAFORMAT_PCM;
    format_pcm.numChannels = 2;
    format_pcm.samplesPerSec = samplerete * 1000;
    format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
    format_pcm.channelMask = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
    format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    
    SLDataSink audioSnk = { &loc_bq, &format_pcm };

    // create audio recorder
    // (requires the RECORD_AUDIO permission)
#ifdef ANDROID
    const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
    const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
#else
    const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE };
    const SLboolean req[1] = { SL_BOOLEAN_TRUE };
#endif

    result = (*engine)->CreateAudioRecorder(engine, &m_recorderObject,
                                            &audioSrc, &audioSnk,
                                            sizeof(req) / sizeof(SLboolean), id, req);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "create audio recorder failed\r\n");
        return false;
    }

#ifdef ANDROID
    // configure recorder source
    SLAndroidConfigurationItf configItf;
    result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION,
                                               &configItf);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "get audio configuration interface failed\r\n");
        return false;
    }

    result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
                                            &m_recorderPreset, sizeof(SLuint32));

    SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE;
    SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big
    result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
                                            &presetSize, (void*)&presetValue);

    if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) 
    {
        log_print(HT_LOG_ERR, "set audio record configuration failed\r\n");
        return false;
    }
#endif

    // realize the audio recorder
    result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "realize audio record object failed\r\n");
        return false;
    }

    // get the record interface
    result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "get audio record object failed\r\n");
        return false;
    }

    // get the buffer queue interface
#ifdef ANDROID
    SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
#else
    SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE;
#endif
    result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "get audio record buff queue failed\r\n");
        return false;
    }

    // register callback on the buffer queue
    result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, ::bufferQueueCallback, this);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "set buffer queue callback failed\r\n");
        return false;
    }

    if (m_bufferSize <= 0) 
    {
        m_bufferSize = (16*2/8)*50*samplerete/1000;
    } 
    else 
    {
        int minimumBufSize = (16*2/8)*5*samplerete/1000;
        if (m_bufferSize < minimumBufSize)
            m_bufferSize = minimumBufSize;
    }

    // enqueue empty buffers to be filled by the recorder
    for (int i = 0; i < NUM_BUFFERS; ++i) 
    {
    	m_buffers[i] = (uint8 *)malloc(m_bufferSize);
    	if (NULL == m_buffers[i])
    	{
    		return false;
    	}

    	memset(m_buffers[i], 0, m_bufferSize);
    	
        result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i], m_bufferSize);
        if (result != SL_RESULT_SUCCESS) 
        {
            log_print(HT_LOG_ERR, "audio record enqueue failed\r\n");
            return false;
        }
    }

    // start recording
    result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
    if (result != SL_RESULT_SUCCESS) 
    {
        log_print(HT_LOG_ERR, "set audio record state failed\r\n");
        return false;
    }

    return true;
}

void GlesInput::stopRecording()
{
	if (m_recorder)
	{
    	(*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED);
    }

    if (m_bufferQueue)
    {
    	(*m_bufferQueue)->Clear(m_bufferQueue);
    }

	if (m_recorderObject)
	{
    	(*m_recorderObject)->Destroy(m_recorderObject);
    }
    
    m_recorder = NULL;
    m_bufferQueue = NULL;
    m_recorderObject = NULL;

    for (int i = 0; i < NUM_BUFFERS; ++i)
    {
    	if (m_buffers[i])
    	{
    		free(m_buffers[i]);
    		m_buffers[i] = NULL;
    	}
    }
    
    m_currentBuffer = 0;    
}

void GlesInput::setBufferSize(int value)
{
    m_bufferSize = value;
}

int GlesInput::bufferSize() const
{
    return m_bufferSize;
}

void GlesInput::processBuffer()
{
    uint8 *processedBuffer = m_buffers[m_currentBuffer];

	if (m_pCallback)
	{
		m_pCallback(processedBuffer, m_bufferSize, m_pUserdata);
    }

    // Re-enqueue the buffer
    SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
                                                processedBuffer,
                                                m_bufferSize);

    m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS;

    // If the buffer queue is empty (shouldn't happen), stop recording.
#ifdef ANDROID
    SLAndroidSimpleBufferQueueState state;
#else
    SLBufferQueueState state;
#endif
    result = (*m_bufferQueue)->GetState(m_bufferQueue, &state);
    if (result != SL_RESULT_SUCCESS || state.count == 0) 
    {
        log_print(HT_LOG_ERR, "processBuffer::state.count == 0\r\n");
    }
}

void GlesInput::setCallback(GlesInputCB cb, void * puser)
{
	m_pCallback = cb;
	m_pUserdata = puser;
}



