/*
 * postprocess.cpp
 *
 */

#include "ghost.h"
#include "ghost_playback.h"

#include "ghost_ipc.h"

#include "gtl_cmdio.h"

#include <string>

#include <string.h>
#include <map>

#include <stdio.h>
#include <math.h>
#include "math_32.h"

#include "idd_header.h"
#include "idd_shared_memory.h"
#include "idd_cdp_cmp.h"
#include "idd_cmp_cdp.h"

#include "AesaStream.h"

//#define AESA_RX_MESSAGE_MAX_SIZE            4096
//#define AESA_TX_MESSAGE_MAX_SIZE            (4096*2)

struct antenna_err_counters_t
{
    unsigned int rfif_sts_received;

    unsigned int rfif_bite_received;

    unsigned int rfif_rxn;

    unsigned int rfif_duty;
    unsigned int rfif_overpulse;

    unsigned int spare[16];
};

struct antenna_reply_buffer_t
{
    unsigned int updates;
	unsigned int hstate;
    unsigned int response_decoded;
	unsigned int msg_count_errors;
	unsigned int timeouterr;
	unsigned int crcerr;
	unsigned int lenerr;
	unsigned int rxerr;
	int pri_err;
	int ignore_aesa_status;

	unsigned int download_map_executed;
	unsigned int spare[6];
	unsigned int data_udpates;
	unsigned int data_size;
	uint8_t data[AESA_RX_MESSAGE_MAX_SIZE];

	unsigned int cbite_mode;
    antenna_err_counters_t	errs;
};

struct antenna_cmd_buffer_t
{
    unsigned int h[8];
	uint8_t data[AESA_TX_MESSAGE_MAX_SIZE];
};

static int pp_level=0;
static unsigned int pp_srcid;
static const char* pp_srcname;

struct cmp_cdp_shared_struct_t
{
    idd_comm::shared_memory_header_t memHeader;
    idd_cmp_cdp_command_t<idd_blob::ChunkWriter> msg;
    idd_comm::shared_memory_footer_t memFooter;
};

struct dbg_t
{
	iddtypes::mode_command_t mode;
	iddtypes::scan_volume_t sv;
	iddtypes::frequency_selection_cmd_t fsel;
	iddtypes::inu_data_t inu;
	iddtypes::cursor_data_t crs;
	iddtypes::as_gnd_tgt_data_t as_gnd_tgt;
	iddtypes::gnd_tgt_data_t gnd_tgt;
	iddtypes::waveform_parameter_t wf;
	iddtypes::acquisition_setup_data_t acq;
};

struct cdp_bite_shared_struct_t
{
    idd_comm::shared_memory_header_t memHeader;
    idd_cdp_bite_report_t<idd_blob::ChunkWriter> msg;
    idd_comm::shared_memory_footer_t memFooter;
};

static const char* m1553str(unsigned int n)
{
	static const char *str[]={
			  "RWS",
			  "VS",
			  "ACM",
			  "TWS",
			  "4",
			  "GM",
			  "SEA1",
			  "SEA2",
			  "GMTI",
			  "BCN",
			  "AGR",
			  "TA",
			  "WA",
			  "TS",
			  "ETET",
			  "DLY"
			};
	return str[n];
}

static const char* m2str(unsigned int n)
{
	static const char* str[]=
	{
		    "idle",
		    "ibit",
		    "gm",
		    "dbs",
		    "rws",
		    "vs",
		    "acm",
		    "tws",
		    "sea1",
		    "sea2",
		    "gmti",
		    "bcn",
		    "sam",
		    "ta",
		    "wa",
		    "stt",
		    "dtt",
		    "sstt",
		    "acq",
		    "ftt",
		    "agr",

		    "sar",

		    "inv",

		    "dummy",                 /*to be used only as a defualt mode for prototypal development*/
		    "ETET",    /*dedicated to special REP test for HW validation*/

		    "boot",
		    "??", "??", "??"
	};
	return str[n];
}

static const char* wf2str(unsigned int n)
{
    static const char* str[]={
      "idle",
      "lprf",
      "mprf",
      "hprf",
      "lprfnc",
      "mprf_short",
      "agr_short",
      "agr_long",
      "mprf_look_up",
      "mprf_adaptive",
      "auto_without_lprf_nc"
	};
	return str[n];
}

struct cdp_res_shared_struct_t
{
	idd_comm::shared_memory_header_t memHeader;
	idd_cdp_cmp_status_t<idd_blob::ChunkWriter> data;
	idd_comm::shared_memory_footer_t memFooter;
};
//cdp_res_shared_struct_t cdp_res_struct;
#include "data_1553.h"

#include <vector>

static bool aesa_details=true;

static FILE* idb_fd;
static FILE* aesa_fd;

static unsigned int rep_print_mask=0x0F;

static void rep_vfprintf(const char* fmt, va_list args)
{

	if (idb_fd && (rep_print_mask & 1))
		vfprintf(idb_fd, fmt, args);

	if (aesa_fd && (rep_print_mask & 2))
		vfprintf(aesa_fd, fmt, args);
}

static void rep_fprintf(const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	rep_vfprintf(fmt, args);
	va_end(args);
}

static void rep_xprintf(float v)
{
	rep_fprintf("%9.6f;", v);
}

static void rep_xprintf(uint16_t v)
{
	rep_fprintf("%6u;", v);
}

static void rep_xprintf(uint8_t v)
{
	rep_fprintf("%3u;", v);
}

class ItemReportFlow
{
public:
	unsigned int flow_mask;

	ItemReportFlow(unsigned int mask=0x0F):
		flow_mask(mask)
	{
	}
};

