/*
 * GcEnvAdaptorMingw.cpp
 *
 *  Created on: 17/ott/2017
 *      Author: chessaa
 */

#include "rtlog.h"

#include "GcEnvAdaptor.h"

#include <string.h>

#include "SoftScanConverterDriver.h"

#include "mrk.h"

#include "vg_driver.h"

#include "gc_color_theme.h"

//#include "gfd_framebuffer.h"
#include "gfddrawingcontext.h"
#include "gfd_grifo_fonts.h"

#include "gc_geometry.h"

#ifdef __MINGW32__
static int ris_sts=0;
#else
static int ris_sts;
#endif

/*
 * Backing store are managed with a triple-buffer policy + one buffer for remote display update
 *   1. on screen
 *   2. ready
 *   3. preparing
 *   4. busy by remote display
 *
 *  Dynamic:
 *   a. GC render on buffer 3
 *   b. 3 swapped to 2. If remote display is free, buffer 2 is marked as busy and sent to remote display
 *   c. if a pending frame refresh event and 2 is produced then swap 2 to 1.
 *
 *   When swap to 3, if the source buffer is busy, swap 4 to 3 and source to 4
 *   When remote display release the buffer, mask it as free.
 *   Note that the remote display cannot have more then one buffer locked.
 *
 *  Warning: current implementation is simplified to try a lock-free version
 */


#define MAX_DX	(gc_cfg::X_MAX_PHYSICAL_DISPLAY_DIMENSION) //640
#define MAX_DY	(gc_cfg::Y_MAX_PHYSICAL_DISPLAY_DIMENSION) //512
#define STRIDE	(gc_cfg::X_MAX_PHYSICAL_DISPLAY_STRIDE)  //MAX_DX

static vg_drv_palette_t gc_palet_cache;

static bool synth_invisible;

//#define GG_TEST

#ifdef GG_TEST
int vg_drv_gen_test_pattern(void* gc_buffer, int patterid, int dx_, int dy_, int stride_);

static bool gen_test_pattern(void* p)
{
	vg_drv_gen_test_pattern(p, 0, MAX_DX, MAX_DY, STRIDE);
	return true;
}
#else
static bool gen_test_pattern(void* /*p*/)
{
	return false;
}

#endif

#ifdef __arm__
#define ATTRIBUTE_NOLOAD __attribute__((section (".bss.data_noload")))
#else
#define ATTRIBUTE_NOLOAD __attribute__((aligned(32)))
#endif


#define BUFFER_SIZE	(MAX_DY*STRIDE)
#define BUFFER_TRAILER_32B 16

struct gc_backstore_t	//WARNING: for performance reason, the buffer field should be aligned at 32 bytes boundaries
{
	//unsigned long long align;
	uint64_t dirty_area[128/2]; //a reserved area that can be used by someone else to keep some fextra frame info (for instance,by the remote display serializer
	uint32_t palette[256];
	uint8_t buffer[BUFFER_SIZE]; //MAX_DY][STRIDE];
	uint8_t trailer[BUFFER_TRAILER_32B];
};

#define BS_NUM	4
static gc_backstore_t gc_backstore[BS_NUM] ATTRIBUTE_NOLOAD;
static volatile unsigned int gc_active_store;

//#define pfunc(n_, v_)	((void)((p->color[n_]=v_), (gc_palet_cache.color[n_]=v_)))
#define pfunc(n_, v_)	((void)(vg_drv_set_gfx_color(n_, v_), (gc_palet_cache.color[n_]=v_)))

#define RGB_DBG_BROWN	0xFFCD853F
#define RGB_DBG_LIGHTBLU  0xFF3F85CD

#define RGB_DBG_CYAN	0xFF00FFFF
#define RGB_DBG_VELVET	0xFF603CB8
#define RBG_DBG_RED		0xFFFF0000
#define RBG_DBG_BLU		0xFF0000FF
#define RBG_DBG_GREEN	0xFF00FF00
#define RBG_DBG_YELLOW	0xFFFFFF00
#define RBG_DBG_GRAY	0xFF808080

#define RGB_DBG0	RGB_DBG_CYAN
#define RBG_DBG1	RBG_DBG_RED
#define RBG_DBG2	RGB_DBG_CYAN
#define RBG_DBG3	RGB_DBG_CYAN
#define RBG_DBG4	RGB_DBG_CYAN

