/*
 * grifo_version.cpp
 *
 */



#include "grifo_version.h"

#include <string.h>
#include "bsk_sprintf.h"


enum {
	gv_fpga_offset=0x00780,
	gv_fpga_info_address=0x44000780,
	gv_zynq_pl_info_address=0x500E0780
};

struct grifo_version_info_t
{
	enum { k_max_items=64};
	struct item_t
	{
		unsigned int rawId;
		char lru[16];
		char sru[16];
		char sub[16];
		char id[32];
		char gap[4];
		char ver[64];
		char ver1[64];
		char ver2[64];
		char spare[8];
		char fullinfo[64];
	};
	item_t items[k_max_items];

	bool txt_rebuild;
	bool mini_rebuild;
	unsigned int txtSize;
	char txBuffer[1024*8];
	unsigned int trailing_gap;
};


static grifo_version_info_t grifo_version;

void GrifoVersion::addItem(const char* lru, const char* sru, const char* sub, unsigned int majorId, unsigned int minorId, unsigned sliceId, const char* itemId, const char* itemVersion, const char* itemBuild, const char* itemChecksum, const char* fullinfo)
{
	unsigned int rawId=(majorId<<16)|(minorId<<8)|(sliceId);
	for(int i=0; i<grifo_version_info_t::k_max_items; ++i)
	{
		if ((grifo_version.items[i].rawId==rawId) || (grifo_version.items[i].rawId==0))
		{
			grifo_version.items[i].rawId=rawId;
			if (lru)
				strncpy(grifo_version.items[i].lru, lru, (sizeof grifo_version.items[i].lru)-1);
			if (sru)
				strncpy(grifo_version.items[i].sru, sru, (sizeof grifo_version.items[i].sru)-1);
			if (sub)
				strncpy(grifo_version.items[i].sub, sub, (sizeof grifo_version.items[i].sub)-1);
			if (itemId)
				strncpy(grifo_version.items[i].id, itemId, (sizeof grifo_version.items[i].id)-1);
			if (itemVersion)
				strncpy(grifo_version.items[i].ver, itemVersion, (sizeof grifo_version.items[i].ver)-1);
			if (itemBuild)
				strncpy(grifo_version.items[i].ver1, itemBuild, (sizeof grifo_version.items[i].ver1)-1);
			if (itemChecksum)
				strncpy(grifo_version.items[i].ver2, itemChecksum, (sizeof grifo_version.items[i].ver2)-1);
			if (fullinfo)
				strncpy(grifo_version.items[i].fullinfo, fullinfo, (sizeof grifo_version.items[i].fullinfo)-1);

			grifo_version.txt_rebuild=true;
			grifo_version.mini_rebuild=true;
			break;
		}
	}

}

#if 0
void GrifoVersion::addItemRawId(unsigned int rawId, const char* itemId, const char* itemVersion, const char* itemBuild, const char* itemChecksum)
{
	for(int i=0; i<grifo_version_info_t::k_max_items; ++i)
	{
		if ((grifo_version.items[i].rawId==rawId) || (grifo_version.items[i].rawId==0))
		{
			grifo_version.items[i].rawId=rawId;
			strncpy(grifo_version.items[i].id, itemId, (sizeof grifo_version.items[i].id)-1);
			strncpy(grifo_version.items[i].ver, itemVersion, (sizeof grifo_version.items[i].id)-1);
			strncpy(grifo_version.items[i].ver1, itemBuild, (sizeof grifo_version.items[i].id)-1);
			strncpy(grifo_version.items[i].ver2, itemChecksum, (sizeof grifo_version.items[i].id)-1);
			grifo_version.txt_rebuild=true;
			break;
		}
	}
}
#endif