class ItemBase
{
public:
	const char* name;
	const char* fmt;
	int width;
	bool diff;

	ItemReportFlow flowMask;

	virtual bool compare() { return false;}
	virtual void actualize() {}

	virtual bool compareExt()
	{
		bool r=compare();
		if (r)
		{
			cmdio_pwarning("Changed: %s = %s", name, str());
		}
		return r;
	}

	virtual void printFiled()
	{
		print();
		rep_fprintf(";");
	}

	virtual void print() {}
	virtual const char* str() { return "?";}

	void writeHeader()
	{
		rep_fprintf("%-*.*s;", width, width, name);
	}

	ItemBase(const char* iname, int iwidth, const char* ifmt, bool idiff, ItemReportFlow fm=ItemReportFlow());

	bool outFlow(unsigned int n=0)
	{
		if (flowMask.flow_mask & (1<<n))
			return true;
		return false;
	}

	virtual ~ItemBase() {}
};

static std::vector<ItemBase*> idb;

ItemBase::ItemBase(const char* iname, int iwidth, const char* ifmt, bool idiff, ItemReportFlow fm):
	name(iname),
	fmt(ifmt),
	width(iwidth),
	diff(idiff),
	flowMask(fm)
{
	idb.push_back(this);
}

template<typename T_> class Item: public ItemBase
{
public:
	typedef Item<T_> item_t;

	Item(const char* iname, int iwidth, const char* ifmt, bool idiff, ItemReportFlow fm=ItemReportFlow()):
		ItemBase(iname, iwidth, ifmt, idiff, fm)
	{
		//idb.push_back(this);
	}

	typedef T_ type;
	type value;
	type pvalue;

	bool compare()
	{
		return diff && (value!=pvalue);
	}

	const char* str() override
	{
		static char s[128];
		va_list args;
		snprintf(s, sizeof s, fmt, value);
		return s;
	}

	void print() override
    {
		rep_fprintf(fmt, value);
    }

	virtual void actualize()
	{
		pvalue=value;
	}

	virtual void set(type v) { value=v;}
};

class ItemString: public Item<std::string>
{
public:
	ItemString(const char* iname, bool idiff=false):
		Item(iname, 8, "%-8.8s", idiff)
	{
	}

	void print() override
    {
		rep_fprintf(fmt, value.c_str());
    }
};

class ItemStringSmall: public Item<std::string>
{
public:
	ItemStringSmall(const char* iname, bool idiff=false):
		Item(iname, 4, "%-4.4s", idiff)
	{
	}

	void print() override
    {
		rep_fprintf(fmt, value.c_str());
    }
};

class ItemUInt: public Item<unsigned int>
{
public:
	ItemUInt(const char* iname, bool idiff=false):
		Item(iname, 9, "%9u", idiff)
	{
	}
};

class ItemHex: public Item<unsigned int>
{
public:
	ItemHex(const char* iname, bool idiff=false):
		Item(iname, 10, "0x%8.8X", idiff)
	{
	}
};

class ItemUIntSmall: public Item<unsigned int>
{
public:
	ItemUIntSmall(const char* iname, bool idiff=false):
		Item(iname, 4, "%4u", idiff)
	{
	}
};

class ItemUIntSmallLatched: public Item<unsigned int>
{
public:
	ItemUIntSmallLatched(const char* iname, bool idiff=false):
		Item(iname, 4, "%4u", idiff)
	{
	}

	void set(type v) override
	{
		value+=v;
	}
	void actualize() override
	{
		value=0;
	}
};

class ItemInt: public Item<int>
{
public:
	ItemInt(const char* iname, bool idiff=false):
		Item(iname, 9, "%9i", idiff)
	{
	}
};

class ItemFloat: public Item<float>
{
public:
	ItemFloat(const char* iname, bool idiff=false):
		Item(iname, 12, "%12.3f", idiff)
	{
	}
};

class ItemFloatSmall: public Item<float>
{
public:
	ItemFloatSmall(const char* iname, bool idiff=false):
		Item(iname, 12, "%12.6f", idiff)
	{
	}
};


static ItemString		itemTime("time", false);
static ItemUInt 		itemDfeBatch("Batch");
static ItemUInt 		itemDfeTime("TTAG", false);
static ItemStringSmall  item1553StrMode("Mode", true);
static ItemUIntSmall	itemDfeMode("Mode", true);
static ItemStringSmall  itemDfeStrMode("Mode", false);
static ItemStringSmall  itemDfeStrStby("STBY", true);

static ItemUInt 		itemAesaUpdate("A-Upd", false);
static ItemUIntSmall 		itemAesaState("A-State", true);
static ItemUInt 		itemAesaErr("A-Err", true);
static ItemUInt 		itemAesaOk("A-Ok", false);
static ItemUInt 		itemAesaCommErr("A-COM", false);
static ItemStringSmall  itemAesaStr("AStr", false);

static ItemBase itemBreak("brk", 0, 0, false);

static ItemUIntSmall    itemScale("Scale", true);
static ItemStringSmall  itemStrScale("Scale", false);
static ItemUIntSmall    itemDfeRad("Rad", true);
static ItemUIntSmall    itemDfeWF("WF", true);
static ItemStringSmall  itemDefWFStr("WF", false);
static ItemUIntSmall    itemSarWF("SAR-WF", true);
static ItemHex			itemDfeHealth("Health", true);
static ItemStringSmall	itemAesaHealth("Health", false);

//static ItemUInt itemDspBatch("DSP-Batch");
static ItemUIntSmallLatched itemDet("Det");
static ItemUIntSmall itemStt("STT");