class BackingStoreManager
{
public:
	struct pool_t {
		uint8_t* volatile onScreen;
		uint8_t* volatile ready;
		uint8_t* volatile prepare;
		uint8_t* volatile delayed;

		uint8_t* volatile released;
		uint8_t* volatile remoteBusy;

		volatile int gg_synch;
	};
	static pool_t pool;

	static void initialize()
	{
		pool.onScreen=&gc_backstore[0].buffer[0];//[0];
		pool.ready=   &gc_backstore[1].buffer[0];//[0];
		pool.prepare= &gc_backstore[2].buffer[0];//[0];
		pool.delayed= &gc_backstore[3].buffer[0];//[0];

		pool.released=0;
		pool.remoteBusy=0;

		memset(pool.onScreen, 0, sizeof gc_backstore[0].buffer);

		//vg_drv_configure(0);

		//vg_drv_setup(vg_std_stanag3350_b /*vg_std_svga*/, pool.onScreen, 800, 600, 800);
		vg_drv_setup(vg_std_default /*vg_std_svga*/, pool.onScreen, //MAX_DX, MAX_DY, STRIDE);
				gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION,
				gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION,
				gc_cfg::X_PHYSICAL_DISPLAY_STRIDE);


		//struct vg_drv_palette_t* p=vg_drv_get_gfx_palette();

        pfunc(0, 0x000000); //0xFF000000);

        pfunc(1, 0); //0x0066421F); //0xFF000000);
        pfunc(2, 0xFF00FF00); //0xFF000000);
        pfunc(3, 0xFF00B000); //0xFF000000);
        pfunc(4, 0xFF00A000); //0xFF000000);
        pfunc(5, 0xFF00D000); // FRI TGT
        pfunc(6, 0xFFFA0000); // FOE TGT
        // CAT_C1 Prio 4
        //   Hist 2
        pfunc(7, 0xFF33CCFF); // UNK TGT
        pfunc(8, 0xFF00D200); // FRI TGT
        pfunc(9, 0xFFD20000); // FOE TGT
        // CAT_C2 Prio 5
        //   Hist 3
        pfunc(10, 0xFF3399FF); // UNK TGT
        pfunc(11, 0xFF00AC00); // FRI TGT
        pfunc(12, 0xFFAC0000); // FOE TGT
        // CAT_C3 Prio 6
        //   Hist 4
        pfunc(13, 0xFF0066FF); // UNK TGT
        pfunc(14, 0xFF008600); // FRI TGT
        pfunc(15, 0xFF860000); // FOE TGT

#if 0
        pfunc(64, RGB_DBG0);
        pfunc(65, RBG_DBG1);
        pfunc(66, RBG_DBG2);
        pfunc(67, RBG_DBG3);
        pfunc(68, RBG_DBG4);
#endif
        pfunc(gc_color_theme::gc_color_dbg, RGB_DBG_CYAN);
        pfunc(gc_color_theme::gc_color_yellow, RBG_DBG_YELLOW);
        pfunc(gc_color_theme::gc_color_red, RBG_DBG_RED);
        pfunc(gc_color_theme::gc_color_green, RBG_DBG_GREEN);
        pfunc(gc_color_theme::gc_color_gray, RBG_DBG_GRAY);
        pfunc(gc_color_theme::gc_color_blue, RBG_DBG_BLU);

        for(int idx=0; idx<128; ++idx)
        {
            //unsigned int base_color=(idx*2) & 255; //idx<<4;
            //unsigned int color=base_color|(base_color<<8)|(base_color<<16);
            unsigned int color=vg_drv_color_encode_luminance(idx, idx, idx, idx); //color|0xFF000000;
            //p->color[idx+128]=color*2;
            pfunc(idx+128, color*2);
        }

		vg_drv_video_enable(1);
	}

	static void displaySynch()
	{
		pool.gg_synch=1; //Atomic
	}
	static int displayGrabageCollect()
	{
		//WARNING: should be atomic!!
		int tmp=pool.gg_synch;
		pool.gg_synch=0;
		return tmp;
	}

	static uint8_t* popFree()
	{
		return pool.prepare;
	}

