/*
 * AgcCalculator.cpp
 *
 */


#include "AgcCalculator.h"

#include "agc_mprf_att_prediction.h"

#define MAX_ATT	63

struct simple_agc_calc_data_t
{
	unsigned int disabled;
	unsigned int mode_mask;
	unsigned int mode_actual;
	AgcCalculator::pred_result_type_t ptype;

	int local_predict;

	unsigned int reg_batches;
	unsigned int reg_counter;

	float dsp_att_k;

	float dsp_estimation_db;
	float dsp_predicted_db;
	float dsp_prediction_sec;

	//float estimation_db;
	float predicted_db;
	float dfe_prediction_db;

	unsigned int agc_batch;
	unsigned int pred_batch;
	float pred_time;

	int min_pipe;
	int max_pipe;
	int pipe;

	float min_dt;
	float max_dt;

	unsigned int min_proc;
	unsigned int max_proc;
	unsigned int min_spare;
	unsigned int max_spare;
	unsigned int last_proc;
	unsigned int last_spare;

	unsigned int agc_miss;
	unsigned int invalid_batch;
	unsigned int invalid_agc;
	unsigned int too_old;

	enum { k_pipe_stat_max=8};

	unsigned int pipe_stat[k_pipe_stat_max];

	float btime[k_pipe_stat_max];

	unsigned int bindex;

	agc_att_prediction_state_t pstate;

	void reset(unsigned int reg)
	{
		min_pipe=0;
		max_pipe=0;
		min_dt=0;
		max_dt=0;

		min_proc=0;
		max_proc=0;
		min_spare=0;
		max_spare=0;
		last_proc=0;
		last_spare=0;

		pipe=-1;
		reg_batches=reg<1 ? 1 : reg;
		reg_counter=0;
		dfe_prediction_db=-1;

		for(int i=0; i<(int)k_pipe_stat_max; ++i)
		{
			pipe_stat[i]=0;
			btime[0]=0;
			bindex=0;
		}

		AGC_att_prediction_initialize(pstate, 0, 0, 0.64, 0.25);
	}