static ItemFloat itemTrueHeading("TrueHead");
static ItemFloat itemPAZ("Ptaz");
static ItemFloat itemBaroAlt("BatoAlt");
static ItemFloatSmall itemLat("Lat");
static ItemFloatSmall itemLon("Lon");

static bool rdrInStby=false;

static bool fix_sar;

struct hdr0_mini_t
{
	uint32_t preamble[17-(6+3)];

    uint32_t header_type;
    uint32_t header_counter;
    uint32_t ch_mask;

    iddheader::ge_header_t ge;
    //signal_mini_header_t hdr;
};

struct block_stat_t
{
	unsigned int num;
	unsigned int srcid;
	const char* srcname;
	block_stat_t(): num(0), srcid(0), srcname(0) {}
};

typedef std::map<std::string, block_stat_t> bmap_t;

struct blob_stat_t
{
	unsigned int num;
	unsigned int counter;
	unsigned int batch;

	void add(const char* s)
	{
		char fullname[512];
		fullname[0]=0;
		if (pp_srcname)
		{
			strcpy(fullname, pp_srcname);
			strcat(fullname, ":");
			strcat(fullname, s);
			s=fullname;
		}

#if 0
		std::string fullname(s);
		if (pp_srcname)
			fullname=std::string(pp_srcname)+std::string(":")+fullname;
#endif
		++bmap_[s].num;
		bmap_[s].srcid=pp_srcid;
		bmap_[s].srcname=pp_srcname;
	}
	bmap_t bmap_;

	blob_stat_t(): num(0), counter(0), batch(0)  {}
};

struct proc_stat_t
{
	typedef std::map<std::string, blob_stat_t> blobmap_t;

	unsigned int blocks;

	unsigned int sign_corrupted;

	blobmap_t bmap;
};

static proc_stat_t proc_stat;

class GhostLoggerCmdio: public GhostLogger
{
public:
	//virtual void logError(const char* fmt, ...);
	void vPrint(Level level, const char* fmt, va_list args) override
	{
		cmdio_severity_t log_level=cmdio_info;
		switch(level)
		{
		default:
			break;
		case GhostLogger::debug:
			log_level=cmdio_debug;
			break;
		case GhostLogger::trace:
			log_level=cmdio_debug;
			break;
		case GhostLogger::success:
			log_level=cmdio_success;
			break;
		case GhostLogger::warning:
			break;
		case GhostLogger::error:
			log_level=cmdio_error;
			break;
		case GhostLogger::fatal:
		case GhostLogger::fail:
			log_level=cmdio_error;
			break;
		}
		char buffer[2048];

		vsnprintf(buffer, sizeof buffer, fmt, args);
		if ((log_level==cmdio_error) && (rdrInStby))
		{

		}
		else
			cmdio_printf(log_level, "PP: %s", buffer);
	}
};

class GrifoPlaybackReaderMemory: public GrifoPlaybackStreamReader
{
public:
	const char* buffer;
	stream_offset_t bsize;
	const char* file_name;
	stream_offset_t cursor;

	GrifoPlaybackReaderMemory():
		buffer(0),
		bsize(0),
		file_name("memory"),
		cursor(0)
	{
	}


	void setBuffer(const void* b, unsigned int size_bytes)
	{
		buffer=(const char*)b;
		bsize=size_bytes;
		cursor=0;
	}

	const char* streamName() const
	{
		return "memory";
	}

	bool streamOpen(const char* name) override
	{
		return true;
	}

	bool isOpen() override
	{
		return true;
	}

	void setAutorewind(bool /*ena*/)
	{
	}

	void setStreamName(const char* name) override
	{
		file_name=name;
	}

	int read(void* datum, unsigned int size)
	{
		if (!datum)
			return -1;

		stream_offset_t tmp=cursor+size;
		if (tmp<=bsize)
		{
			memcpy(datum, &buffer[cursor], size);
			cursor+=size;
			return size;
		}
		return -1;
	}

	template<typename T_> int readDatum(T_* datum) { return read(datum, sizeof *datum);}

	stream_offset_t currentPosition() const override
	{
		return cursor;
	}

	const void* getDataPointer(stream_offset_t pos) const override
	{
		return &buffer[pos];
	}

	int setCurrentPosition(stream_offset_t pos)
	{
		if ((pos>=bsize) || (pos<0))
			return 1;
		cursor=pos;
		return 0;
	}

	int moveCurrentPosition(stream_offset_t offset) override
	{
		stream_offset_t tmp=cursor+offset;
		return setCurrentPosition(tmp);
	}

	bool eof() override
	{
		return cursor>=bsize;
	}

	virtual void rewind()
	{
		setCurrentPosition(0);
	}

	virtual bool error() { return false;}

	void* data(stream_offset_t offset) { return (void*)&buffer[offset];}

	~GrifoPlaybackReaderMemory()
	{
	}
};

static GrifoPlaybackReaderMemory memoryReader;

extern unsigned int* fpga_sum;
extern unsigned int fpga_sum_size;

struct BlockObserver
{
	int hit;
	FILE* fd;
	FILE* sarFd;
	const char* infile;

	GrifoPlaybackStream::blockHandle hdrBlock;

	GhostEifIpc::avionics_adaptor_message_store_t d1553;

	uint32_t hdrBuffer[4096];

	hdr0_mini_t* h; //=reinterpret_cast<hdr0_mini_t*>(hdrBuffer);
	hdr0_mini_t fpgaHdr;
	hdr0_mini_t dspHdr;

	unsigned int d1553_time;

	bool force_report;
	int aesa_tx_pending;
	int aesa_rx_pending;

	unsigned int first_batch;

