/*
 * BatchPerformance.cpp
 *
 */

#include "BatchPerformance.h"

#include "rtg_high_resolution_timer.h"

#include "rtlog.h"



class BatchPerformanceImplementation
{
public:
	static constexpr float BATCH_TT_SEC_LSB=64E-6;
	static constexpr int BATCH_TT_US_LSB=64;

	typedef unsigned long long btime_us_t;
	struct batch_info_t
	{
		int length;
		int work_time;
		int spare_time;
		float load;
		int exp_length;
		int ddelta;
		unsigned int type;

		int rpipe;
		int rdelay_time;
		int rspare_time;

		int ttag;
		int avio_tt;
		int avio_shift;
	};

	struct batch_info_set_t
	{
		bool valid;
		batch_info_t min;
		batch_info_t max;
		batch_info_t last;
	};

	enum { history_depth=32, history_mod=~(history_depth-1), batch_types=32};

	bool disabled;
	btime_us_t bstart;
	btime_us_t bend;
    unsigned int bcounter;

	int batch_length;

	int tt_precision;
	int tt_err;
	volatile float tt_sec;

	batch_info_set_t current;

	batch_info_t history[history_depth];


	batch_info_set_t btypes[batch_types];

	batch_info_t last;

	unsigned int hcursor;

	int exp_length;
	int next_length;


	bool batch_terminated;
	unsigned int last_btype;
	unsigned int next_btype;

	btime_us_t rtime;
	unsigned int rbatch;
	int rarrived;

	int avio_max_shift;

	int tt_err_log_threshold;

	void minmax(batch_info_set_t& bt, const batch_info_t& b)
	{
		//if (btype<batch_types)
		{
			//batch_info_set_t& bt=btypes[btype];

			if (!bt.valid)
			{
				bt.max=b;
				bt.min=b;
				bt.valid=true;
			}
			else
			{
				if (bt.min.load>b.load)
					bt.min=b;
				if (bt.max.load<b.load)
				{
					bt.max=b;
				}
			}
			bt.last=b;
		}
	}

	void push_end(unsigned int btype, unsigned int next_batch_duration_us)
	{
		bend=rtg_hrt_to_microseconds_longlong(rtg_hrt_get());
		batch_terminated=true;
		next_btype=btype;
		next_length=next_batch_duration_us;
	}

	void push_start(unsigned int bcounter_, int batch_timetag, int avionics_timetag)
	{
		btime_us_t now=rtg_hrt_to_microseconds_longlong(rtg_hrt_get());

		{
			tt_sec=float(batch_timetag)*BATCH_TT_SEC_LSB;
			int sec2tt=tt_sec/BATCH_TT_SEC_LSB;
			tt_precision=batch_timetag-sec2tt;
			tt_err=tt_precision*BATCH_TT_US_LSB;

			if (tt_err_log_threshold)
			{
				int t=tt_err<0 ? -tt_err : tt_err;
				if (t>tt_err_log_threshold)
				{
					rtlog_printf("BPERF", rtlog_warning|rtlog_breaking_news, "TT Precision: %d != %f (err=%dus)", batch_timetag, tt_sec, tt_err);
				}
			}
		}

		batch_length=now-bstart;

		if (!disabled)
		{
			if (batch_terminated && exp_length)
			{
				batch_info_t& b=history[hcursor];

				b.ttag=batch_timetag;
				b.avio_tt=avionics_timetag;
				b.avio_shift=batch_timetag-avionics_timetag;
				if (avio_max_shift)
				{
					if (batch_timetag<avionics_timetag)
						rtlog_printf("BPERF", rtlog_warning|rtlog_breaking_news, "AVTT %d > %d (%d)", avionics_timetag, batch_timetag, b.avio_shift);
					else if (b.avio_shift>avio_max_shift)
						rtlog_printf("BPERF", rtlog_warning|rtlog_breaking_news, "AVTT %d <> %d (%d)", avionics_timetag, batch_timetag, b.avio_shift);
				}

				b.length=batch_length;
				b.exp_length=exp_length;
				b.work_time=bend-bstart;
				b.spare_time=exp_length-b.work_time;
				b.type=last_btype;
				b.ddelta=b.length-b.exp_length;

				b.rpipe=rbatch-bcounter;
				b.rdelay_time=rtime-bstart;
				b.rspare_time=now-rtime;

				if ((exp_length>100) && (b.work_time>10))
				{
					b.load=((float(b.work_time)/float(exp_length))*100.0f);
				}
				else
					b.load=100;

				if (last_btype<batch_types)
					minmax(btypes[last_btype], b);
				if (last_btype>0)
					minmax(current, b);

				if (hcursor>=(history_depth-1))
					hcursor=0;
				else
					++hcursor;

				last=b;
			}
		}
		exp_length=next_length;
		next_length=0;
		last_btype=next_btype;
		batch_terminated=false;
		bstart=now;
		bcounter=bcounter_;
	}

	void push_results(unsigned int bcounter)
	{
		btime_us_t now=rtg_hrt_to_microseconds_longlong(rtg_hrt_get());
		rtime=now;
		rbatch=bcounter;
	}
};


static BatchPerformanceImplementation autoMonitorable_bperf;

static BatchPerformanceImplementation& cx=autoMonitorable_bperf;



void BatchPerformance::batchStart(unsigned int bcounter, int batch_timetag, int avionics_timetag)
{
	cx.push_start(bcounter, batch_timetag, avionics_timetag);
}

void BatchPerformance::batchEnd(unsigned int btype, unsigned int next_batch_duration_us)
{
	cx.push_end(btype, next_batch_duration_us);
}

void BatchPerformance::batchResultsRecevied(unsigned int bcounter)
{
	cx.push_results(bcounter);
}

const void* BatchPerformance::getLastOpaqueInfo(unsigned int& infosize)
{
	infosize=sizeof cx.last;
	return &cx.last;
}

