/*
 * ghost_ipc.cpp
 *
 */


#include "ghost_ipc.h"

#include <windows.h>

#include <string.h>
#include <stdint.h>

#define SHMEM_MARKER	(0x12345678)

struct ghost_shmem_header_t
{
	uint32_t marker;
	uint32_t active;
};

union ghost_shmem_preamble_t
{
	ghost_shmem_header_t header;
	uint32_t raw[1024];
};

struct ghost_shmem_base_t
{
	ghost_shmem_preamble_t preamble;

	uint32_t pending_headers[2][1024*2];

	uint32_t payload[64];
};

class GhostShMemSegment
{
public:
	enum { MIN_SIZE=16*1024U};

	HANDLE shmem;
	ghost_shmem_base_t* shmem_vbase;

	void initialize(const char* name, unsigned int size_bytes)
	{
		static char buffer[64];

		if (name[0]=='!')
		{
			strcpy(buffer, &name[1]);
		}
		else
		{
			strcpy(buffer, "GHOST_SHMEM_");
			strcat(buffer, name);
		}

		const char* shname=buffer; //"GRIFO_EIF_EMU";
		unsigned int msize=size_bytes;

		const unsigned int min_size=MIN_SIZE;

		msize+=sizeof(ghost_shmem_preamble_t);
		if (msize<min_size)
			msize=min_size;
		else
		{
			//round to min size (must be a power of 2)
			const unsigned int min_size_mask=~(min_size-1);
			msize+=(min_size-1);
			msize&=min_size_mask;
		}

		shmem_vbase=0;

		shmem=CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, msize, shname);
		if (shmem)
		{
			shmem_vbase=reinterpret_cast<decltype(shmem_vbase)>(MapViewOfFile(shmem, FILE_MAP_ALL_ACCESS, 0, 0, msize));
		}
		if (shmem_vbase==0)
			shmem_vbase=reinterpret_cast<decltype(shmem_vbase)>(new unsigned char[msize]);
		memset(&(shmem_vbase->preamble), 0, sizeof shmem_vbase->preamble);

		shmem_vbase->preamble.header.marker=SHMEM_MARKER;
	}
};

class GhostIpcEvents
{
public:
	HANDLE evBatch;
	HANDLE evRdyIn[2];
	HANDLE evRdyOut;

	static const char* mkname(const char* owner, const char* name, const char* prefix=0)
	{
		static char buffer[1024];
		strcpy(buffer, prefix ? prefix : "GHOST_EVN_");
		strcat(buffer, owner);
		strcat(buffer, "_");
		strcat(buffer, name);
		return buffer;
	}

	void initialize(const char* owner)
	{
		evBatch=CreateEventA(0, 0, 0, mkname(owner, "BATCH"));
		evRdyIn[0]=CreateEventA(0, 0, 0, mkname(owner, "RDYIN0"));
		evRdyIn[1]=CreateEventA(0, 0, 0, mkname(owner, "RDYIN1"));
		evRdyOut=CreateEventA(0, 0, 0, mkname(owner, "RDYOUT"));
	}
};

class GhostDspIpcImplementation
{
public:
	GhostShMemSegment shMem;
	GhostIpcEvents evn;

    struct signal_input_t
    {
        enum {
            max_ch=15,
            max_samples=4096*64,
            header_size_word32=1024
        };
        typedef uint32_t sample_iq_t;

        uint32_t header[header_size_word32];
        sample_iq_t samples[max_ch][max_samples];
        sample_iq_t sampls_spare[max_samples-header_size_word32];
    };
    struct signal_input_memory_layout_t
    {
    	enum { max_buffers=2};

    	signal_input_t signal_data[max_buffers];
    };

    signal_input_memory_layout_t* hdr;

    GhostDspIpcImplementation()
	{
		shMem.initialize("DSP", sizeof(signal_input_memory_layout_t)+sizeof(*shMem.shmem_vbase));
		hdr=reinterpret_cast<signal_input_memory_layout_t*>(&shMem.shmem_vbase->payload);
		evn.initialize("DSP");

		//signal_input_t::sample_iq_t* p=reinterpret_cast<signal_input_t::sample_iq_t*>(hdr->signal_data[0].samples[1]);
		for(int i=0; i<signal_input_t::max_samples; ++i)
		{
			//p[i]=i|(i<<16);
			//hdr->signal_data[0].samples[0][i]=p[i];
			int v=i & 0x1F;
			v-=256;
			unsigned int vv=v;
			signal_input_t::sample_iq_t iq=vv|(vv<<16);
			for(int ch=0; ch<signal_input_t::max_ch; ++ch)
			{
				hdr->signal_data[0].samples[ch][i]=iq;
				hdr->signal_data[1].samples[ch][i]=iq;
			}
		}
	}

	static GhostDspIpcImplementation& instance()
	{
		 static GhostDspIpcImplementation* theInstance=new GhostDspIpcImplementation;
		 return *theInstance;
	}
};