	BlockObserver():
		hit(0),
		fd(0),
		sarFd(0),
		infile(0)
	{
		d1553_time=0;
		resTxCursor=0;

		force_report=false;
		aesa_tx_pending=0;
		aesa_rx_pending=0;

		first_batch=0;

		h=0;
	}

	void sarPrintf(const char* fmt, ...)
	{
		va_list args;
		static char strbuffer[1024];
		if (sarFd==0)
		{
			const char* fname_base=cmdio_get_proc_file(0);
			snprintf(strbuffer, sizeof strbuffer, "sar-%s.txt", fname_base);
			sarFd=fopen(strbuffer, "w");
		}
		if (!sarFd)
			return;
		va_start(args, fmt);
		vsnprintf(strbuffer, sizeof strbuffer, fmt, args);
		fprintf(sarFd, strbuffer);
		va_end(args);
		fflush(sarFd);
	}

	bool openFile()
	{
		if (pp_level<0)
			return false;

		if (fd)
			return true;

		infile=cmdio_get_proc_file(0);
		char fname[512];
		snprintf(fname, sizeof fname, "pp-%s.txt", infile);
		fd=fopen(fname, "w");
		if (!fd)
		{
			cmdio_oserror("PP history file %s", fname);
			return false;
		}
		idb_fd=fd;

		if (aesa_details)
		{
			snprintf(fname, sizeof fname, "pp-aesa-%s.txt", infile);
			aesa_fd=fopen(fname, "w");
			if (!aesa_fd)
			{
				cmdio_oserror("PP: AESA file %s", fname);
				//return false;
			}
		}

		rep_fprintf("%-32.32s;", "file");

		for(auto i: idb)
		{
			if (i!=&itemBreak)
				i->writeHeader();
		}

		rep_fprintf("-\n");
		fflush(idb_fd);


		return true;
	}

	void repUpdate(bool showAll=false)
	{
		static unsigned int p_time;
		static unsigned int d1553_time_update;

		if (!fd)
		{
			if (!openFile())
				return;
		}

		rep_print_mask=0x0F;

		bool changed=false;
		for(auto i: idb)
		{
			changed=i->compareExt();
			if (changed)
				break;
		}

		if (pp_level>0)
		{
			unsigned int d_time=itemDfeTime.value-p_time;
			static const unsigned int d_one_second=1000000/64;
			if (d_time>=(d_one_second*(unsigned int)pp_level))
			{
				changed=true;
			}

			unsigned int d1553_delta=(d1553_time-d1553_time_update) & 0x00FFFF;

			if ((d1553_delta*2)>=(unsigned int) pp_level)
			{
				changed=true;
			}

			if (changed)
			{
				p_time=itemDfeTime.value;
				d1553_time_update=d1553_time;
			}
		}

		if (showAll || changed || force_report)
		{
			//repPrintf("%s;%u",cmdio_get_proc_file(0),h->ge.signal.bacth_counter);
			rep_fprintf("%-32.32s;", cmdio_get_proc_file(0));

			for(auto i: idb)
			{
				if (i==&itemBreak)
				{
					rep_print_mask=0x2;
					aesaDetails();
					rep_print_mask=0x1;
					continue;
				}

				i->printFiled();
				i->actualize();

			}

			rep_print_mask=0x0F;
			rep_fprintf("0\n");
		}

		force_report=false;
	}

	AesaDeserializer::DesResults res;

	unsigned int resTxCursor;
	AesaDeserializer::DesResults resTx[8];

	void aesaDetails()
	{
		unsigned int tx_cursor=resTxCursor; //(resTxCursor-1) & 0x7;

		if (aesa_tx_pending)
		{
			AesaDeserializer::DesResults& tx=resTx[tx_cursor];

			rep_fprintf(">>>%3d;", tx.msg_num);

			int n=tx.msg_num;
			if (n>4)
			{
				n=4;
			}
			for(int i=0; i<n; ++i)
			{
				AesaDeserializer::DesResultMsg& m=tx.msg[i];
				rep_fprintf("0x%02.2X;%u;%3u;%4d;%-12.12s;", m.id, m.area, m.tag, 0, m.name);
				if (m.payload_id==1)
				{
					const auto& b=m.payload.beam_cmd;
					rep_fprintf("%d;", m.payload_destination);
					rep_xprintf(b.tx_freq);
					rep_xprintf(b.rx_freq);
					rep_xprintf(b.tx_u);
					rep_xprintf(b.tx_v);

					rep_xprintf(b.rx_u);
					rep_xprintf(b.rx_v);

					rep_xprintf(b.tx_ctrl_state_index);
					rep_xprintf(b.tx_enable_pattern_index);

					rep_xprintf(b.rx_ctrl_state_index);
					rep_xprintf(b.rx_enable_pattern_index);

					rep_xprintf(b.guard_rx_u);
					rep_xprintf(b.guard_rx_v);
					rep_xprintf(b.guard_rx_ctrl_state_index);
					rep_xprintf(b.guard_rx_enable_pattern_index);

					rep_xprintf(b.sync_divisor);
					rep_xprintf(b.duty_ratio);
				}
				rep_fprintf(">;");
			}
			for(int i=n; i<4; ++i)
			{
				rep_fprintf("0x%02.2X;%u;%3u;%4d;%-12.12s;>;", 0, 0, 0, 0, "");
			}
		}
		if (aesa_rx_pending)
		{
		  int n=res.msg_num;
			if (n>16)
			{
				n=16;
			}
			rep_fprintf("<<<%3d;", res.msg_num);
			for(int i=0; i<n; ++i)
			{
				AesaDeserializer::DesResultMsg& m=res.msg[i];
				rep_fprintf("0x%02.2X;%u;%3u;%4d;%-12.12s;", m.id, m.area, m.tag, m.error, m.name);
				if (m.payload_id==1)
				{
					const auto& b=m.payload.cti_sts;
					rep_fprintf("%2.2X;", b.byte_0.raw);
					rep_fprintf("%2.2X;", b.byte_1.raw);
					rep_fprintf("%2.2X;", b.byte_2.raw);
					rep_fprintf("%d;", b.byte_3.cti_temperature);
				}
				rep_fprintf("<;");
			}
		}
		aesa_tx_pending=0;
		aesa_rx_pending=0;
	}