	static uint8_t* pushPrepeared()
	{
		//TODO: to refine with the real display synch
		// Current assumption to try to be interrupts and lock free:
		//   1. physical display refresh time <= (frame_generation_time*2), that is
		//   2. The Graphic generator automatically latches the frame base address at frame begins, so I can update it when I want and keep display coherent
		// Thus:
		//   1. When a new frame is generated it can be immediately committed to the GG
		//   2. the previous committed buffer shall be reused only after one new buffer generation, that is not used immediately bud at the next round

		//commitToDisplay(pool.prepare);

		uint8_t* tmp=pool.onScreen;
		pool.onScreen=pool.prepare;
		pool.prepare=pool.ready;
		pool.ready=pool.delayed;
		pool.delayed=tmp;

		return pool.onScreen; //the buffer for the remote display, delay by one frame since the remote display can overwrite the data
	}

	static void remoteRelease(const void* p)
	{
		if (pool.remoteBusy==p)
		{
			pool.released=(uint8_t*)const_cast<void*>(p);
			pool.remoteBusy=0;
		}
	}
	static void remoteLock(uint8_t* p)
	{
		pool.remoteBusy=p;
	}

	static void commitToDisplay(uint8_t* p, unsigned int size=0)
	{
		rtlog_printf("GG", rtlog_debug, "Commit frame buffer: 0x%8.8X*%u", (unsigned int)p, size);
		vg_drv_set_framebuffer(p, size);
	}
};

BackingStoreManager::pool_t BackingStoreManager::pool ={
		&gc_backstore[0].buffer[0],//[0],
		&gc_backstore[1].buffer[0],//[0],
		&gc_backstore[2].buffer[0],//[0],
		&gc_backstore[3].buffer[0],//[0],
		0,

		0,0

};

static void clear_background_handler(const GcEnvAdaptor::framebuffer_descriptor_t& fb)
{
	memset(fb.buffer, 0, fb.dy*fb.stride);
}

static void soft_sc_background_handler(const GcEnvAdaptor::framebuffer_descriptor_t& fb)
{
	SoftScanConverterDriver& sc=SoftScanConverterObject::instance();
	sc.generate(fb.buffer);
}

static void commit_dummy_handler(const GcEnvAdaptor::framebuffer_descriptor_t& fb)
{
	BackingStoreManager::remoteLock(fb.buffer);
	BackingStoreManager::remoteRelease(fb.buffer);
}

static void commit_remote_display_handler(const GcEnvAdaptor::framebuffer_descriptor_t& fb)
{
	extern void gc_remote_update(const uint8_t* src, unsigned int rows, unsigned int cols, unsigned int pitch, uint32_t* new_palette=0);
	extern void gc_remote_set_memory_release_handler(void (*handler)(const void*));
	gc_remote_set_memory_release_handler(BackingStoreManager::remoteRelease);
	BackingStoreManager::remoteLock(fb.buffer);
	gc_remote_update(fb.buffer, fb.dy, fb.dx, fb.stride, gc_palet_cache.color); //vg_drv_get_gfx_palette()->color);
}

static GcEnvAdaptor::framebuffer_handler_t background_prepare_handler=soft_sc_background_handler;
static GcEnvAdaptor::framebuffer_handler_t commint_frame_handler;
static GcEnvAdaptor::framebuffer_handler_t final_overlay_handler;

//TODO: remove direct link to geometry and pass some more info to setup!
#include "gc_geometry.h"

unsigned int GcEnvAdaptor::ggGeometrySetup(unsigned int pdx, unsigned int pdy, unsigned int ldx, unsigned int ldy) //return the pitch
{
	if ((pdx>MAX_DX) || (pdy>MAX_DY))
		return 0;
#ifdef GC_GEOMETRY_VGA
	(void)ldx;
	vg_drv_set_graphic_geometry(pdx, ldy);
#else
	vg_drv_set_graphic_geometry(ldx, ldy);
#endif

	BackingStoreManager::initialize();
	ggSoftScanConverterEnable(true);
	ggRemoteDisplayEnable(true);

	return gc_cfg::X_PHYSICAL_DISPLAY_STRIDE; //STRIDE;
}