static bool evWait(HANDLE evn, int timeout_ms=-1)
{
	if (timeout_ms<0)
		timeout_ms=INFINITE;
	DWORD res=WaitForSingleObject(evn, timeout_ms);
	if (res==WAIT_OBJECT_0)
		return true;
	if (res==WAIT_TIMEOUT)
		return false;
	//Error?
	::Sleep(10);
	return false;

}

void* GhostDspIpc::sharedInputMemory()
{
	return GhostDspIpcImplementation::instance().shMem.shmem_vbase->payload;
}

int GhostDspIpc::waitReadyInput(int timeout_ms)
{
	//bool ok=evWait(GhostDspIpcImplementation::instance().evn.evRdyIn, timeout_ms);
	if (timeout_ms<0)
		timeout_ms=INFINITE;
	DWORD res=WaitForMultipleObjects(2, GhostDspIpcImplementation::instance().evn.evRdyIn, FALSE, timeout_ms);
	if (res==WAIT_OBJECT_0)
		return 1;
	if (res==(WAIT_OBJECT_0+1))
		return 2;
	if (res==WAIT_TIMEOUT)
		return 0;
	return 0;

	//return ok ? 1 : 0;
}

bool GhostDspIpc::signalReadyOutput()
{
	SetEvent(GhostDspIpcImplementation::instance().evn.evRdyOut);
	return true;
}

static int dsp_pending_ping_pong;

bool GhostDspIpc::signalReadyInput(int ping_pong)
{
	--ping_pong;
	if (ping_pong<0)
	{
		unsigned bytes=4*1024; //sizeof h->signal_data[0].header; // ? sizeof h->signal_data[0].header : len;
		memcpy(GhostDspIpcImplementation::instance().hdr->signal_data[dsp_pending_ping_pong].header,
				GhostDspIpcImplementation::instance().shMem.shmem_vbase->pending_headers[dsp_pending_ping_pong], bytes);

		SetEvent(GhostDspIpcImplementation::instance().evn.evRdyIn[dsp_pending_ping_pong]);
		return true;
	}
	else if (ping_pong<2)
	{
		unsigned bytes=4*1024; //sizeof h->signal_data[0].header; // ? sizeof h->signal_data[0].header : len;
		memcpy(GhostDspIpcImplementation::instance().hdr->signal_data[ping_pong].header,
				GhostDspIpcImplementation::instance().shMem.shmem_vbase->pending_headers[ping_pong], bytes);

		SetEvent(GhostDspIpcImplementation::instance().evn.evRdyIn[ping_pong]);
		return true;
	}
	return false;
}

bool GhostDspIpc::waitBatch()
{
	return evWait(GhostDspIpcImplementation::instance().evn.evBatch, 1000);
}


void GhostDspIpc::writeHeader(int ping_pong, const void* data, unsigned int /*len*/)
{
	auto h=GhostDspIpcImplementation::instance().hdr;

	if (!h)
		return;

	dsp_pending_ping_pong=ping_pong;
	memcpy(GhostDspIpcImplementation::instance().shMem.shmem_vbase->pending_headers[ping_pong], data, 4096);
	//unsigned bytes=len>sizeof h->signal_data[0].header ? sizeof h->signal_data[0].header : len;
	//memcpy(GhostDspIpcImplementation::instance().hdr->signal_data[ping_pong].header, data, bytes);
}

//EIF
class GhostEifIpcImplementation
{
public:
	struct img_t
	{
		unsigned valid;
		unsigned int updates;
		unsigned int current_buffer;
		GhostEifIpc::avionics_adaptor_message_t data1553[2];
	};

	GhostShMemSegment shMem;

	volatile img_t* img;


	GhostEifIpcImplementation()
	{
		//unsigned int msize=64*1024;
		shMem.initialize("!GRIFO_EIF_EMU", 0);

		img=reinterpret_cast<volatile img_t*>(shMem.shmem_vbase);

		img->valid=0;
		img->current_buffer=0;
		img->updates=0;
	}

	void update1553(const GhostEifIpc::avionics_adaptor_message_t& d, bool only_nav=false)
	{
		if (!img)
			return;

		unsigned int next_buffer=img->current_buffer ^ 1;
		if (only_nav)
			img->valid=0x12345600;  //only NAV
		else
			img->valid=0x12345600; //0x12345678;

		//img->data1553[next_buffer]=const_cast<volatile >d;
		memcpy((void*)&img->data1553[next_buffer], &d, sizeof d);

		img->current_buffer=next_buffer;
		++img->updates;
	}

	static GhostEifIpcImplementation& instance()
	{
		 static GhostEifIpcImplementation* theInstance=new GhostEifIpcImplementation;
		 return *theInstance;
	}

};


void GhostEifIpc::update1553(const avionics_adaptor_message_t* msg)
{
	update1553(msg, false);
}

void GhostEifIpc::update1553(const avionics_adaptor_message_t* msg, bool only_nav)
{
	if (msg)
	{
		GhostEifIpcImplementation::instance().update1553(*msg, only_nav);
	}

}