	void repPrintf(const char* fmt, ...)
	{
		if (!fd)
		{
			if (!openFile())
				return;
		}

		va_list args;
		va_start(args, fmt);
		rep_vfprintf(fmt, args);
		va_end(args);
	}
	void reportHeader(const hdr0_mini_t* h)
	{
		//repPrintf("%s;%u",cmdio_get_proc_file(0),h->ge.signal.bacth_counter);
	}

	void updateReport()
	{

	}

    struct dbg_data_t
    {
        int wfId;

    	float azimuth;
    	float elevation;
    	float range_m;
    	float range_us;
    	float lsn_dly_us;
    	float lsn_len_us;
    	float deramp_dly_us;
    	unsigned int nrbin;
    	float bchirp;
    	float patch_len_us;
    	float prt;

    	unsigned int prt_ticks;
    	unsigned int deramp_ticks;
        unsigned int lsn_dly_ticks;
        unsigned int lsn_len_ticks;

    	unsigned int spare[6];

    	//SarLegacyMode::tracking_t trk;

    	unsigned int spare_trailer[14-8];

    	unsigned int trailing[128];
    };
    dbg_data_t dbg;

	int cdpStsObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		//repUpdate();

		static cdp_res_shared_struct_t cdp_res_struct;
		GrifoPlaybackStream::readBlockData(&cdp_res_struct, b, sizeof cdp_res_struct);

		itemDfeBatch.value=cdp_res_struct.data.timetag.constData().batch_id;
		itemDfeTime.value=cdp_res_struct.data.timetag.constData().time;
		itemDfeRad.value=cdp_res_struct.data.status.constData().radiate;
		itemDfeMode.value=cdp_res_struct.data.status.constData().mode;

		itemDfeStrMode.value=m2str(itemDfeMode.value);

		unsigned int cdp_health_status=cdp_res_struct.data.status.constData().health_status;
		//unsigned int aesa_health_status=cdp_res_struct.data.status.constData().antenna_state;

		itemDfeHealth.value=cdp_health_status;
		if (cdp_health_status & 0x01000000)
			itemAesaHealth.value="COM";
		else
			itemAesaHealth.value="";
		//itemAesaHealth.value=cdp_res_struct.data.status.constData().antenna_state;

		itemDfeWF.value=cdp_res_struct.data.status.constData().waveform;
		itemDefWFStr.value=wf2str(itemDfeWF.value);

		itemDet.set(cdp_res_struct.data.detections.constData().num_of_detected_tgts);

		repUpdate();

