/* Standards */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* uC3 */
#include "kernel.h"

#include "vram/vrammgr.h"
#include "bmu/bmumgr.h"
#include "dsp/dspmgr.h"
#include "timr/timrmgr.h"
#include "sys/spcprm.h"
#include "sys/sscprm.h"

/* GFX */
#include "gmalloc.h"
#include "EGL/eglext_AG903.h"
#include "EGL/egl.h"
#include "VG/openvg.h"
#include "VG/vgext_AG903.h"
#include "vxlib.h"
#include "VG/vgu.h"

#include "mtx_gfx.h"

/* ライブラリハンドラ */
extern AG903_DSPMgrHandle	*MTX_dsp_handle[2];
extern AG903_TIMRMgrHandle	*MTX_tim_handle;
extern AG903_BMUMgrHandle	*MTX_bmu_handle[4];


bool gFlagGFXDone = 0;
bool gFlagGFXFlip = 0;
ER_ID gID_MPL_GFX = 0;

uint32_t gGFX_cnt = 0;
VGfloat gBaseMatrix[9];

uint8_t *gGFXMemAddress = NULL;

VXDisplay		*vx_display;
VXWindow		vx_window;
EGLDisplay		egl_display;
EGLConfig		egl_config;
EGLContext		egl_context;
EGLSurface		egl_surface;
EGLint config_attr[] =
{
	EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
	EGL_BUFFER_SIZE, 24,
	EGL_RED_SIZE,	8,
	EGL_GREEN_SIZE,	8,
	EGL_BLUE_SIZE,	8,
	EGL_ALPHA_SIZE,	0,
	EGL_NONE
};

EGLint surface_attr[] =
{
	EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
	EGL_ALPHA_FORMAT, EGL_ALPHA_FORMAT_NONPRE,
	EGL_COLORSPACE, EGL_COLORSPACE_sRGB,
	EGL_NONE
};

VGint yuvconv =	VG_EXT_YUV2RGB_Y1_B3	|
				VG_EXT_YUV2RGB_V_B2		|
				VG_EXT_YUV2RGB_Y0_B1	|
				VG_EXT_YUV2RGB_U_B0		|
				VG_EXT_RGB2YUV_B3_Y1	|
				VG_EXT_RGB2YUV_B2_V		|
				VG_EXT_RGB2YUV_B1_Y0	|
				VG_EXT_RGB2YUV_B0_U		|
				VG_EXT_YUV_YPM;
VGint yuvcoef[9] ={
	0x12a, 0x000, 0x199,
	0x12a, 0x79c, 0x730,
	0x12a, 0x204, 0x000
};

VGPaint paint;
VGImage image;
uint8_t *image_rawdata;
VGfloat ClearColor[4]  = {0.2F, 0.2F, 0.2F, 1.0F};


void UpdateWindowAddr(int win_id, uintptr_t addr)
{
	AG903_DSPMgrWinAttribute *dsp_attr;
	AG903_DSPMgrGetAttribute(MTX_dsp_handle[0], win_id, &dsp_attr);
	dsp_attr->framebuffer_base = (uint32_t)addr;
	if (dsp_attr->conf.valid == false)
		dsp_attr->conf.valid = true;
	AG903_DSPMgrSetAttribute(MTX_dsp_handle[0], win_id);
}

void GfxSwapBuffer(void)
{
	EGLint addr;

	eglSwapBuffers(egl_display, egl_surface);
	eglQuerySurface(egl_display, egl_surface, EGL_FRONT_BUFFER_ADDRESS_EXT, &addr);
	UpdateWindowAddr(0, GM2M(addr));
}