	float predict(AgcCalculator::pred_result_type_t* ptype_, float agc_estimation_db, float agc_predicted_db, float dsp_att_k_, unsigned int agc_src_batch, unsigned int proc_pred_batch, float pred_time_sec, unsigned int proc_time_us)
	{
#ifdef __MINGW32__
		if (agc_src_batch==0)
		{
			agc_estimation_db=5;
			agc_src_batch=proc_pred_batch-4;
		}
		agc_src_batch=proc_pred_batch-4;
#endif

		dsp_att_k=dsp_att_k_;

		dsp_estimation_db=agc_estimation_db;
		dsp_predicted_db=agc_predicted_db;
		dsp_prediction_sec=pred_time_sec;

		if (agc_estimation_db>MAX_ATT)
			agc_estimation_db=MAX_ATT;
		if (agc_predicted_db>MAX_ATT)
			agc_predicted_db=MAX_ATT;

		bindex=(bindex+1) & ((k_pipe_stat_max-1));
		btime[bindex]=pred_time_sec;

		if (agc_batch==agc_src_batch)
			++agc_miss;

		pred_batch=proc_pred_batch;
		agc_batch=agc_src_batch;
		float estimation_db=agc_estimation_db;
		predicted_db=agc_predicted_db;

		pred_time=pred_time_sec;

		ptype=AgcCalculator::res_dsp_prediction;

		bool local_prediction=(local_predict) || (dsp_predicted_db<0);

		if ((agc_batch!=0) && (estimation_db>=0))
		{
			last_proc=proc_time_us;

			if (agc_batch<pred_batch)
			{
				pipe=int(pred_batch-agc_batch);
				int pred_step=pipe;
				if (pipe>k_pipe_stat_max)
				{
					++too_old;
					pred_step=k_pipe_stat_max;
				}

				float dt=0;
				unsigned int tindex=bindex;
				for(int i=0; i<pred_step; ++i)
				{
					dt+=btime[tindex];
					tindex=(tindex-1) & ((k_pipe_stat_max-1));
				}
				unsigned int agc_proc_batch_us=(btime[(tindex-(pred_step-1)) & ((k_pipe_stat_max-1))])*1000000;  //us

				pred_time=dt;

				dfe_prediction_db=AGC_att_prediction(dsp_att_k, pred_time, pstate);

				if (agc_proc_batch_us>last_proc)
					last_spare=agc_proc_batch_us-last_proc;
				else
					last_spare=0;

				if (reg_counter<reg_batches)
				{
					min_pipe=pipe;
					max_pipe=pipe;
					min_dt=pred_time;
					max_dt=pred_time;
					++reg_counter;
					predicted_db=-1;
					min_spare=last_spare;
					max_spare=last_spare;
					min_proc=last_proc;
					max_proc=last_proc;
				}
				else
				{
					if (pipe<min_pipe)
					{
						min_pipe=pipe;
					}
					if (pipe>max_pipe)
					{
						max_pipe=pipe;
					}

					if (pred_time<min_dt)
						min_dt=pred_time;
					if (pred_time>max_dt)
						max_dt=pred_time;

					if (last_proc<min_proc)
						min_proc=last_proc;
					if (last_proc>max_proc)
						max_proc=last_proc;

					if (last_spare<min_spare)
						min_spare=last_spare;
					if (last_spare>max_spare)
						max_spare=last_spare;
				}
				if (pipe>0)
				{
					if (pipe>=7)
					{
						++pipe_stat[7];
					}
					else
						++pipe_stat[pipe];
				}
				else
				{
					++pipe_stat[0];
				}

				if (local_prediction)
				{
					predicted_db=dfe_prediction_db;
					ptype=AgcCalculator::res_local_prediction;
				}
			}
			else
			{
				//Nothing use last
				++invalid_batch;
			}
		}
		else
		{
			reg_counter=0;
			pipe=-1;
			predicted_db=-1;
			++invalid_agc;
		}

		if (predicted_db>MAX_ATT)
			predicted_db=MAX_ATT;


		float res_predicted_db=predicted_db;

		if (disabled || ((mode_mask & mode_actual)==0))
		{
			res_predicted_db=-1;
			ptype=AgcCalculator::res_disabled;
		}

		if (ptype_)
			*ptype_=ptype;

		return res_predicted_db;
	}
};

static simple_agc_calc_data_t autoMonitorable_agcdata;

class AgcInitializer
{
public:
	AgcInitializer()
	{
		autoMonitorable_agcdata.mode_mask=(1<<AgcCalculator::search_as)|(1<<AgcCalculator::search_aa); //|(1<<AgcCalculator::track_aa);
	}
	static void setMode(unsigned mode_mask)
	{
		autoMonitorable_agcdata.mode_actual=mode_mask;
	}
};

static AgcInitializer agcInitializer;

void AgcCalculator::agcReset(unsigned int agc_regimation_batches)
{
	autoMonitorable_agcdata.reset(agc_regimation_batches);
	//autoMonitorable_agcdata.mode_actual=1;
}

void AgcCalculator::agcMode(agc_mode_t agcMode)
{
	agcInitializer.setMode(1U<<static_cast<unsigned int>(agcMode));
}

float AgcCalculator::predictAttenuationDb(pred_result_type_t* ptype, float agc_estimation_db, float agc_predicted_db, float dsp_att_k, unsigned int agc_batch, unsigned int pred_batch, unsigned batch_duration_us, unsigned int proc_time_us)
{
	return autoMonitorable_agcdata.predict(ptype, agc_estimation_db, agc_predicted_db, dsp_att_k, agc_batch, pred_batch, batch_duration_us/1000000.0f, proc_time_us);
}

float AgcCalculator::predictionTime()
{
	return localPrediction() ? autoMonitorable_agcdata.dsp_prediction_sec : autoMonitorable_agcdata.pred_time;
}

bool AgcCalculator::localPrediction()
{
	return autoMonitorable_agcdata.local_predict;
}

int AgcCalculator::agcPipe()
{
	return autoMonitorable_agcdata.pipe;
}



