#include "SV_basic_op.h"
#define TRANSIENT_ONSET_DELAY 5
#define TRANSIENT_OFFSET_DELAY 5
#define TRANSIENT_FRAME_INTERVAL 7
#define SV_FLAG_FRAME_ENERGY_Q 23

#if (TRANSIENT_FRAME_INTERVAL < TRANSIENT_ONSET_DELAY) || (TRANSIENT_FRAME_INTERVAL < TRANSIENT_OFFSET_DELAY)
#if TRANSIENT_ONSET_DELAY > TRANSIENT_OFFSET_DELAY
#define TRANSIENT_FRAME_INTERVAL TRANSIENT_ONSET_DELAY
#else
#define TRANSIENT_FRAME_INTERVAL TRANSIENT_OFFSET_DELAY
#endif
#endif

#define TRANSIENT_BUF_LEN TRANSIENT_FRAME_INTERVAL
typedef long long INT64;
#define SMULL( a,   b )\
    ((INT64)(a)*(INT64)(b))


#if defined (_WIN32) && defined(BSH_DEBUG)
#define DEBUG_FLAG
#endif
#ifdef DEBUG_FLAG
#include<stdlib.h>
#include<stdio.h>
extern FILE *f_flag[7];
#endif



typedef struct
{
	int frameLogE[TRANSIENT_BUF_LEN] ; //Q23
	int onset;
	int offset;
	int onsetCnt;
	int offsetCnt;

} TransientDetector;



typedef struct
{
	int onset;
	int onsetCnt;
} NoiseOnsetDetector;

NoiseOnsetDetector NOD_vars;
TransientDetector flag;
TransientDetector *flag_vars = &flag;


void SV_FLAG_NOD_Init()
{
	NOD_vars.onset = 0;
	NOD_vars.onsetCnt = 0;
}


void SV_FLAG_Init()
{
	int i;

	flag_vars->onsetCnt = 0;
	flag_vars->offsetCnt = 0;
	flag_vars->onset = 0;
	flag_vars->offset = 0;
	for (i = 0; i < TRANSIENT_BUF_LEN; i++) flag_vars->frameLogE[i] = 0;

	SV_FLAG_NOD_Init();

}
void SV_FLAG_DetectTransient(int *Spec, int TxVAD)
{
	int i;

	INT64 LL_frameE = 0;
	int L_frameE = 0;
	int frameLogE = 0; //Q(SV_FLAG_FRAME_ENERGY_Q)
	int diff = 0; //Q(SV_FLAG_FRAME_ENERGY_Q)
	//int diffThd = 83886080; // 10dB Q(SV_FLAG_FRAME_ENERGY_Q)
	//int diffThd = 105606318; // 13dB Q(SV_FLAG_FRAME_ENERGY_Q)
	int diffThd = 167374734; // 13dB Q(SV_FLAG_FRAME_ENERGY_Q)

	for (i = 0; i < 128; i++)
	{
		int real, imag;
		real = 2 * i;
		imag = real + 1;

		LL_frameE = LL_frameE + ((SMULL(Spec[real], Spec[real]) + SMULL(Spec[imag], Spec[imag])) >> 7);
	}
	L_frameE = (int)(LL_frameE >> (30 - SV_FLAG_FRAME_ENERGY_Q));
	frameLogE = SV_10log10_l(L_frameE, SV_FLAG_FRAME_ENERGY_Q);

	flag_vars->onset = 0;
	flag_vars->offset = 0;

	if (TxVAD == 0)
	{
		
		diff = frameLogE - flag_vars->frameLogE[TRANSIENT_FRAME_INTERVAL - 1];

		if (diff > diffThd)
		{
			flag_vars->onsetCnt++;
		}
		else
		{
			flag_vars->onsetCnt=0;
		}

		if (diff < -diffThd)
		{
			flag_vars->offsetCnt++;
		}
		else
		{
			flag_vars->offsetCnt = 0;
		}
	}
	else
	{
		flag_vars->onsetCnt = 0;
		flag_vars->offsetCnt = 0;
	}

	if (flag_vars->onsetCnt >= TRANSIENT_ONSET_DELAY)
	{
		flag_vars->onset= 1;
	}

	if (flag_vars->offsetCnt >= TRANSIENT_OFFSET_DELAY)
	{
		flag_vars->offset = 1;
	}

	// buffer move
	for (i = TRANSIENT_BUF_LEN - 1; i > 0 ; i--) {
		flag_vars->frameLogE[i] = flag_vars->frameLogE[i - 1];
	}
	flag_vars->frameLogE[0] = frameLogE;

#ifdef DEBUG_FLAG
	float ftmp;
	int *inPtr = Spec;
	for (i = 0; i < 256; i++)
	{
		int real = *inPtr++;
		int imag = *inPtr++;
		ftmp = ((float)real*real + (float)imag*imag) / (float)((INT64)1 << 30);
		fwrite(&ftmp, sizeof(float), 1, f_flag[0]);
	}

	//ftmp = ESum / (float)((INT64)1 << 30);
	ftmp = frameLogE / (float)((INT64)1 << SV_FLAG_FRAME_ENERGY_Q);
	fwrite(&ftmp, sizeof(float), 1, f_flag[1]);
	fwrite(&flag_vars->onset, sizeof(int), 1, f_flag[2]);
	fwrite(&flag_vars->offset, sizeof(int), 1, f_flag[3]);
#endif
}