int32_t gfx_init(void)
{
	EGLint num_config;
	AG903_VRAMMgrMplPrm	mplprm;

	uint32_t ram_base = AG903_SAMPLE_VRAM_ADDR+(AG903_SAMPLE_VRAM_SIZE-MTX_GFX_HEAP_SIZE-0x100000);

	if(0 != gID_MPL_GFX) {
		return -AG903_EFAULT;	/* 初期化済み */
	}

	mplprm.memtype = AG903_VRAM_STRONGLY_ORDERED;
	mplprm.mplatr  = AG903_VRAM_ATRFIFO;
	mplprm.mplsz   = MTX_GFX_HEAP_SIZE;
	mplprm.mpl     = (void *)ram_base;

	gID_MPL_GFX = AG903_VRAMMgrCreateMpl(&mplprm);

	if (gID_MPL_GFX <= 0) {
		gID_MPL_GFX = 0;
		return -AG903_ENOMEM;
	}

	{
		AG903_VRAMMgrMplStat mplsta;
		AG903_VRAMMgrMrefer(gID_MPL_GFX, &mplsta);
		MTX_Printf("[Memory] GFX memory : %u [Byte]\r\n", mplsta.fmplsz);
	}

	gGFXMemAddress = (uint8_t *)AG903_VRAMMgrMallocAlign(gID_MPL_GFX, 4096, MTX_GFX_HEAP_SIZE-0x100000);
	if(gGFXMemAddress == NULL) {
		MTX_ErrPrintf("ERROR: Memory allocate failed.\r\n");
		return -AG903_ENOMEM;
	}

	gmInitialize(M2GM(gGFXMemAddress), MTX_GFX_HEAP_SIZE);

	eglBindAPI(EGL_OPENVG_API);

	vx_display = VXOpenDisplay(0);
	vx_window = VXCreateWindow(vx_display, 0, 0, MTX_GFX_CANBUS_WIDTH, MTX_GFX_CANBUS_HEIGHT, 16);
	egl_display = eglGetDisplay(vx_display);
	eglInitialize(egl_display, NULL, NULL);

	eglChooseConfig(egl_display, config_attr, NULL, 1, &num_config);
	if(num_config < 1)
	{
		MTX_ErrPrintf("ERROR: eglChooseConfig() not returned any configs.\r\n",
				  (int32_t)num_config);
		return -AG903_EINVAL;
	}

	eglChooseConfig(egl_display, config_attr, &egl_config, 1, &num_config);

	egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, NULL);
	egl_surface = eglCreateWindowSurface(egl_display, egl_config, vx_window, surface_attr);
	eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);

	/* 描画サーフェスのクリアカラーを指定 */
	vgSetfv(VG_CLEAR_COLOR, 4, ClearColor);

	/* 描画サーフェス全体をクリア */
	vgClear(0, 0, MTX_GFX_CANBUS_WIDTH, MTX_GFX_CANBUS_HEIGHT);

	/* バッファスワップ */
	GfxSwapBuffer();

	/* 描画サーフェス全体をクリア */
	vgClear(0, 0, MTX_GFX_CANBUS_WIDTH, MTX_GFX_CANBUS_HEIGHT);

	/* 現在のマトリクスをIMAGE_USER_TO_SURFACEに設定 */
	vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);

	GfxSwapBuffer();

	uint32_t tex = (uint32_t)1<<31 | 1<<29; /* BMU0 */

	mpsContext *mpsctx = MPS_getContext(0);
	int32_t stream_width	= mpsctx->info.width;
	int32_t stream_height	= mpsctx->info.height;

	image = vgCreateDirectImageEXT(VG_EXT_YUV422,
								   stream_width,
								   stream_height,
								   VG_IMAGE_QUALITY_NONANTIALIASED,
								   (void *)tex,
								   stream_width*2);

	/* YUV->RGB変換係数の設定 */
	vgSetiEXT(VG_EXT_YUV_RGB_CONVERSION, yuvconv);
	vgSetivEXT(VG_EXT_YUV_RGB_COEFFICIENT, 9, yuvcoef);

	paint = vgCreatePaint();
	vgSetPaint(paint, VG_FILL_PATH);

	sys_memset(gBaseMatrix, 0, 9);
	gBaseMatrix[0] = gBaseMatrix[4] = gBaseMatrix[8] = 1.0;

	vgLoadIdentity();
	vgTranslate(MTX_GFX_CANBUS_WIDTH/2, MTX_GFX_CANBUS_HEIGHT/2);
	vgScale(MTX_GFX_CANBUS_WIDTH/(float)stream_width*0.8f,
			MTX_GFX_CANBUS_HEIGHT/(float)stream_height*-0.8f); // V-Flip
	vgTranslate(stream_width/-2, stream_height/-2);
	vgFlush();
	vgGetMatrix(gBaseMatrix);

	return AG903_ENONE;
}




void gfx_run(void)
{
	VGfloat matrix[9];

	typedef struct _VGvector {
		VGfloat x;
		VGfloat y;
	}VGvector;

	VGint imageWidth	= vgGetParameteri(image, VG_IMAGE_WIDTH);
	VGint imageHeight	= vgGetParameteri(image, VG_IMAGE_HEIGHT);

	double angle = 3.141592*(gGFX_cnt % 720)/180.0/2.0; // horizontal angle

	VGfloat ratio = 0.333;
	VGfloat X = cos(angle) * imageWidth;
	VGfloat Y = sin(angle) * imageHeight * 0.5 * ratio;
	VGfloat R = (imageWidth - X) * 0.5;

	VGvector src[4] = { { 0.0        ,  imageHeight },
                        { imageWidth ,  imageHeight },
                        { imageWidth ,          0.0 },
                        { 0.0        ,          0.0 } };

    VGvector dst[4] = { { R     ,  imageHeight + Y },
                        { R + X ,  imageHeight - Y },
                        { R + X ,                Y },
                        { R     ,           -1 * Y } };

	vguComputeWarpQuadToQuad(dst[0].x, dst[0].y,
							 dst[1].x, dst[1].y,
							 dst[2].x, dst[2].y,
							 dst[3].x, dst[3].y,
							 src[0].x, src[0].y,
							 src[1].x, src[1].y,
							 src[2].x, src[2].y,
							 src[3].x, src[3].y,
							 matrix);

	vgLoadMatrix(gBaseMatrix);

	vgMultMatrix(matrix); // Rotate Y-Axis
	vgClear(0, 0, MTX_GFX_CANBUS_WIDTH, MTX_GFX_CANBUS_HEIGHT);
	vgDrawImage(image);

	vgFinish();
	GfxSwapBuffer();
	gGFX_cnt++;
}


void gfx_final(void)
{
	vgDestroyImage(image);

	eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
	eglDestroySurface(egl_display, egl_surface);
	eglDestroyContext(egl_display, egl_context);
	eglTerminate(egl_display);
	VXDestroyWindow(egl_display, vx_window);
	VXCloseDisplay(vx_display);

	gmTerminate();
	AG903_VRAMMgrFree(gID_MPL_GFX, gGFXMemAddress);
	AG903_VRAMMgrDeleteMpl(gID_MPL_GFX);
	gID_MPL_GFX = 0;
}

void *malloc_ovg(size_t size)
{
	return AG903_VRAMMgrMalloc(gID_MPL_GFX, size);
}

void free_ovg(void *ptr)
{
	AG903_VRAMMgrFree(gID_MPL_GFX, ptr);
}

void *realloc_ovg(void *ptr, size_t size)
{
	void* get;

	get = AG903_VRAMMgrMalloc(gID_MPL_GFX, size);
	if(NULL != get) {
		sys_memcpy(get, ptr, size);
		AG903_VRAMMgrFree(gID_MPL_GFX, ptr);
	}

	return get;
}
