#include "Drv_mic.h"
#include "ima_enc.h"
#include "system.h"
#undef INFO_HEADER
#define INFO_HEADER "MIC"

//#define TEST_AUDIO_PCM

unsigned char micRecordbuf[RECORE_BUFFER_SIZE];
unsigned char gAdcCtrl;
tMicCallback gfMicCb;
unsigned char gadcEncodeBuf[ENCODE_OUTPUT_LEN+20];
IPC_DATA_FORMAT *gIpcDataPtr;
MIC_CUR_VARIABLE sMicCurVariable;

QUEUE_HEADER VoiceDecodeDataQueue;
uint8_t VoiceDecodeDataQueueBuffer[MIC_QUEUE_MAX*MIC_ADPCM_PACKET_LEN];

/******************************Queue Function Start***********************************/
/*
 * @method queue_init
 * @brief  init queue struct.
 * @retval None
 */
bool queue_init(QUEUE_HEADER *pQheader, void *pMem, uint8_t unitLen, uint8_t len)
{
//	printf("Queue initial\r\n");
	if (pQheader == NULL || pMem == NULL || unitLen == 0 || len == 0)
	{
		return false;
	}

	pQheader->pRead = pMem;
	pQheader->pWrite = pMem;
	pQheader->pHead = pMem;
	pQheader->array_Len = len;
	pQheader->unit_Len = unitLen;
	pQheader->current_queue_len = 0;
	return true;
}

/*
 * @method queue_clear
 * @brief  clear queue pointer.
 * @retval false or true.
 */
bool queue_clear(QUEUE_HEADER *pQheader)
{
	if (pQheader == NULL)
	{
		return false;
	}

	pQheader->pRead = pQheader->pHead;
	pQheader->pWrite = pQheader->pHead;
	pQheader->current_queue_len = 0;
	return true;
}

void * Delete_Queue(QUEUE_HEADER *pQheader)
{
	void *pTemp;
	if (queue_is_empty(pQheader))
	{
		return NULL;
	}

	pTemp = pQheader->pRead;
	pQheader->pRead = (void *)(((unsigned char *)pQheader->pRead)+ pQheader->unit_Len);
	if ((uint8_t *)pQheader->pRead == ((uint8_t *)pQheader->pHead) + (pQheader->unit_Len*pQheader->array_Len))
	{
		pQheader->pRead = pQheader->pHead;
	}
	pQheader->current_queue_len--;
	return pTemp;
}

bool queue_is_empty(QUEUE_HEADER *pQheader)
{
	if (pQheader->current_queue_len == 0)
	{
		return true;
	}
	return false;
}

bool queue_is_full(QUEUE_HEADER *pQheader)
{
	if (pQheader->current_queue_len == pQheader->array_Len)
	{
		return true;
	}
	return false;
}

bool Insert_Queue(QUEUE_HEADER *pQheader,void *pEle)
{
	uint8_t i;
	//unsigned char * pTemp;
	if (queue_is_full(pQheader))
	{
		return false;
	}

	memcpy(pQheader->pWrite,pEle,pQheader->unit_Len);
	pQheader->pWrite = (void *)(((uint8_t *)pQheader->pWrite)+pQheader->unit_Len);
	if ((uint8_t *)pQheader->pWrite == ((uint8_t *)pQheader->pHead) + (pQheader->unit_Len*pQheader->array_Len))
	{
		pQheader->pWrite = pQheader->pHead;
	}
	pQheader->current_queue_len++;
	return true;
}


/******************************Queue Function End***********************************/


void Drv_adcdma_init(DRV_ADC_CONFIG *pAdcConfig)
{
	unsigned short buffenEnd;
	int i,j;	
	buffenEnd = ((unsigned short)((uint32_t)pAdcConfig->mDmaBuf + pAdcConfig->mDmaBufLen) & 0xFFFF);
	//mic bias
	HWRITE(0x8977,0x2a);
	HWRITE(0x8978,0xa0);// three bits means mic suply high
	HWRITE(0x8979,0xE0);
	HWRITE(0x897a,0x68);
	HWRITE(0x897b,0xa8);
	HWRITE(0x897c,0x55);
	HWRITE(0x897d,0x18);
	HWRITE(0x897e,0xF3);
	HWRITE(0x897f,0xE1);
	HWRITE(0x8980,0xFF);
	HWRITE(0x8981,0x30);

	HWRITE(0x897f,0xE3);
	HWRITE(0x897f,0xE1);
	HWRITE(0x897e,0xf3);	
	HWRITE(0x897e,0xE3);
	HWRITE(0x897e,0xf3);	
	
	gAdcCtrl = pAdcConfig->mAdcConfig;
	
	HWRITE(CORE_MIC_HPF_CTRL,0x10);
	
	HWRITEW(CORE_ADCD_SADDR, pAdcConfig->mDmaBuf);
	HWRITEW(CORE_ADCD_EADDR, buffenEnd-1);
}

void Drv_adc_enable(void)
{
	for (int j =100*1000;j>0;j--);//mic need 320/7ms to wake
	HWRITE(CORE_ADCD_CTRL, DRV_ADC_ENABLE | gAdcCtrl);
	return;
}

unsigned short Drv_adc_getWptr(void)
{
	return HREADW(CORE_ADCD_ADDR);	
}

void Drv_adc_disable(void)
{
	HWRITE(CORE_ADCD_CTRL, gAdcCtrl);
	return;
}

