/*
 * GcDisplayExtraInfo.cpp
 *
 */


#include "GcDisplayExtraInfo.h"
#include "GcEnvAdaptor.h"

#include "gfd.h"
#include "gfd_grifo_fonts.h"
#include "gc_geometry.h"
#include "gc_color_theme.h"

#include "stdarg.h"

#ifdef __MINGW32__
#pragma GCC optimize("-O0")
#endif

class GcDisplayExtraInfoImplementation
{
public:
	enum { max_handlers=8};

	static gfd_framebuffer_t sfb_[2];
	static gfd_framebuffer_t pfb_[2];

	//typedef void (*raw_handler_t)(void);

	struct handler_info_t
	{
		GcDisplayExtraInfoHandler* classHandler;
		GcDisplayExtraInfo::handler_t raw_handler;
		void* cookie;
		bool used;
	};

	static void drawHandler(const GcEnvAdaptor::framebuffer_descriptor_t& fd)
	{
		GcDisplayExtraInfo& d=GcDisplayExtraInfo::instance();
		gfd_fb_init(d.pfb, gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION, fd.stride, fd.buffer);
		gfd_fb_init(d.sfb, gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION, gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION, fd.stride, &fd.buffer[fd.stride*gc_cfg::Y_LOGICAL_DISPLAY_OFFSET+gc_cfg::X_LOGICAL_DISPLAY_OFFSET]);

		GcDisplayExtraInfo& ed=GcDisplayExtraInfo::engInstance();

		if (gc_cfg::X_LOGICAL_DISPLAY_OFFSET<64)
		{
			ed.font_normal=&gfd_font256_PC_8x8;

			int y=2; //((gc_cfg::Y_LOGICAL_DISPLAY_OFFSET+gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION)+1);
			int x=2+(gc_cfg::X_LOGICAL_DISPLAY_OFFSET+gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION);
			if (x & 0x3) //not aligned.. iìis it reallyimportant for performance?
			{
				x=(x+3) & (~0x3);
			}

			int dx=fd.dx-x;
			int dy=fd.dy-y;

			uint8_t* b_start=&fd.buffer[fd.stride*y+x];

			gfd_fb_init(ed.pfb, dx, dy, fd.stride, b_start);
			gfd_fb_init(ed.sfb, dx, dy, fd.stride, b_start);
		}
		else
		{
			d.font_normal=&gfd_font256_F5_10x14;
			ed.font_normal=&gfd_font256_F5_10x14;

			gfd_fb_init(ed.pfb, gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION, fd.stride, fd.buffer);
			gfd_fb_init(ed.sfb, gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION, gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION, fd.stride, &fd.buffer[fd.stride*gc_cfg::Y_LOGICAL_DISPLAY_OFFSET+gc_cfg::X_LOGICAL_DISPLAY_OFFSET]);
		}

		//gfd_fb_init(&sfb, fd.dx, fd.dy, fd.stride, fd.buffer);

		int terminate=false;
		for(int i=0; (i<max_handlers) & (!terminate); ++i)
		{
			if (!handlers[i].used)
				break;
			if (handlers[i].raw_handler)
				terminate=handlers[i].raw_handler(d, ed, handlers[i].cookie);
			else if (handlers[i].classHandler)
				terminate=handlers[i].classHandler->displayExtraInfo(d, ed, handlers[i].cookie);
		}
	}


	static void addHandler(int priority, GcDisplayExtraInfoHandler* classHandler, GcDisplayExtraInfo::handler_t raw_handler, void* cookie) __attribute__((noinline));

	static handler_info_t handlers[8];
};

GcDisplayExtraInfoImplementation::handler_info_t GcDisplayExtraInfoImplementation::handlers[8];

void GcDisplayExtraInfoImplementation::addHandler(int priority, GcDisplayExtraInfoHandler* classHandler, GcDisplayExtraInfo::handler_t raw_handler, void* cookie)
{
	//WARNING: mutex required!
	for(int i=priority; i<GcDisplayExtraInfoImplementation::max_handlers; ++i)
	{
		if (GcDisplayExtraInfoImplementation::handlers[i].used)
			continue;
		if (raw_handler && (GcDisplayExtraInfoImplementation::handlers[i].raw_handler==raw_handler))
			return;
		if (classHandler && (GcDisplayExtraInfoImplementation::handlers[i].classHandler==classHandler))
			return;
		GcDisplayExtraInfoImplementation::handlers[i].used=true;
		GcDisplayExtraInfoImplementation::handlers[i].classHandler=classHandler;
		GcDisplayExtraInfoImplementation::handlers[i].raw_handler=raw_handler;
		GcDisplayExtraInfoImplementation::handlers[i].cookie=cookie;
	}

}