unsigned int GcEnvAdaptor::ggGeometryResize(unsigned int pdx, unsigned int pdy, unsigned int ldx, unsigned int ldy) //return the pitch
{
	if ((pdx>MAX_DX) || (pdy>MAX_DY))
		return 0;
#ifdef GC_GEOMETRY_VGA
	(void)ldx;
	vg_drv_set_graphic_geometry(pdx, ldy);
#else
	vg_drv_set_graphic_geometry(ldx, ldy);
#endif

	for(int i=0; i<BS_NUM; ++i)
		memset(gc_backstore[i].buffer, 0, sizeof gc_backstore[0].buffer);

	SoftScanConverterDriver& sc=SoftScanConverterObject::instance();
	sc.setGeometry(gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::X_PHYSICAL_DISPLAY_STRIDE); //MAX_DX, MAX_DY, STRIDE);

	return gc_cfg::X_PHYSICAL_DISPLAY_STRIDE; //STRIDE;
}


void* GcEnvAdaptor::ggGetBackingStore()
{
	//void* p=gc_backstore[gc_active_store].buffer;
	//memset(p, 0, 512*512);
	//SoftScanConverter& sc=SoftScanConverterObject::instance();
	//sc.generate(p);
	//memcpy(p, sc.buffer().buffer, 512*512);
	uint8_t* p=BackingStoreManager::popFree();
	//const framebuffer_descriptor_t fb={&gc_backstore[gc_active_store].buffer[0][0], 512, 512, 512, &gc_backstore[gc_active_store].buffer[0][0]};
	//const framebuffer_descriptor_t fb={p, MAX_DX, MAX_DY, STRIDE, p};
	const framebuffer_descriptor_t fb={p, (unsigned int)gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, (unsigned int)gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION, (unsigned int)gc_cfg::X_PHYSICAL_DISPLAY_STRIDE, p};
	background_prepare_handler(fb);

	return p;
}

void GcEnvAdaptor::ggPrepareSurface()
{
	if (ris_sts)
	{
		GrifoDrawingContext dc;
		dc.displayDc().setFont(gfd_font256_F5_10x14);
		dc.displayDc().setColor(gc_color_theme::gc_color_yellow);
		int sx=10*10;
		int x=dc.displayDc().width();
		int y=dc.displayDc().height()-16;
		dc.displayDc().printf(x-sx, 4, "SIMULATOR");
		dc.displayDc().printf(4, 4, "SIMULATOR");
		dc.displayDc().printf(x-sx, y, "SIMULATOR");
		dc.displayDc().printf(4, y, "SIMULATOR");
	}
}

void* GcEnvAdaptor::ggGetDefaultBackingStore()
{
	return gc_backstore[0].buffer;
}

void GcEnvAdaptor::ggSetBackgroundPrepareHandler(framebuffer_handler_t handler)
{
	background_prepare_handler=handler;
}

void GcEnvAdaptor::ggSetFrameCommitHandler(framebuffer_handler_t handler)
{
	commint_frame_handler=handler;
}

void GcEnvAdaptor::ggSoftScanConverterEnable(bool enable)
{
	SoftScanConverterDriver& sc=SoftScanConverterObject::instance();
	sc.setGeometry(gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::X_PHYSICAL_DISPLAY_STRIDE); //MAX_DX, MAX_DY, STRIDE);
	background_prepare_handler=enable ? soft_sc_background_handler : clear_background_handler;
}

void GcEnvAdaptor::ggRemoteDisplayEnable(bool enable)
{
	commint_frame_handler=enable ?  commit_remote_display_handler : commit_dummy_handler;
}

void GcEnvAdaptor::ggSetFinalOverlayHandler(GcEnvAdaptor::framebuffer_handler_t handler)
{
	final_overlay_handler=handler;
}

