/*
 * 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"

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

	static gfd_framebuffer_t sfb;
	static gfd_framebuffer_t pfb;

	//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(&sfb, fd.dx, fd.dy, fd.stride, fd.buffer);
		gfd_fb_init(&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(&pfb, gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION, fd.stride, fd.buffer);

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


	static void addHandler(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(GcDisplayExtraInfoHandler* classHandler, GcDisplayExtraInfo::handler_t raw_handler, void* cookie)
{
	//WARNING: mutex required!
	for(int i=0; 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;
gfd_framebuffer_t GcDisplayExtraInfoImplementation::pfb;

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

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

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

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

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


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;
	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;

	gfd_fb_vprintf(&GcDisplayExtraInfoImplementation::sfb, &gfd_font256_PC_8x8, 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(&GcDisplayExtraInfoImplementation::sfb, &gfd_font256_PC_8x8, 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;
	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;

	gfd_fb_vprintf(&GcDisplayExtraInfoImplementation::sfb, &gfd_font256_F5_10x14, 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*9;
	int y=row*9;
	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;

	gfd_fb_vprintf(&GcDisplayExtraInfoImplementation::sfb, &gfd_font256_PC_8x8, 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(&GcDisplayExtraInfoImplementation::pfb, &gfd_font256_F5_10x14, 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(&GcDisplayExtraInfoImplementation::pfb, &gfd_font256_PC_8x8, 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(&GcDisplayExtraInfoImplementation::pfb, &gfd_font256_PC_8x8, 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*gfd_font256_F5_10x14.char_spacing;
	int y=row*gfd_font256_F5_10x14.height;
	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;

	gfd_fb_vprintf(&GcDisplayExtraInfoImplementation::sfb, &gfd_font256_F5_10x14, x, y, 18, fmt, args);
	va_end(args);
}

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

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