		return 0;
	}

	int fpgaHeaderObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		//repPrintf("\n");

		hit=1;
		hdrBlock=b;

		GrifoPlaybackStream::scanFpgaHeader(hdrBuffer, 0, hdrBlock);

		/*hdr0_mini_t* */h=reinterpret_cast<hdr0_mini_t*>(hdrBuffer);
		fpgaHdr=*h;

		//reportHeader(h);

		//++proc_stat.bmap[std::string("PP")].bmap["FPGA-HDR"].num;
		proc_stat.bmap[std::string("PP")].add("FPGA-HDR");
		return 0;
	}

    int sigObserver(const GrifoPlaybackStream::blockHandle& b, const char* name, unsigned int* sig_cmp=0, int zero_check=0)
    {
    	if (pp_level>=5)
    	{
			static unsigned int sum_counter;
			static unsigned s_size;

			unsigned int expected_size=(dspHdr.ge.signal.packet_descr.npri*dspHdr.ge.signal.packet_descr.nrbin)*4;

			unsigned int batch=b.s_counter-first_batch;
			if  ((pp_level>20) || ((b.size!=s_size) && (pp_level>5)))
			{
				unsigned int x=b.size-(expected_size+56);
				cmdio_pwarning("%s %u(%u) size: %u -> %u (%u) <- %u (%u) [%u,%u]",
						name, b.s_counter, batch, s_size, b.size, sum_counter, expected_size, x, dspHdr.ge.signal.packet_descr.npri, dspHdr.ge.signal.packet_descr.nrbin);
			}

			unsigned char* p=(unsigned char*)GrifoPlaybackStream::getDataPointer(b);
			for(unsigned int i=16; i<(b.size-32); ++i)
			{
				uint32_t* d32=(uint32_t*)&p[i];
				if (d32[0]==0x5a5a5a5a)
				{
					auto bsw=GrifoPlaybackStream::tryDecodeSoftReference(&p[i]);

					cmdio_perror("%s %u(%u): Marker inside signal +%u: %s: %u", name, b.s_counter, batch, i, &p[i+68], bsw.s_counter);
					//break;
					++proc_stat.sign_corrupted;
					i+=4;
				}
			}

			if (sig_cmp)
			{
				static unsigned int fpga_x[128];

				uint32_t* d32_base=(uint32_t*)&p[0];
				uint32_t* d32=&d32_base[10];
				unsigned int zs=dspHdr.ge.signal.packet_descr.npri*dspHdr.ge.signal.packet_descr.nrbin;
				if (fpga_sum_size<(zs*4))
				{
					cmdio_perror("%s %u(%u): FPGA[%u,%u] too small %u", fpga_sum_size);
				}

				for(unsigned int i=0; i<zs; ++i)
				{
					unsigned int v_dsp=d32[i];
					unsigned int v_sig=sig_cmp[i];
					fpga_x[i & 0x07F]=v_dsp-v_sig;
					if (v_dsp!=v_sig)
					{
						cmdio_perror("%s %u(%u): FPGA[%u,%u]->DSP mismatch @%u [%u,%u] 0x%X != 0x%X",
								name, b.s_counter, batch,
								dspHdr.ge.signal.packet_descr.npri,
								dspHdr.ge.signal.packet_descr.nrbin,
								i,
								i/dspHdr.ge.signal.packet_descr.nrbin,
								i-((i/dspHdr.ge.signal.packet_descr.nrbin)*dspHdr.ge.signal.packet_descr.nrbin),
								v_sig, v_dsp
								);
						break;
					}
				}
			}
			if (zero_check)
			{
				int zero=0;
				uint32_t* d32=(uint32_t*)&p[32];
				unsigned int zs=(b.size>>2)-128;
				for(unsigned int i=0; i<zs; ++i)
				{
					if (d32[i]==0)
					{
						++zero;
						if (zero>zero_check)
						{
							cmdio_perror("%s %u(%u): Zeros inside signal +%u: %d", name, b.s_counter, batch, i, zero);
							//break;
							++proc_stat.sign_corrupted;
							break;
						}
					}
					else
						zero=0;
				}
			}
			s_size=b.size;
			++sum_counter;
    	}
    	return 0;
    }

    int sumObserver(const GrifoPlaybackStream::blockHandle& b)
    {
    	return sigObserver(b, "SUM", fpga_sum, 12);
    }

    int dazObserver(const GrifoPlaybackStream::blockHandle& b)
    {
    	return sigObserver(b, "DAZ");
    }

    int delObserver(const GrifoPlaybackStream::blockHandle& b)
    {
    	return sigObserver(b, "DEL");
    }
    int guardObserver(const GrifoPlaybackStream::blockHandle& b)
    {
    	return sigObserver(b, "GUA");
    }

	int dspHeaderObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		//repPrintf("\n");

		hit=1;
		hdrBlock=b;

		hdrBlock.offset-=44;
		hdrBlock.size+=44;

		hdr0_mini_t* hfix=reinterpret_cast<hdr0_mini_t*>(memoryReader.data(hdrBlock.offset));

		GrifoPlaybackStream::scanFpgaHeader(hdrBuffer, 0, hdrBlock);


		if (fix_sar)
		{
			//if (b.size<sizeof dbg)
			{
				const float k_lsn_fix=0; //+33
				float lsn_56=dbg.lsn_dly_us*56;
				unsigned int t=ceilf(lsn_56)+k_lsn_fix; //+33;
				dbg.lsn_dly_ticks=t;

				lsn_56=dbg.deramp_dly_us*56;
				t=ceilf(lsn_56);
				dbg.deramp_ticks=t;

				lsn_56=dbg.lsn_len_us*56;
				t=ceilf(lsn_56);
				dbg.lsn_len_ticks=t;

				//float lsn_recalc=t/56.0f;
			}

			if (hfix)
			{
				static bool sar_hdr_fixed;
				if (!sar_hdr_fixed)
				{
					cmdio_pwarning("Fixing SAR headers");
					sar_hdr_fixed=true;
				}
				{//Fix SAR Header
			    	auto t2us=[](unsigned int  v) -> float { return v/56.0f;}; //TODO: use a function from Timer

					//float* f=reinterpret_cast<float*>(&(hdrin->header.ge.general_settings.waveform.spare[1]));
					//hdrin->header.ge.general_settings.waveform.spare[0]=dbg.wfId;
					hfix->ge.general_settings.waveform.wfcode=dbg.wfId;
					hfix->ge.function_settings.pe_settings.deramp_dly_us=t2us(dbg.deramp_ticks); //dbg.deramp_dly_us;
					hfix->ge.function_settings.pe_settings.lsn_dly_us=t2us(dbg.lsn_dly_ticks); //dbg.lsn_dly_us;
					hfix->ge.function_settings.pe_settings.lsn_len_us=t2us(dbg.lsn_len_ticks); //dbg.lsn_len_us;
					hfix->ge.function_settings.pe_settings.tgt_altitude=0;
					hfix->ge.function_settings.pe_settings.tgt_longitude=hfix->ge.general_settings.navigation.geo_pos.lon_rad;
					hfix->ge.function_settings.pe_settings.tgt_latitude=hfix->ge.general_settings.navigation.geo_pos.lat_rad;
					hfix->ge.function_settings.pe_settings.tgt_band=0;
					hfix->ge.function_settings.pe_settings.clutter_band=0;

					hfix->ge.function_settings.pe_settings.rb_start=0;
					hfix->ge.function_settings.pe_settings.rb_stop=0;

					//cmdio_pwarning("dly %f", hfix->ge.function_settings.pe_settings.deramp_dly_us);

					//compute_sar_target(hdrin->header);
				}
			}
		}

		dspHdr=*hfix;
		if (first_batch==0)
			first_batch=dspHdr.ge.signal.bacth_counter;

		if (pp_level>10)
		{
			cmdio_pwarning("DSPHDR %u: Expected signal: [%u,%u]", dspHdr.ge.signal.bacth_counter, dspHdr.ge.signal.packet_descr.npri, dspHdr.ge.signal.packet_descr.nrbin);
		}
		//fpgaHdr=*h;

		//reportHeader(h);

		//++proc_stat.bmap[std::string("PP")].bmap["HDRDSP"].num;
		proc_stat.bmap[std::string("PP")].add("HDR");

		return 0;
	}

	static float w2f(uint16_t w0, uint16_t w1, float lsb)
	{
		int32_t tmp32=((unsigned int)w0)<<16 | ((unsigned int)w1);
		return tmp32*lsb;
	}

	int d1553Observer(const GrifoPlaybackStream::blockHandle& b)
	{

		bool ok=GrifoPlaybackStream::readBlockData(&d1553, b, sizeof d1553);
		if (!ok)
			return 0;
		//if (ok) GhostEifIpc::update1553(&d1553.d);

		//uint16_t a5_tt=d1553.d.a5[1];

		//char time_of_mission[128];
		double lat=0;
		double lon=0;
		//uint16_t date=d1553.d.a1[5];
		uint16_t time=d1553.d.a1[6];

		d1553_time=time;

		unsigned int tt=time*2;
		unsigned int h=tt/3600;
		unsigned int m=(tt-h*3600)/60;
		unsigned int s=(tt-(h*3600+m*60));

		int tmp32=((unsigned int)d1553.d.a4[23])<<16 | ((unsigned int)d1553.d.a4[24]);
		lat=tmp32*ICD1553_GEOPOS_DEG_LSB;

		//tmp32=((unsigned int)d1553.a4[25])<<16 | ((unsigned int)d1553.a4[26]);
		//lon=tmp32*ICD1553_GEOPOS_DEG_LSB;
		lon=w2f(d1553.d.a4[25], d1553.d.a4[26], ICD1553_GEOPOS_DEG_LSB);

		float ele=d1553.d.a4[9]*ICD1553_BARO_ALT_METERS_LSB;

		char buffer[128];
		snprintf(buffer, sizeof buffer, "%02u:%02u:%02u", h, m, s);
		itemTime.value=buffer;

		itemBaroAlt.value=ele;
		itemLat.value=lat;
		itemLon.value=lon;

		float thead=((signed short)d1553.d.a4[2])*ICD1553_SEMICIRCLE_DEG_LSB;
		//float mhead=((signed short)d1553.d.a4[3])*ICD1553_SEMICIRCLE_DEG_LSB;
		float ptaz=((signed short)d1553.d.a5[8])*ICD1553_SEMICIRCLE_DEG_LSB;

		itemTrueHeading.value=thead;
		itemPAZ.value=ptaz;

		unsigned int mode=(d1553.d.a2[0]>>12) & 0x0F;

		item1553StrMode.value=m1553str(mode);
		return 0;
	}

	int cmpcObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		static dbg_t cmp;
		GrifoPlaybackStream::readBlockData(&cmp, b, sizeof cmp);

		const char* scale="?";
		switch(cmp.mode.range_scale)
		{
		case 0: scale="??NM"; break;
		case 1: scale="10NM"; break;
		case 2: scale="20NM"; break;
		case 3: scale="40NM"; break;
		case 4: scale="80NM"; break;

		default:
			break;
		}

		itemStrScale.value=scale;

		itemScale.value=cmp.mode.range_scale;

		const char* stby="RAD";
		if (cmp.mode.stby)
		{
			rdrInStby=true;
			stby="STBY";
		}
		else
		{
			if (!cmp.mode.radiate)
				stby="SIL";
			rdrInStby=false;
		}

		if ((d1553.d.a2[0]>>6) & 0x1)
		{
			stby="STBY";
			rdrInStby=true;
		}

		itemDfeStrStby.value=stby;

		return 0;
	}

	int detrObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		return 0;
	}

	int sttdObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		return 0;
	}

	int mttrObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		return 0;
	}

	int sarObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		//dbg_data_t dbg;
		GrifoPlaybackStream::readBlockData(&dbg, b, b.size);
		itemSarWF.value=dbg.wfId;