void GcEnvAdaptor::ggCommitBackingStore(unsigned int rows, unsigned int cols, unsigned int pitch)
{
	//rtlog_printf("GCENV", rtlog_debug, "GC Display Commit %u %p", gc_active_store, gc_backstore[gc_active_store].buffer[0]);

	//gc_remote_update(&gc_backstore[gc_active_store].buffer[0][0], rows, cols, pitch);
	uint8_t* p=BackingStoreManager::pushPrepeared();
	if (p)
	{
        //const framebuffer_descriptor_t fb={p, cols, rows, pitch, p};
        if (final_overlay_handler)
        {
        	const framebuffer_descriptor_t fb={p, cols, rows, pitch, p};
        	final_overlay_handler(fb);
        }

        gen_test_pattern(p);
        BackingStoreManager::commitToDisplay(p, pitch*rows);

        //commint_frame_handler(fb);
	}
	if (BackingStoreManager::pool.delayed)
	{
        const framebuffer_descriptor_t fb={BackingStoreManager::pool.delayed, cols, rows, pitch, p};
        commint_frame_handler(fb);
	}
	//rtlog_printf("GCENV", rtlog_debug, "GC Display Commit %p", p);

	//p=BackingStoreManager::pool.delayed;
#if 0
	if (p)
		{
		//gen_test_pattern(p);
		//const framebuffer_descriptor_t fb={&gc_backstore[gc_active_store].buffer[0][0], cols, rows, pitch, &gc_backstore[gc_active_store].buffer[0][0]};
                const framebuffer_descriptor_t fb={p, cols, rows, pitch, p};
                //const framebuffer_descriptor_t fb={BackingStoreManager::pool.onScreen, cols, rows, pitch, p};
        commint_frame_handler(fb);
	}
#endif
#if __TI_COMPILER_VERSION__
	gc_active_store^=1; //race condition!!! - don't care, this will never be used on the DSP
#else
	__sync_fetch_and_xor(&gc_active_store, 1); 	//gc_active_store^=1;
#endif
}


void GcEnvAdaptor::ggUpdate(const uint8_t* /*src*/, unsigned int /*rows*/, unsigned int /*cols*/, unsigned int /*pitch*/)
{
}

void GcEnvAdaptor::ggRemoteRelease(const void* p)
{
	BackingStoreManager::remoteRelease(p);
}

bool GcEnvAdaptor::ggIsVisible()
{
	return !synth_invisible;
}

void GcEnvAdaptor::ggSetVisible(bool visible)
{
	synth_invisible=!visible;
}

void GcEnvAdaptor::ggSetColor(int index, uint32_t rgba)
{
	gc_palet_cache.color[index]=rgba;
	//vg_drv_get_gfx_palette()->color[index]=rgba;
	vg_drv_set_gfx_color(index, rgba);
}


void GcEnvAdaptor::ggSetColorSC(int index, uint32_t rgba)
{
	if ((index<0) || (index>255))
		return;

	//vg_drv_get_sc_palette()->color[index]=rgba;
	//index=(index>>1)+128;
	//gc_palet_cache.color[index]=rgba;
	vg_drv_set_sc_color(index, rgba);

	//index=index=(index>>1)+128;
	index=gc_color_theme::sc_color_index_convert(index);
	if (index==gc_color_theme::sc_color_base)
		rgba=0;
	gc_palet_cache.color[index]=rgba;
	vg_drv_set_gfx_color(index, rgba);  //vg_drv_get_gfx_palette()->color[index]=rgba;
}

void GcEnvAdaptor::ggCommintPalet()
{
}

void GcEnvAdaptor::ggCommitPaletSC()
{
}


static GcEnvAdaptor::post_operative_handler_t ggPostOperativeHandler;
static void* ggPostOperativeHandleCookie;

void GcEnvAdaptor::ggSetPostOperativeHandler(post_operative_handler_t h, void* cookie)
{
	ggPostOperativeHandleCookie=cookie;
	ggPostOperativeHandler=h;
}

extern void GrifoMfdSerializer(const uint32_t* palette, const uint8_t* data, unsigned int dx, unsigned int dy, unsigned int stride);

void GcEnvAdaptor::ggSetRisEnabed(bool enable)
{
	if ((enable) && (ris_sts<1))
		ris_sts=1;
}

void GcEnvAdaptor::ggSetRisActivated(bool active)
{
	if ((active) && (ris_sts<2))
		ris_sts=2;
}

void GcEnvAdaptor::ggUpdateOpeative(const uint8_t* src, unsigned int rows, unsigned int cols, unsigned int pitch)
{
#if 0
	if (ris_sts)
	{
		GrifoDrawingContext dc;
		dc.displayDc().setFont(gfd_font256_F5_10x14);
		dc.displayDc().setColor(gc_color_theme::gc_color_yellow);
		int x=dc.displayDc().width()-10*10;
		dc.displayDc().printf(x, 4, "SIMULATOR");
	}
#endif
	GrifoMfdSerializer(gc_palet_cache.color, src, rows, cols, pitch);
	if (ggPostOperativeHandler)
		ggPostOperativeHandler(ggPostOperativeHandleCookie, src, rows, cols, pitch);
}