void DRV_Mic_Init(void)
{
	DRV_ADC_CONFIG adcCfg;
	gIpcDataPtr = (IPC_DATA_FORMAT*)&gadcEncodeBuf;
	
	sMicCurVariable.mEndPtr =(unsigned char *)( ((int)micRecordbuf +  RECORE_BUFFER_SIZE) );
	memset(micRecordbuf, 0, RECORE_BUFFER_SIZE);
	sMicCurVariable.mMicEnable = MIC_DISABLE;
	sMicCurVariable.mReadBufPtr = micRecordbuf;
	sMicCurVariable.mReadPtr = micRecordbuf;

	adcCfg.mDmaBuf = micRecordbuf;
	adcCfg.mDmaBufLen = RECORE_BUFFER_SIZE;
	adcCfg.mAdcConfig = DRV_ADC_M0RAM_ENABLE;
	adcCfg.mAdcConfig = DRV_ADC_M0RAM_ENABLE|DRV_ADC_16K_FILTer;
	adcCfg.mChannelCfg = DRV_ADC_CHANNEL_DIFF_GPIO1213;
	adcCfg.mSampleDelay = 0;
	Drv_adcdma_init(&adcCfg);
}

void DRV_Mic_DeInit(void)
{
	int len;
	len = (int)(sMicCurVariable.mReadBufPtr - sMicCurVariable.mEndPtr);
	memset(sMicCurVariable.mReadBufPtr, 0, len);
	return;
}

void DRV_Mic_sendEnable()
{
	sMicCurVariable.mMicBleEnable= MIC_ENABLE;
}

void DRV_Mic_sendDisable()
{
	sMicCurVariable.mMicBleEnable= MIC_DISABLE;
}


void DRV_Mic_Enable(void)
{
	int len;
	if(sMicCurVariable.mMicEnable == MIC_ENABLE)
		return;
	sMicCurVariable.mMicEnable = MIC_ENABLE;
	sMicCurVariable.mReadPtr = sMicCurVariable.mReadBufPtr;
	Drv_adc_enable();
}

void DRV_Mic_Disable(void)
{
	sMicCurVariable.mMicEnable = MIC_DISABLE;
	Drv_adc_disable();
} 

MIC_STATUS_ENUM DRV_Mic_State(void)
{
	return sMicCurVariable.mMicEnable;
}

void Drv_micQueueInit()
{
	queue_init(&VoiceDecodeDataQueue, VoiceDecodeDataQueueBuffer,MIC_ADPCM_PACKET_LEN, MIC_QUEUE_MAX);
}

bool Drv_micQueueEmpty(QUEUE_HEADER *pQheader)
{
	if (queue_is_empty(&VoiceDecodeDataQueue)){
		//Lpm_unLockLpm(VOICE_LPM_FLAG);
		return true;
	}
	else
		return false;
}

void Drv_micClearQueue()
{
	queue_clear(&VoiceDecodeDataQueue);
}

bool Drv_micInsertQueue(QUEUE_HEADER *pQheader,void *pEle)
{
	if(Insert_Queue(pQheader,pEle))
		return true;
	else
		return false;
}
void Drv_voiceDecodeDataSend()
{
	while(IPC_TxBufferIsEnough(MIC_ADPCM_PACKET_LEN)
		&& (!Drv_micQueueEmpty(&VoiceDecodeDataQueue)))
	{
		 	IPC_DATA_FORMAT *tmp=Delete_Queue(&VoiceDecodeDataQueue);
		 	(*gfMicCb)(tmp,ENCODE_OUTPUT_LEN); 		
	}
}


void DRV_Mic_multiply_2(s16 *data,int datalen)
{
	for(int i=0;i<datalen;i++){
		int tmp=*(data+i);
		tmp <<=1;
		if(tmp>32767)tmp=32767;
		if(tmp<-32768)tmp=-32768;
		*(data+i)=(s16)tmp;
	}
}

int DRV_Mic_Get_Buffer_Len()
{
	int audioWPtr = HREADW(CORE_ADCD_ADDR) ;
	int audioRPtr = TO_16BIT_ADDR(sMicCurVariable.mReadPtr);
	int audioBufferLen = (int)(sMicCurVariable.mEndPtr - sMicCurVariable.mReadBufPtr);
	if(audioRPtr <= audioWPtr)
	{
		return (audioWPtr - audioRPtr);
	}
	else
	{
		return (audioBufferLen - (audioRPtr - audioWPtr));
	}
}


void DRV_Mic_Encode_ADC()
{
	//check enable
	if (sMicCurVariable.mMicBleEnable == MIC_DISABLE)
		return;
	int audioWPtr = Drv_adc_getWptr();
	//check mic adc buf
	int bufferLen = DRV_Mic_Get_Buffer_Len();
	
	if (bufferLen != 0 && bufferLen >= ENCODE_PACKETS_LEN)
	{
		for (int i =0; i<ENCODE_PACKET_NUM;i++)
		{
			#ifdef TEST_AUDIO_PCM
			for(int indexL = 0; indexL < ENCODE_INPUT_LEN; indexL++)
			{
				MyPrintf("%c",*(sMicCurVariable.mReadPtr + indexL));
			}
			#endif
			DRV_Mic_multiply_2((s16*)sMicCurVariable.mReadPtr,ENCODE_INPUT_LEN/2);
			//����
			encode(&mg, (s16*)sMicCurVariable.mReadPtr, (ENCODE_INPUT_LEN/2), 
				&gIpcDataPtr->ipcUnion.uBleData.data);

			while(!Drv_micInsertQueue(&VoiceDecodeDataQueue,gIpcDataPtr))
			{
				Drv_voiceDecodeDataSend();
			}
			Drv_voiceDecodeDataSend();
			sMicCurVariable.mReadPtr+=ENCODE_INPUT_LEN;	
			if (sMicCurVariable.mReadPtr == sMicCurVariable.mEndPtr){	
				sMicCurVariable.mReadPtr = sMicCurVariable.mReadBufPtr;
			}
		}
}
}