const char* GrifoVersion::getFormattedVersionInfo(unsigned int& size)
{
	if (grifo_version.txt_rebuild)
	{
		bsk_snprint_accumulator_t acc;
		bsk_snprintf_accumulator_init(&acc, grifo_version.txBuffer, sizeof grifo_version.txBuffer);
		int s=0;
		for(int i=0; i<grifo_version_info_t::k_max_items; ++i)
		{
			if (grifo_version.items[i].rawId==0)
				break;
			s=bsk_snprintf_accumulate(&acc, "0x%8.8X,%s,%s,%s,%x:%u:%u,%s,%s,%s,%s,%s\n",
					grifo_version.items[i].rawId,
					grifo_version.items[i].lru, grifo_version.items[i].sru,grifo_version.items[i].sub,
					(grifo_version.items[i].rawId>>16) & 0x0FF,
					(grifo_version.items[i].rawId>>8) & 0x0FF,
					(grifo_version.items[i].rawId) & 0x0FF,
					grifo_version.items[i].id, grifo_version.items[i].ver,grifo_version.items[i].ver1,grifo_version.items[i].ver2,
					grifo_version.items[i].fullinfo);
		}
		grifo_version.txtSize=s;

		grifo_version.txt_rebuild=false;
	}

	size=grifo_version.txtSize;
	return grifo_version.txBuffer;
}

void GrifoVersion::addItemUc(const char* lru, const char* sru, const char* sub, unsigned int majorId, unsigned int minorId, unsigned sliceId, const fw_info_record_t::vinfo_t& vinfo)
{
    struct {
    	char str[16];
    } tmp1, tmp2, tmp3;

    const char* p=(const char*)vinfo.id;
    for(unsigned int i=0; i<8; ++i)
    	tmp1.str[i]=p[i];
    tmp1.str[8]=0;

    p=(const char*)vinfo.rel.w16;
    for(unsigned int i=0; i<sizeof(vinfo.rel); ++i)
    	tmp2.str[i]=p[i];
    tmp2.str[sizeof(vinfo.rel)]=0;

    p=(const char*)&vinfo.build;
    for(unsigned int i=0; i<sizeof(vinfo.build); ++i)
    	tmp3.str[i]=p[i];
    tmp3.str[sizeof(vinfo.build)]=0;

    addItem(lru, sru, sub==0 ? "uC" : sub, majorId, minorId, sliceId, tmp1.str, tmp2.str, tmp3.str);
}

void GrifoVersion::addItemFpga(const char* lru, const char* sru, const char* sub, unsigned int majorId, unsigned int minorId, unsigned sliceId, const fw_info_record_t::vinfo_t& vinfo)
{
    struct {
    	char str[16];
    } tmp1, tmp2, tmp3;

    const char* p=(const char*)vinfo.id;
    for(int i=0; i<12; ++i)
    	tmp1.str[i]=p[i];
    tmp1.str[12]=0;

    unsigned int pl_ver_reg=vinfo.build;
    for(int i=0; i<8; ++i)
    {
    	unsigned int tmp=pl_ver_reg>>28;
    	tmp2.str[i]="0123456789ABCDEF"[tmp];
    	pl_ver_reg<<=4;
    }
    tmp2.str[8]=0;

    /*
    p=(const char*)vinfo.fw_build;
    for(int i=0; i<4; ++i)
    	tmp3.str[i]=p[i];
    tmp3.str[4]=0;
    */
    tmp3.str[0]=0;

    addItem(lru, sru, sub==0 ? "FPGA" : sub, majorId, minorId, sliceId, tmp1.str, tmp2.str, tmp3.str);
}

static bool gver_pl_sim;


//#define PL_VER_OLD_FMT