#if 0
		float lsn_56=dbg.lsn_dly_us*56;
		unsigned int t=ceilf(lsn_56);
		float lsn_recalc=t/56.0f;
		float lsn_recalc32=math32_div(t, 56.0f);
		unsigned int t32=ceilf(math32_mul(lsn_recalc32, 56));
		float err=lsn_recalc-lsn_recalc32;
		sarPrintf("%f,%f: TGT: %24.12f t56: %24.12f ticks: %u LSN: %24.12f ERR: %24.12f t32: %u  LSN32: %24.12f ERR: %24.12f\n",
				dbg.azimuth,
				dbg.range_m,
				dbg.lsn_dly_us, lsn_56,
				t, lsn_recalc, lsn_recalc-dbg.lsn_dly_us,
				t32, lsn_recalc32,
				err);
#endif
		return 0;
	}

	int aesaRxObserver(const GrifoPlaybackStream::blockHandle& b)
	{
		static antenna_reply_buffer_t saved;

		GrifoPlaybackStream::readBlockData(&saved, b, sizeof saved);
		{
			const antenna_reply_buffer_t* x=&saved; //(raw_antenna_reply_buffer_t*)&(((uint32_t*)aesax)[36]);
			//unsigned int delta=x->msg_count_errors-saved.msg_count_errors;
			//unsigned int edelta=x->rxerr-saved.rxerr;
			//unsigned int tdelta=x->timeouterr-saved.timeouterr;

			itemAesaUpdate.value=x->updates;
			itemAesaState.value=x->hstate;
			itemAesaErr.value=x->msg_count_errors;

			const char* res_str=AesaDeserializer::desResponse(&res, x->data, sizeof x->data);
			itemAesaStr.value=res_str;
			itemAesaOk.value=res.ok;
			itemAesaCommErr.value=res.errors;

#if 0
			const char* errstr="OK";
			if (x->pri_err>=0)
			{
				cmdio_pwarning("(%u) AESA PRI Error: %d", out_line, x->pri_err);
				errstr="ERR";
			}

			fprintf(tfd, "%3s;%6u;%6u;%6u;%4d;%5d", errstr, delta, edelta, tdelta, x->pri_err, prog_min_time);

			saved=*x;
#endif
		}

		++aesa_rx_pending;
		//force_report=true;
		return 0;
	}

	int aesaTxObserver(const GrifoPlaybackStream::blockHandle& b)
	{

		static antenna_cmd_buffer_t saved;

		GrifoPlaybackStream::readBlockData(&saved, b, sizeof saved);

		resTxCursor=0;//(resTxCursor+1) & 0x7;

		AesaDeserializer::desCommand(&resTx[resTxCursor], saved.data, sizeof saved.data);

		++aesa_tx_pending;
		//force_report=true;

		return 0;
	}

	static int promObserver(GrifoPlaybackStream::block_observer_cookie_t cookie, const GrifoPlaybackStream::blockHandle& handle);