gfd_framebuffer_t GcDisplayExtraInfoImplementation::sfb_[2];
gfd_framebuffer_t GcDisplayExtraInfoImplementation::pfb_[2];

GcDisplayExtraInfo::GcDisplayExtraInfo(int idx)
{
	font_normal=&gfd_font256_F5_10x14;
	font_small=&gfd_font256_PC_8x8;

	sfb=&GcDisplayExtraInfoImplementation::sfb_[idx];
	pfb=&GcDisplayExtraInfoImplementation::pfb_[idx];

	GcEnvAdaptor::ggSetFinalOverlayHandler(GcDisplayExtraInfoImplementation::drawHandler);
}

GcDisplayExtraInfo& GcDisplayExtraInfo::instance()
{
	static GcDisplayExtraInfo theInstance(0);
	return theInstance;
}

GcDisplayExtraInfo& GcDisplayExtraInfo::engInstance()
{
	static GcDisplayExtraInfo theEngInstance(1);
	return theEngInstance;
}

void GcDisplayExtraInfo::addVideoFrameHandler(GcDisplayExtraInfo::handler_t handler, void* cookie)
{
	GcDisplayExtraInfoImplementation::addHandler(1, 0, handler, cookie);
}

void GcDisplayExtraInfo::addVideoFrameHandler(GcDisplayExtraInfoHandler& handler)
{
	GcDisplayExtraInfoImplementation::addHandler(0, &handler, 0, 0);
}

void GcDisplayExtraInfo::print(int x, int y, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	gfd_fb_vprintf(sfb, font_small, x, y, gc_color_theme::gc_color_dbg/*64*/, fmt, args);
	va_end(args);
}

static inline void cxy_limit(gfd_framebuffer_struct_t* fb, int&x, int& y)
{
	if (x<0)
		x=(fb->dx/*gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION*/+x)-2;
	else
		x+=2;
	if (y<0)
		y=(fb->dy/*gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION*/+y)-2;
	else
		y+=2;
}

void GcDisplayExtraInfo::print_c(int col, int row, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	int x=col*9;
	int y=row*9;

	cxy_limit(sfb, x, y);
#if 0
	if (col<0)
		x=(gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION+x)-2;
	else
		x+=2;
	if (row<0)
		y=(sfb->dy/*gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION*/+y)-2;
	else
		y+=2;
#endif

	gfd_fb_vprintf(sfb, font_small, x, y, gc_color_theme::gc_color_dbg/*64*/, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::color_print(int color, int x, int y, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	gfd_fb_vprintf(sfb, font_small, x, y, color, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::print_error_c(int col, int row, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	int x=col*9;
	int y=row*9;

	cxy_limit(sfb, x, y);
#if 0

	if (col<0)
		x=(gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION+x)-2;
	else
		x+=2;
	if (row<0)
		y=(gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION+y)-2;
	else
		y+=2;
#endif

	gfd_fb_vprintf(sfb, font_normal, x, y, gc_color_theme::gc_color_yellow/*34*/, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::color_print_c(int color, int col, int row, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	int x=col*(font_small->char_spacing);
	int y=row*(1+font_small->height);
	//int x=col*9;
	//int y=row*9;
	cxy_limit(sfb, x, y);
#if 0
	if (col<0)
		x=(gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION+x)-2;
	else
		x+=2;
	if (row<0)
		y=(gc_cfg::XY_LOGICAL_DISPLAY_DIMENSION+y)-2;
	else
		y+=2;
#endif
	gfd_fb_vprintf(sfb, font_small, x, y, color, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::phy_print(int x, int y, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	gfd_fb_vprintf(pfb, font_normal, x, y, 18, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::phy_print_small(int x, int y, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	gfd_fb_vprintf(pfb, font_small, x, y, 18, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::phy_print_small_dbg(int x, int y, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	gfd_fb_vprintf(pfb, font_small, x, y, gc_color_theme::gc_color_dbg, fmt, args);
	va_end(args);
}

void GcDisplayExtraInfo::phy_print_c(int col, int row, const char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	int x=col*(font_normal->char_spacing);
	int y=row*(1+font_normal->height);
	cxy_limit(pfb, x, y);
#if 0
	if (col<0)
		x=(gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION+x)-2;
	else
		x+=2;
	if (row<0)
		y=(gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION+y)-2;
	else
		y+=2;
#endif
	gfd_fb_vprintf(pfb, font_normal, x, y, 18, fmt, args);
	va_end(args);
}

void* GcDisplayExtraInfo::opaqueFramebuffer()
{
	return sfb; //&GcDisplayExtraInfoImplementation::sfb;
}

void* GcDisplayExtraInfo::opaquePhysicalFramebuffer()
{
	return pfb; //&GcDisplayExtraInfoImplementation::pfb;
}