void GrifoVersion::addItemPl(const char* lru, const char* sru, const char* sub, unsigned int majorId, unsigned int minorId, unsigned sliceId, const fw_info_record_t::vinfo_t& vinfo)
{
    struct {
    	char str[16];
    } tmp1, tmp2, tmp3;

    const char* p=(const char*)vinfo.id;

    if (vinfo.build<=2)
    {
        auto cvstr = [](char* p, uint32_t v)
    	{
        	p[3]=v & 0xFF;
        	p[2]=(v>>8) & 0xFF;
        	p[1]=(v>>16) & 0xFF;
        	p[0]=(v>>24) & 0xFF;
    	};

        cvstr(&tmp1.str[0], vinfo.id[0]);
        cvstr(&tmp1.str[4], vinfo.id[1]);
        cvstr(&tmp1.str[8], vinfo.rel.w32);
        tmp1.str[12]=0;
    }
    else
    {
    	for(int i=0; i<12; ++i)
    		tmp1.str[i]=p[i];
    	tmp1.str[12]=0;
    }

    p=(const char*)&vinfo.build;
    for(int i=0; i<4; ++i)
    {
    	tmp2.str[7-i*2]="0123456709ABCDEF"[p[i] & 0x0F];
    	tmp2.str[7-(1+i*2)]="0123456709ABCDEF"[(p[i]>>4) & 0x0F];
    }
    tmp2.str[8]=0;

    uint32_t pl_ver_reg=0xDEB010FF;

    if (!gver_pl_sim)
    	pl_ver_reg=*(volatile uint32_t*)(0x50080000);

    for(int i=0; i<8; ++i)
    {
    	unsigned int tmp=pl_ver_reg>>28;
    	tmp3.str[i]="0123456709ABCDEF"[tmp];
    	pl_ver_reg<<=4;
    }
    tmp3.str[8]=0;

    addItem(lru, sru, sub==0 ? "PL" : sub, majorId, minorId, sliceId, tmp1.str, tmp2.str, tmp3.str);
}

static GrifoVersion::fw_info_record_t::fpga_info_t fpga_info;

void GrifoVersion::addEmbeddedFwItem(const char* lru, const char* sru, unsigned int majorId, unsigned int minorId, const void* address_override)
{
	static const uint32_t sim_info[64]=
	{
			 /*500E0780:*/ 0x30213153 ,0x33323732 ,0x32303130 ,0x39363234,
			 /*500E0790:*/ 0x00000016 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07A0:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07B0:*/ 0x00003F1F ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07C0:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07D0:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07E0:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E07F0:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000,
			 /*500E0800:*/ 0x20213338 ,0x34333539 ,0x2D303031 ,0x00000002,
			 /*500E0810:*/ 0x00000000 ,0x00000000 ,0x00000000 ,0x00000000
	};

#ifdef __MINGW32__
	address_override=&sim_info;
	gver_pl_sim=true;
#else
	if (address_override==0)
		address_override=reinterpret_cast<const void*>(gv_zynq_pl_info_address);
	else if (address_override==(const void*)0xFFFFFFFFU)
	{
		address_override=&sim_info;
		gver_pl_sim=true;
	}
#endif

	const fw_info_record_t::fpga_info_t* info=reinterpret_cast<const fw_info_record_t::fpga_info_t*>(address_override);
	fpga_info=*info;
	addItemPl(lru, sru, "ZynQ/PL", majorId, minorId, 1, fpga_info.fw_info.vinfo);
	addItemUc(lru, sru, "ZynQ/uC", majorId, minorId, 2, fpga_info.uc_info.vinfo);
}


bool GrifoVersion::getMinimalVersionInfo(unsigned int vid, GrifoVersion::minimal_info_t* info)
{
	static bool already_build;
	static minimal_info_t minfo;

	if ((!already_build) || (grifo_version.mini_rebuild))
	{
		already_build=true;
		grifo_version.mini_rebuild=false;

		for(int i=0; i<grifo_version_info_t::k_max_items; ++i)
		{
			unsigned int rawid=grifo_version.items[i].rawId;

			if ((rawid & 0xFF000000)!=0x02000000)  //REP?
				continue;
			unsigned int raw_vid=(rawid>>16U) & 0xFF;
			if (raw_vid!=vid)
				continue;
			unsigned int sub=(rawid>>8) & 0x0FF;
			unsigned int slice=rawid & 0x0FF;

			if ((sub==0x02) || (sub==0x01))
			{
				if (slice==0)
				{
					memcpy(minfo.ver, grifo_version.items[i].ver1, sizeof minfo.ver);
				}
			}
			else if (sub==0)
			{
				if (slice==0)
					memcpy(minfo.fpga[0], grifo_version.items[i].ver, sizeof minfo.fpga[0]);
				else if (slice==1)
					memcpy(minfo.fpga[1], grifo_version.items[i].ver, sizeof minfo.fpga[1]);
			}
		}
		*info=minfo;
		return true;
	}
	return false;
}