#if 0
	{
		++proc_stat.bmap[std::string("PP")].num;
		++proc_stat.bmap[std::string("PP")].bmap[handle.name].num;

		repUpdate();

		return 0;
	}
#endif

	void playback_initizalize()
	{
		GrifoPlaybackStream::registerMethodBlockObserver("D1553", *this, &BlockObserver::d1553Observer);

		//GrifoPlaybackStream::registerMethodBlockObserver("EXT CMPC", *this, &dfe_playback_t::cmpcObserver);
		GrifoPlaybackStream::registerMethodBlockObserver("CDPSTS", *this, &BlockObserver::cdpStsObserver);

		GrifoPlaybackStream::registerMethodBlockObserver("SUM", *this, &BlockObserver::sumObserver);
		GrifoPlaybackStream::registerMethodBlockObserver("GUARD", *this, &BlockObserver::guardObserver);
		GrifoPlaybackStream::registerMethodBlockObserver("DAZ", *this, &BlockObserver::dazObserver);
		GrifoPlaybackStream::registerMethodBlockObserver("DEL", *this, &BlockObserver::delObserver);

		GrifoPlaybackStream::registerMethodBlockObserver("DSPHDRIN", *this, &BlockObserver::dspHeaderObserver);

		GrifoPlaybackStream::registerMethodBlockObserver("DISPHEAD", *this, &BlockObserver::fpgaHeaderObserver);
		GrifoPlaybackStream::registerMethodHeaderObserver("EXT CMPC", *this, &BlockObserver::cmpcObserver);
		GrifoPlaybackStream::registerMethodHeaderObserver("EXT DETR", *this, &BlockObserver::detrObserver);
		GrifoPlaybackStream::registerMethodHeaderObserver("EXT STTD", *this, &BlockObserver::sttdObserver);
		GrifoPlaybackStream::registerMethodHeaderObserver("EXT MTTR", *this, &BlockObserver::mttrObserver);
		GrifoPlaybackStream::registerMethodHeaderObserver("EXT SARR", *this, &BlockObserver::sarObserver);


		GrifoPlaybackStream::registerMethodBlockObserver("AESATX", *this, &BlockObserver::aesaTxObserver);
		GrifoPlaybackStream::registerMethodBlockObserver("AESARX", *this, &BlockObserver::aesaRxObserver);

		GrifoPlaybackStream::registerPromisucousBlockObserver(&BlockObserver::promObserver, 0);

	}
};

static BlockObserver blockObserver;

int BlockObserver::promObserver(GrifoPlaybackStream::block_observer_cookie_t cookie, const GrifoPlaybackStream::blockHandle& handle)
{
	++proc_stat.bmap[std::string("PP")].num;
	//++proc_stat.bmap[std::string("PP")].bmap[handle.name].num;
	proc_stat.bmap[std::string("PP")].add(handle.name);


	blockObserver.repUpdate();

	return 0;
}

void postprocess_fixsar(bool fixit)
{
	fix_sar=fixit;
}

void postprocess_initialize(int level)
{
	pp_level=level;
	//if (pp_level<0)
	//	return;

	static GhostLoggerCmdio logger;
	Ghost::setLoggerInterface(&logger);
	GrifoPlaybackStream::setReaderInterface(&memoryReader);

	blockObserver.playback_initizalize();
}

void postprocess_block(unsigned int srcid, const char* srcname, const void* buffer, unsigned int size)
{
	if (pp_level<-1)
		return;

	pp_srcid=srcid;
	pp_srcname=srcname;

	//if (pp_level<0) return;

	//cmdio_printf(cmdio_warning, "PP %u", size);

	memoryReader.setBuffer(buffer, size);

	GrifoPlaybackStream::reset();

	for(;;)
	{
		GrifoPlaybackStream::marker_t m=GrifoPlaybackStream::nextBlock();
		if (m.value==0)
			break;
	}
}

void postprocess_print_summary()
{
	for(auto b: proc_stat.bmap)
	{
        cmdio_pinfo("%-32.32s: %u", "Postprocess", b.second.num);

		for(auto c: b.second.bmap_)
		{
			if (c.second.srcname)
				cmdio_pinfo("  %-32.32s: %u", c.first.c_str(), c.second.num);
			else
			{
				cmdio_pinfo("  @2X:%-28.28s: %u", c.second.srcid, c.first.c_str(), c.second.num);
			}
		}
	}
	if (proc_stat.sign_corrupted)
		cmdio_perror("%u corrupted signal block(s)", proc_stat.sign_corrupted);
}