void SV_FLAG_Deinit() {

}
int SV_FLAG_GetNoiseOnset()
{
	return flag_vars->onset;
}
int SV_FLAG_GetNoiseOffset()
{
	return flag_vars->offset;
}


int SV_FLAG_DetectNoiseOnset(int *noisySpec, int *enhSpec, short *refMask, short TxVAD, short RxVAD, short noiseLevel)
{
	int i;
	INT64 LL_enhSpecE = 0;
	INT64 LL_noisySpecE = 0;
	int L_enhSpecE = 0, L_enhSpecLogE = 0;
	int L_noisySpecE = 0, L_noisySpecLogE = 0;
	int LogEnergyDiff = 0;
	const int E_Q = SV_FLAG_FRAME_ENERGY_Q;
	if (TxVAD != 1 && RxVAD == 0 && noiseLevel == 0) {
		for (i = 10; i < 64; i++)
		{
			if (refMask[i] > 30000) continue;

			LL_enhSpecE   += (SMULL(enhSpec[2 * i], enhSpec[2 * i]) + SMULL(enhSpec[2 * i + 1], enhSpec[2 * i + 1]) ) >> 6;
			LL_noisySpecE += (SMULL(noisySpec[2 * i], noisySpec[2 * i]) + SMULL(noisySpec[2 * i + 1], noisySpec[2 * i + 1]) ) >> 6;
		}

		L_enhSpecE = (int)(LL_enhSpecE >> (30 - E_Q));
		L_enhSpecLogE = SV_10log10_l(L_enhSpecE, E_Q);

		L_noisySpecE = (int)(LL_noisySpecE >> (30 - E_Q));
		L_noisySpecLogE = SV_10log10_l(L_noisySpecE, E_Q);

		LogEnergyDiff = L_noisySpecLogE - L_enhSpecLogE;
		if (LogEnergyDiff > 83886080/* 10dB */)
		{
			NOD_vars.onsetCnt++;
		}
		else
		{
			NOD_vars.onsetCnt = 0;
			NOD_vars.onset = 0;

		}
	}
	else
	{
		NOD_vars.onsetCnt = 0;
		NOD_vars.onset = 0;

		L_enhSpecLogE = -SV_MIN_32;
		L_noisySpecLogE = -SV_MIN_32;
	}

	if (NOD_vars.onsetCnt >= 2)
	{
		NOD_vars.onset = 1;
	}

#ifdef DEBUG_FLAG
	{
		float ftmp;
#if 1
		ftmp = L_enhSpecLogE / (float)((INT64)1 << E_Q);
		fwrite(&ftmp, sizeof(float), 1, f_flag[4]);
		ftmp = L_noisySpecLogE / (float)((INT64)1 << E_Q);
		fwrite(&ftmp, sizeof(float), 1, f_flag[5]);
#else
		ftmp = LL_enhSpecE / (float)((INT64)1 << 30);
		fwrite(&ftmp, sizeof(float), 1, f_flag[4]);
		ftmp = LL_noisySpecE / (float)((INT64)1 << 30);
		fwrite(&ftmp, sizeof(float), 1, f_flag[5]);
#endif
		ftmp = (float)NOD_vars.onset;
		fwrite(&ftmp, sizeof(float), 1, f_flag[6]);

	}
#endif

	return NOD_vars.onset;
}