/*****************************************************************************
 * Copyright (C) 2018 SP Technology
 *              AISTB2 HAL implementation.
 *              SP Technology All Rights Reserved.
 *
 * Author: SP Technology
 * Date: 2019.01.14
 * Description:
 * Note:
 *****************************************************************************/

/*
 * Comment out if you want to remove log.
 */
#define LOG_TAG	"HAL_mic"


#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/time.h>

#include <cutils/trace.h>
#include <cutils/str_parms.h>
#include <utils/Log.h>

#include <binder/ProcessState.h>

#include <tinyalsa/asoundlib.h>

#include <fcntl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>

#include <amp_client.h>
#include <hardware/audio.h>

#if defined(_INTEK_PORTING_)
#include <cutils/properties.h>
#endif

#include "utils.h"
#include "EchoEffect.h"
#include "HAL_mic.h"


#ifdef __cplusplus
extern "C" {
#endif

#if defined(_INTEK_PORTING_)
#define HAL_MIC_DIRECT_STOP_START
#define HAL_MIC_ECHO_PROCESS_WO_RBUF
#endif

#define SYNA_ALSA_PARAM_PERIOD 256
#define SYNA_ALSA_PARAM_N 8
#define SYNA_ALSA_PARAM_RATE 48000

#define INPUT_BUF_LENGHT 2000
#define RAW_BUF_CNT 4
#define AEC_BUF_CNT_GOOGLE 24
#define AEC_BUF_CNT_CAF 4
#define DUMP_THREAD_MSG_Q_SIZE 16

#define SYNA_AEC_REF_ALSA_CARD 0
#define SYNA_AEC_REF_ALSA_DEV 0

#define SYNA_BUILT_IN_MIC_CARD 0
#define SYNA_BUILT_IN_MIC_DEV 1

#define FRAME_SIZE_32BIT 8

#define SAMPLES_PER_FRAME 0x1000

#if 0 //defined(_INTEK_PORTING_)
#define MAX_MIC_VOLUME_TABLE                     33
#define DEFAULT_MIC_VOLUME_LEVEL                 24
#endif

typedef enum{
    stream_raw = 0,
    stream_max
} stream_index;

typedef struct{
    uint32_t sample_rate;
    audio_channel_mask_t channel_mask;
    audio_format_t  format;
} effect_audio_config;

int effect_init();
void effect_deinit();
int stream_read(stream_index stream, unsigned char *buf, size_t bytes);


typedef enum {
    dump_index_ref,
    dump_index_effect
} dump_index_type;

typedef struct{
    dump_index_type index;
    int size;
    void* buf;
} dump_msg_type;

typedef struct {
    unsigned char *buf;
    int size;
    int rd;
    int wd;
    int fullness;
    pthread_mutex_t lock;
} ringbuf_type;

//static MIC_STATE mic_state = MIC_ON;
static int dump_effect_enable = 0;
static int dump_ref_enable = 0;

static struct pcm *ref_pcm = NULL;
static unsigned int pcm_buf_size = 0;

//dump files
static FILE* dump_effect_fp = NULL;
static FILE* dump_ref_fp = NULL;
static unsigned int dump_total = 0;
static pthread_t tid_dump;
static sync_queue* msgq_dump = NULL;

static pthread_attr_t attr;
// effect workder
static pthread_t tid_effect_worker;
static ringbuf_type ringbuf_raw;
static int effect_worker_exit = 0;
//static pthread_mutex_t effectlock;
static pthread_t tid_input_reader_ref;

static ringbuf_type ringbuf_ref;

//static effect_audio_config streamConfigs[stream_max] = {
//        {48000, AUDIO_CHANNEL_IN_MONO, AUDIO_FORMAT_PCM_16_BIT},
//};

#if defined(_INTEK_PORTING_)

#define PROPERTY_IS_MIC_OWNER                    "vendor.audio.skb.d0_owner_is"
#define PROPERTY_SET_MIC_OWNER                   "vendor.audio.skb.d0_owner_to_be"
#define OWNER_NUGU                               "nugu"
#define OWNER_KARAOKE                            "karaoke"

#define HAL_MIC_WORKER_STAE_IDLE                 0
#define HAL_MIC_WORKER_STAE_RUN                  1

#define HAL_MIC_SLEEP_TIME_MSEC                  250 /* 250ms */
#define HAL_MIC_WAIT_TIME_SEC                    2 /* sec */

#define HAL_MIC_WAIT_COUNT (HAL_MIC_WAIT_TIME_SEC*1000/HAL_MIC_SLEEP_TIME_MSEC)

#if 0
static float micVolume[MAX_MIC_VOLUME_TABLE] =
{	
	-103.0, -36.0, -35.0, -34.0, -33.0, -32.0, -31.0, -30.0, -29.0, -28.0,
	-27.0,  -26.0, -25.0, -24.0, -23.0, -22.0, -21.0, -20.0, -19.0, -18.0,
	-17.0,  -16.0, -15.0, -14.0, -13.0, -12.0, -11.0, -10.0,  -8.0,  -6.0,
	-4.0,    -2.0,   0.0,
};
static int micVolumeLv = DEFAULT_MIC_VOLUME_LEVEL;
#else
static int micVolumeLv = 100;
#endif

static int mic_act = 0;
static int mic_worker_state = HAL_MIC_WORKER_STAE_IDLE;
#if !defined(HAL_MIC_DIRECT_STOP_START)
static int mic_worker_exit = 0;
static pthread_t tid_mic_worker;
#endif

static int mic_switch_worker_exit = 0;
static pthread_t tid_mic_switch_worker;


#endif

static HANDLE hStream = NULL;

#if defined(_INTEK_PORTING_)
static int gMicSwitchStatus = 0;

static int readMicState(void)
{
	int fd;
	char status[64];
	int retval = 0;

	if ((fd = open("/sys/class/switch/mic/state", O_RDONLY)) < 0)
	{
		return false;
	}

	if (read(fd, status, 64) < 0) {
		close(fd);
		return 0;
	}

	if (!strncmp(status, "1", 1))
		retval = 1;
	else
		retval = 0;

	close(fd);

	//L_DEBUG("readMicState retval=%d", retval);
	return retval;	
}
#endif


static void dump_data(dump_index_type index, void*buf, int size) {
    char* path;
    FILE* fp;

    switch(index) {
        case dump_index_ref:
            path = (char*)"/data/vendor/ref.dat";
            fp = dump_ref_fp;
            break;

        case dump_index_effect:
            path = (char*)"/data/vendor/effect.dat";
            fp = dump_effect_fp;
            break;
        default:
            return;
    }

    if (fp == NULL) {
        fp = fopen(path, "w");
        if (fp == NULL) {
            return;
        }
        L_DEBUG("open dump file %p\n", fp);
        if (index == dump_index_ref) {
            dump_ref_fp = fp;
        }else if (index == dump_index_effect) {
            dump_effect_fp = fp;
        }

        dump_total = 0;
    }

    if (fp) {
        if (dump_total < 400*1024*1024) {
            fwrite(buf, size, 1, fp);
            dump_total += size;
        }
    }
}

static void dump_data_wrap(dump_index_type index, void* buf, int size) {
    dump_msg_type msg;
    msg.index = index;
    msg.size = size;
    msg.buf = malloc(size);
    memcpy(msg.buf, buf, size);
    sync_queue_push(msgq_dump, &msg);
}

static void* dump_thread(void * args __unused){
    //int r;
    dump_msg_type msg;

	L_DEBUG("dump thread is running.\n");
    while (!effect_worker_exit) {
        sync_queue_pop(msgq_dump, &msg);

        if (msg.index == -1) {
            break;
        }

        dump_data(msg.index, msg.buf, msg.size);
        free(msg.buf);
    }

    L_DEBUG("dump_thread exit\n");
    return 0;
}

static void ringbuf_write(ringbuf_type* ringbuf, unsigned char *buf, int size) {
    pthread_mutex_lock(&ringbuf->lock);

    unsigned char* pw = ringbuf->buf + ringbuf->wd;

    //L_DEBUG("ringbuf_write ringbuf->wd=0x%08x size=%d\n", ringbuf->wd, size);
    if (ringbuf->wd + size > ringbuf->size) {
        int len1 = ringbuf->size - ringbuf->wd;
        int len2 = size - len1;
		//L_DEBUG("\t ringbuf_write split size=%d len1=%d,len2=%d\n", size, len1, len2);
        memcpy((void*)pw,  buf, len1);
        memcpy(ringbuf->buf, (void*)((unsigned char*)buf + len1), len2);
        ringbuf->wd = len2;
    } else {
		//L_DEBUG("\t ringbuf_write one size=%d\n", size);

        memcpy((void*)pw, buf, size);
        ringbuf->wd += size;
        if (ringbuf->wd == ringbuf->size) {
            ringbuf->wd = 0;
        }
    }
    if (ringbuf->fullness + size <= ringbuf->size) {
        ringbuf->fullness += size;
    } else {
        ringbuf->fullness = ringbuf->size;
        ringbuf->rd = ringbuf->wd;
        if (ringbuf == &ringbuf_ref) {
            L_DEBUG("[%p]overflow %d", ringbuf, ringbuf->fullness + size - ringbuf->size);
        }
    }
    pthread_mutex_unlock(&ringbuf->lock);
}

static int ringbuf_read(ringbuf_type* ringbuf, unsigned char* buf, int size) {

    while(1) {
        if (ringbuf->fullness < size) {
            usleep(10000);
            return -1;
        } else {
            break;
        }
    }

    //L_DEBUG("ringbuf_read ringbuf->rd=0x%08x size=%d\n", ringbuf->rd, size);
    pthread_mutex_lock(&ringbuf->lock);
    if (ringbuf->rd + size <= ringbuf->size) {
        unsigned char* pr = (unsigned char*)ringbuf->buf + ringbuf->rd;

		//L_DEBUG("\t ringbuf_read one size=%d\n", size);
        memcpy(buf, (void*)(pr), size);
        ringbuf->rd += size;
        if (ringbuf->rd == ringbuf->size) {
            ringbuf->rd = 0;
        }
    } else {
        int len1 = ringbuf->size - ringbuf->rd;
        int len2 = size - len1;

		//L_DEBUG("\t ringbuf_read split size=%d len1=%d,len2=%d\n", size, len1, len2);

        memcpy(buf, (void*)((unsigned char*)ringbuf->buf + ringbuf->rd), len1);
        memcpy((void*)((unsigned char*)buf + len1), ringbuf->buf, len2);
        ringbuf->rd = len2;
    }
    ringbuf->fullness -= size;
    pthread_mutex_unlock(&ringbuf->lock);

	return 0;
}

#if defined(HAL_MIC_ECHO_PROCESS_WO_RBUF)
static void process_effect(unsigned char *sound_data, int sample_cnt) 
{
	HRESULT rc = SUCCESS;
	//L_DEBUG("process_effect sample_cnt %d", sample_cnt);

	echo_process_pcm(sound_data, sound_data, sample_cnt * 2);

    if (dump_effect_enable)
        dump_data_wrap(dump_index_effect, sound_data, sample_cnt * 2);

    rc = AMP_SND_PushPCM_NonBlock(hStream, sound_data, sample_cnt * 2);
	if (rc != S_OK) {
	    L_DEBUG("AMP_SND_PushPCM fail:%x\n", rc);
	}
}
#else
static void process_effect(int sample_cnt) 
{
	int ret = 0;
	//unsigned char sound_data[SAMPLES_PER_FRAME * 2];
	unsigned char *sound_data = NULL;
	
	sound_data = (unsigned char *)malloc(sample_cnt * 2);
	if (sound_data)
	{	
		ret = stream_read(stream_raw, sound_data, sample_cnt * 2);
		if (ret == 0)
		{
			echo_process_pcm(sound_data, sound_data, sample_cnt * 2);

		    if (dump_effect_enable)
		        dump_data_wrap(dump_index_effect, sound_data, sample_cnt * 2);

			HRESULT rc = SUCCESS;
		    rc = AMP_SND_PushPCM(hStream, sound_data, sample_cnt * 2);
			if (rc != S_OK) {
			    L_DEBUG("AMP_SND_PushPCM fail:%x\n", rc);
			}
		}
		free(sound_data);
	}
}
#endif

static int open_ref_input(){
	int count = 10;
    struct pcm_config alsa_config;
    alsa_config.channels = 2;
    alsa_config.rate = SYNA_ALSA_PARAM_RATE;
    alsa_config.format = PCM_FORMAT_S32_LE;
    alsa_config.period_size = SYNA_ALSA_PARAM_PERIOD;
    alsa_config.period_count = SYNA_ALSA_PARAM_N;
    alsa_config.start_threshold = 0;
    alsa_config.stop_threshold = 0;
    alsa_config.silence_threshold = 0;

    ref_pcm = pcm_open(SYNA_AEC_REF_ALSA_CARD, SYNA_AEC_REF_ALSA_DEV,
                        PCM_IN|PCM_MONOTONIC, &alsa_config);
	if (ref_pcm == NULL)
	{
		return -1;
	}

	while (count--)
	{
	    if (!pcm_is_ready(ref_pcm)) {
	        L_DEBUG("can't open ref i2s alsa input!");
	        usleep(100);
	    }
		else
		{
			break;
		}
	}

	L_DEBUG("pcm_open ... count = %d", count);
	if (count <= 0)
	{
		pcm_close(ref_pcm);
		ref_pcm = NULL;
		return -1;
	}
	
	pcm_buf_size = pcm_frames_to_bytes(ref_pcm,pcm_get_buffer_size(ref_pcm));
    L_DEBUG("pcm buffer size %d\n", pcm_buf_size);
	
    return 0;
}

#if !defined(_INTEK_PORTING_)
static void copy_to_raw(void* buf, int size) {
    int i;
    int sample_cnt = size/FRAME_SIZE_32BIT;
    short tmp[16384];
    int* p = (int*)buf;
	
    for(i=0; i < sample_cnt; i++){
       tmp[i] = p[2*i]>>16;
    }
	
    ringbuf_write(&ringbuf_raw, (unsigned char*)tmp, 2*sample_cnt);
}
#endif

#if !defined(HAL_MIC_ECHO_PROCESS_WO_RBUF)
static void copy_to_mono(void* buf, int size) {
    int i;
    int sample_cnt = size/FRAME_SIZE_32BIT;
    //short tmp[SAMPLES_PER_FRAME * FRAME_SIZE_32BIT];
    short *tmp;
    short* p = (short*)buf;

	tmp = (short*)malloc(sizeof(short)*sample_cnt);
	if (tmp)
	{
		//L_DEBUG("copy_to_mono: %d\n", size);	
	    for(i = 0; i < sample_cnt; i++){
	       tmp[i] = p[4*i];
	    }
		
	    ringbuf_write(&ringbuf_raw, (unsigned char*)tmp, 2*sample_cnt);
		free(tmp);
	}
}
#endif

static void* effect_worker(void * args __unused){
	int ret = 0;
    //static unsigned int print_cnt = 0;

    //unsigned char mic_buf[pcm_buf_size];
    //unsigned char ref_buf[pcm_buf_size];
	unsigned char *ref_buf = NULL;
#if defined(HAL_MIC_ECHO_PROCESS_WO_RBUF)
    int i;
	int sample_cnt = pcm_buf_size/FRAME_SIZE_32BIT;
	short *tmp = NULL;
	short* p = NULL;
	short multiple = 1;

	tmp = (short*)malloc(sizeof(short)*sample_cnt);
	if (!tmp)
	{
		return 0;
	}
#endif

	ref_buf = (unsigned char*)malloc(pcm_buf_size);
	if (ref_buf)
	{
	    while (!effect_worker_exit){
	        ret = ringbuf_read(&ringbuf_ref, ref_buf, pcm_buf_size);
			if (ret == 0)
			{
		        if (dump_ref_enable) {
		            dump_data_wrap(dump_index_ref, ref_buf, pcm_buf_size);
		        }
				
#if defined(HAL_MIC_ECHO_PROCESS_WO_RBUF)
				p = (short*)ref_buf;
				if (gMicSwitchStatus)
				{
					for(i = 0; i < sample_cnt; i++)
					{
						tmp[i] = p[4*i + 1] * multiple;
					}
					process_effect((unsigned char*)tmp, pcm_buf_size/FRAME_SIZE_32BIT);
				}
#else
		        copy_to_mono(ref_buf, pcm_buf_size);

		        process_effect(pcm_buf_size/FRAME_SIZE_32BIT);
#endif
			}
	    }
		free(ref_buf);
	}

#if defined(HAL_MIC_ECHO_PROCESS_WO_RBUF)
	if (tmp)
		free(tmp);
#endif

    L_DEBUG("effect_worker exit");
    return 0;
}

static void* input_reader(void * args){
	int ret;
    //unsigned char buf[pcm_buf_size];
	unsigned char *buf = NULL;
    //int pcm_dev_no = (int)args;
    struct pcm* pcm_dev;
    ringbuf_type* ringbuf;

	(void)args;
	
    prctl(PR_SET_NAME, "reader_ref", 0, 0, 0);
    pcm_dev = ref_pcm;
    ringbuf = &ringbuf_ref;

	buf = (unsigned char *)malloc(pcm_buf_size);
	if (buf)
	{
	    while (!effect_worker_exit)
		{
	        ret = pcm_read(pcm_dev, buf, pcm_buf_size);		
			if (ret == 0)
		        ringbuf_write(ringbuf, buf, pcm_buf_size);
			else
			{
				L_DEBUG("input_reader pcm read error");
				usleep(10000);
			}
	    }
		free(buf);
	}

    return 0;
}

#if !defined(_INTEK_PORTING_)
static void cp_16_32(void* buf16, void* buf32, int size) {
    int sample_cnt = size/2;
    int i;
    int* p32 = (int*)buf32;
    short* p16 = (short*)buf16;

    for(i=0; i < sample_cnt; i++) {
        p32[i] = ((int)p16[i]) << 16;
    }
}
#endif

int effect_init() {
    int r = open_ref_input();
    if (r != 0){
        L_DEBUG("effect_init: failed!");
        return -1;
    }

    pthread_attr_init(&attr);
    if(pthread_attr_setschedpolicy(&attr, SCHED_RR) !=0 ) {
        L_DEBUG("Unable to set policy.\n");
    }

    struct sched_param schedparam;
    pthread_attr_getschedparam(&attr, &schedparam);
    schedparam.sched_priority = 98;
    int res = pthread_attr_setschedparam(&attr, &schedparam);
    if (res != 0) {
        L_DEBUG( "unable set priority!");
    }

    memset((void*)&ringbuf_raw, 0 ,sizeof(ringbuf_type));
    ringbuf_raw.size = RAW_BUF_CNT*pcm_buf_size;
    ringbuf_raw.buf = (unsigned char*)malloc(ringbuf_raw.size);
    pthread_mutex_init(&(ringbuf_raw.lock), NULL);

	if (dump_effect_enable || dump_effect_enable)
	    msgq_dump = sync_queue_create(DUMP_THREAD_MSG_Q_SIZE, sizeof(dump_msg_type));

    int input_buf_cnt = (INPUT_BUF_LENGHT*SYNA_ALSA_PARAM_RATE)/((pcm_buf_size/FRAME_SIZE_32BIT)*1000);
    L_DEBUG("input buf cnt %d\n", input_buf_cnt);

    memset((void*)&ringbuf_ref, 0 ,sizeof(ringbuf_type));
    ringbuf_ref.size = input_buf_cnt*pcm_buf_size;
    ringbuf_ref.buf = (unsigned char*)malloc(ringbuf_ref.size);
    pthread_mutex_init(&(ringbuf_ref.lock), NULL);
    return 0;
}

void effect_deinit() {
    if (ref_pcm)
    {
		pcm_close(ref_pcm);
		ref_pcm = NULL;
		L_DEBUG("pcm_close... \n");
    }

    if (ringbuf_raw.buf) {
        free(ringbuf_raw.buf);
		ringbuf_raw.buf = NULL;
    }
    pthread_mutex_destroy(&(ringbuf_raw.lock));

	if (ringbuf_ref.buf) {
        free(ringbuf_ref.buf);
		ringbuf_ref.buf = NULL;
    }
    pthread_mutex_destroy(&(ringbuf_ref.lock));

	if (msgq_dump) {
		sync_queue_delete(msgq_dump);
		msgq_dump = NULL;
	}
	
    if (dump_ref_fp)
        fclose(dump_ref_fp);

    if (dump_effect_fp)
        fclose(dump_effect_fp);

	pthread_attr_destroy(&attr);
}

int stream_read(stream_index stream, unsigned char *buf, size_t bytes) {
	int ret = 0;
    //static long long int cnt_gg=0, cnt_caf=0, cnt_raw = 0;
    //static int print_int = 100;
    if (stream == stream_raw) {
		#if 0
        if ((cnt_raw++%print_int)==0) {
           L_DEBUG("raw read %lld", cnt_raw);
        }
		#endif
        ret = ringbuf_read(&ringbuf_raw, buf, bytes);
    } else {
        pcm_read(ref_pcm, buf, bytes);
    }
    return ret;
}

int sound_init() {
    //AMP_STATE state;
    HRESULT rc;
	AMP_SND_STRM_ATTR stStrmAttr;
    int i;
    UINT32 aChMask[] = {
        AMP_AUDIO_CHMASK_LEFT,
        AMP_AUDIO_CHMASK_RGHT,
        AMP_AUDIO_CHMASK_CNTR,
        AMP_AUDIO_CHMASK_LFE,
        AMP_AUDIO_CHMASK_SRRD_LEFT,
        AMP_AUDIO_CHMASK_SRRD_RGHT
    };

	static UINT32 m_pcm_bit_depth = 16;
	static UINT32 m_chnr = 1;
	static UINT32 m_fs = 48000;
	static INT32 m_spk_mode = AMP_AUDIO_CHMAP_3_2_0;
	static INT32 m_lfe_mode = AMP_AUDIO_LFE_ON;

    AMP_APP_PARASPKMODE stSpkMode;

    rc = AMP_SND_Init();
    if (rc != SUCCESS) {
        L_DEBUG("AMP_SND_Init fail:%x!\n", rc);
        return -1;
    }

    AmpMemClear(&stSpkMode, sizeof(AMP_APP_PARASPKMODE));
    stSpkMode.eSpkMode = m_spk_mode;
    stSpkMode.eLfeMode = m_lfe_mode;
    AMP_SND_SetSpkMode(&stSpkMode);
    AmpMemClear(&stStrmAttr, sizeof(AMP_SND_STRM_ATTR));

    stStrmAttr.uiBlkNum                    = 16;
    stStrmAttr.uiBlkLen                    = 1024 * (m_pcm_bit_depth >> 3) * m_chnr;
    stStrmAttr.uiChannels                  = m_chnr;
    stStrmAttr.uiBitDepth                  = m_pcm_bit_depth;
    stStrmAttr.uiSampleRate                = m_fs;
    stStrmAttr.uiBufNr                     = 1;
	
    for (i = 0; i < (int)stStrmAttr.uiChannels; i++) {
        stStrmAttr.uiChanMask[i] = aChMask[i];
    }
    rc = AMP_SND_CreateStream(&stStrmAttr, &hStream);

    if (rc != SUCCESS) {
        L_DEBUG("AMP_SND_CreateStream fail:%x.\n", rc);
        return -1;
    }

    AMP_SND_SetBitDepth(hStream, m_pcm_bit_depth);
    AMP_SND_SetSampleRate(hStream, m_fs);
	
	return 0;
}


int amp_init()
{
#if !defined(_INTEK_PORTING_)
	char const *client_argv[] =
    { 	
    	"client",
    	"iiop:1.0//127.0.0.1:999/AMP::FACTORY/factory"
    };
    HRESULT result = 0xFFFFFFFF;
    AMP_FACTORY hFactory;
    //UINT32 test_case = 0;

    MV_OSAL_Init();
    /* Get factory */
    result = AMP_Initialize(2, (char**)client_argv, &hFactory);
    assert(result == SUCCESS);
    result = AMP_GetFactory(&hFactory);
    assert(result == SUCCESS);
	int ret = sound_init();
	assert(ret != 0);
	(void)ret;
#endif
	return 0;
}

int amp_deinit()
{
#if !defined(_INTEK_PORTING_)
	// sound_Deinit
	AMP_SND_DestroyStream(hStream);
	hStream = NULL;
	AMP_SND_Deinit();
	AMP_Deinitialize();
#endif
	return 0;
}

#if defined(_INTEK_PORTING_)
static void* mic_worker(void * args __unused)
{
	HAL_Error ret;

	int wait_count;
	char property[PROPERTY_VALUE_MAX];
	memset(property, 0, PROPERTY_VALUE_MAX);

	property_set(PROPERTY_SET_MIC_OWNER, OWNER_KARAOKE);

	wait_count = 0;
#if defined(HAL_MIC_DIRECT_STOP_START)
	while (wait_count < HAL_MIC_WAIT_COUNT)
#else
    while ((!mic_worker_exit) && (wait_count < HAL_MIC_WAIT_COUNT))
#endif
	{
		if (mic_worker_state == HAL_MIC_WORKER_STAE_IDLE)
		{
			property_get(PROPERTY_IS_MIC_OWNER, property, "");
			if ((!strcmp(property, OWNER_KARAOKE)))
			{
				ret = HAL_MicInit();
				L_DEBUG("mic_worker 1 ret =%d", ret);
				if (ret == HAL_OK)
				{
					HAL_MicSetOnOff(MIC_ON);
					mic_worker_state = HAL_MIC_WORKER_STAE_RUN;
#if !defined(HAL_MIC_DIRECT_STOP_START)
					mic_worker_exit = 1;
#endif
					break;
				}
			}
		}

		usleep(HAL_MIC_SLEEP_TIME_MSEC*1000);
		wait_count++;
		
		L_DEBUG("mic_worker wait_count=%d", wait_count);
    }

	if ((wait_count == HAL_MIC_WAIT_COUNT) && (mic_worker_state != HAL_MIC_WORKER_STAE_RUN))
	{
		ret = HAL_MicInit();
		L_DEBUG("mic_worker 2 ret =%d", ret);
		if (ret == HAL_OK)
		{
			HAL_MicSetOnOff(MIC_ON);
			mic_worker_state = HAL_MIC_WORKER_STAE_RUN;
		}
	}

    L_DEBUG("mic_worker exit");
    return 0;
}

static void* mic_switch_worker(void * args __unused)
{
	int micStatus = 0, micInCnt = 0;

	gMicSwitchStatus = readMicState();

	while (!mic_switch_worker_exit)
	{
		if (gMicSwitchStatus)
		{
			gMicSwitchStatus = readMicState();
		}
		else
		{
			micStatus = readMicState();
			if (micStatus)
			{
				micInCnt++;
				if (micInCnt >= (4 + 2) /* 250ms*(4 + 2) = 1.5 sec */)
				{
					gMicSwitchStatus = 1;
				}
			}
			else
				micInCnt = 0;
		}
		usleep(HAL_MIC_SLEEP_TIME_MSEC*1000);
	}

	L_DEBUG("mic_switch_worker exit");
	return 0;
}


int HAL_MicActivate(void)
{

    L_DEBUG("HAL_MicActivate Enter");

	if (mic_act)
		return 0;

	mic_worker_state = HAL_MIC_WORKER_STAE_IDLE;

#if defined(HAL_MIC_DIRECT_STOP_START)
	mic_worker(NULL);
#else
   	mic_worker_exit = 0;
	pthread_create(&tid_mic_worker, NULL, mic_worker, NULL);
#endif

	mic_switch_worker_exit = 0;
	pthread_create(&tid_mic_switch_worker, NULL, mic_switch_worker, NULL);


	mic_act = 1;

    L_DEBUG("HAL_MicActivate Leave");

	return 0;
}

int HAL_MicDeactivate(void)
{
	void* retval;

    L_DEBUG("HAL_MicDeactivate Enter");

	if (!mic_act) return 0;

	mic_switch_worker_exit = 1;
	pthread_join(tid_mic_switch_worker, &retval);

#if !defined(HAL_MIC_DIRECT_STOP_START)
	mic_worker_exit = 1;
	pthread_join(tid_mic_worker, &retval);
#endif
	if (mic_worker_state == HAL_MIC_WORKER_STAE_RUN)
	{
		HAL_MicSetOnOff(MIC_OFF);
		HAL_MicDeinit();
	}

	property_set(PROPERTY_SET_MIC_OWNER, OWNER_NUGU);

	mic_act = 0;

    L_DEBUG("HAL_MicDeactivate Leave");

	return 0;
}
#endif

/**
 * @return HAL interface version
 */
int HAL_MicGetVersion()
{
	L_DEBUG("%s()", __FUNCTION__);

	return 0;
}

HAL_Error HAL_MicInit()
{	
	HAL_Error ret = HAL_OK;
	L_DEBUG("%s()", __FUNCTION__);

	if (amp_init() < 0)
	{
		ret = HAL_ERROR;
		goto amp_init_failed;
	}

	if (effect_init() < 0)
	{
		ret = HAL_ERROR;
		goto effect_init_failed;
	}

	return ret;
	
effect_init_failed:	
	amp_deinit();
amp_init_failed:
	return ret;
}



HAL_Error HAL_MicDeinit()
{
	L_DEBUG("%s()", __FUNCTION__);

	effect_deinit();
	amp_deinit();
	return HAL_OK;
}

HAL_Error HAL_MicSetOnOff(MIC_STATE status)
{
    HAL_Error ret = HAL_OK;
	void* retval;

	L_DEBUG("%s(%d)", __FUNCTION__, status);
    switch (status)
    {
    case MIC_MAX:
		HAL_MicSetEchoLevel(HAL_MicGetEchoMaxLevel());
    case MIC_ON:
#if defined(_INTEK_PORTING_)
		HAL_MicSetVolume(micVolumeLv);
#endif
		echo_state_set(true);
		
    	effect_worker_exit = 0;
		pthread_create(&tid_effect_worker, NULL, effect_worker, NULL);
		if (dump_effect_enable || dump_effect_enable)
			pthread_create(&tid_dump, NULL, dump_thread, NULL);
		pthread_create(&tid_input_reader_ref, NULL, input_reader, (void*)1);
		break;
    case MIC_OFF:
		echo_state_set(false);

		effect_worker_exit = 1;
		pthread_join(tid_input_reader_ref, &retval);
		if (dump_effect_enable || dump_effect_enable)
			pthread_join(tid_dump, &retval);
		pthread_join(tid_effect_worker, &retval);
		
		break;
    default:
		ret = HAL_ERROR;
		break;
    }
	return ret;
}

MIC_STATE HAL_MicGetOnOff()
{
	L_DEBUG("%s", __FUNCTION__);
	bool state = echo_state_get();
	return state ? MIC_ON : MIC_OFF;
}

HAL_Error HAL_MicSetEchoLevel(int lv)
{
	L_DEBUG("%s(%d)", __FUNCTION__, lv);
    echo_params param;
	echo_params_get(&param);
#if defined(_INTEK_PORTING_)
	if (lv > HAL_MicGetEchoMaxLevel())
		lv = HAL_MicGetEchoMaxLevel();
#endif
	param.delay = lv;
	echo_params_set(&param);
	return HAL_OK;
}

int HAL_MicGetEchoLevel()
{
	L_DEBUG("%s", __FUNCTION__);
	echo_params param;
	echo_params_get(&param);
	return param.delay;
}

int HAL_MicGetEchoMaxLevel()
{
	L_DEBUG("%s", __FUNCTION__);
	echo_params param;
	echo_params_max_get(&param);
	return param.delay;
}

HAL_Error HAL_MicSetVolume(unsigned int volume)
{
	L_DEBUG("%s()", __FUNCTION__);
#if defined(_INTEK_PORTING_)
#if 0
	if (!((0 <= volume) && (volume < MAX_MIC_VOLUME_TABLE)))
		return HAL_ERROR;
#else
	if (!((0 <= volume) && (volume <= 100)))
		return HAL_ERROR;
#endif
#endif

	if (hStream)
	{
#if 0 //defined(_INTEK_PORTING_)
		L_DEBUG("%s() volume=%d -> %f", __FUNCTION__, volume, micVolume[volume]);

		AMP_SND_SetStreamVolumeIndB(hStream, micVolume[volume]);
#else
	#if defined(_INTEK_PORTING_)
		float fVolume = 12.0 - (100.0 - (float)volume)/2.0;
	
		AMP_SND_SetStreamVolumeIndB(hStream, fVolume);
		L_DEBUG("volume: %f\n", fVolume);
	#else
		AMP_SND_SetStreamVolume(hStream, volume);
		AMP_SND_GetStreamVolume(hStream, &volume);
		L_DEBUG("volume: %d\n", volume);
	#endif
		//set volume in db, please refer to
		//AMP_SND_SetStreamVolumeIndB(hStream, volume);
#endif
	}
	micVolumeLv = volume;

	return HAL_OK;
}

#if defined(_INTEK_PORTING_)
HAL_Error HAL_MicGetVolume(int *volume)
{
	L_DEBUG("%s()", __FUNCTION__);
	if (volume)
		*volume = micVolumeLv;
	L_DEBUG("%s() micVolumeLv=%d", __FUNCTION__, micVolumeLv);

	return HAL_OK;
}
#endif

HAL_Error HAL_MicPause()
{
	L_DEBUG("%s", __FUNCTION__);

	return HAL_OK;
}

HAL_Error HAL_MicResume()
{
	L_DEBUG("%s", __FUNCTION__);
	
	return HAL_OK;
}

#ifdef __cplusplus
}
#endif
