/*
 * $QNXLicenseC:
 * Copyright 2008, QNX Software Systems. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"). You 
 * may not reproduce, modify or distribute this software except in 
 * compliance with the License. You may obtain a copy of the License 
 * at: http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied.
 *
 * This file may contain contributions from others, either as 
 * contributors under the License or as licensors under other terms.  
 * Please review this entire file for other proprietary rights or license 
 * notices, as well as the QNX Development Suite License Guide at 
 * http://licensing.qnx.com/license-guide/ for other information.
 * $
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>
#include <GLES/gl.h>
#include <GLES/egl.h>

#include <gf/gf3d.h>

#include <readtex.h>

extern int demo_init_3d(int width, int height, GLubyte *image, GLint tw, GLint th);
extern int demo_init_2d(gf_context_t context);
extern void demo_draw_3d(EGLDisplay display, EGLSurface surface, int width, int height);
extern void demo_draw_2d(gf_context_t context, EGLSurface surface, int width, int height);
extern GLfloat view_rotx, view_roty, view_rotz;
extern GLfloat angle;

gf_dev_t	gf_inst;
gf_layer_t	layer;
int		layer_idx;

static EGLDisplay	display;
static EGLSurface	surface;

static EGLint attribute_list[] = {
	EGL_NATIVE_VISUAL_ID, 0,
	EGL_NATIVE_RENDERABLE, EGL_TRUE,
	EGL_RED_SIZE, 5,
	EGL_GREEN_SIZE, 5,
	EGL_BLUE_SIZE, 5,
	EGL_DEPTH_SIZE, 16,
	EGL_NONE
};

int
main(int argc, char **argv)
{
	gf_3d_target_t		target;
	gf_display_t		gf_disp;
	int			i;
	EGLConfig		config;
	EGLContext		econtext;
	EGLint			num_config;
	gf_dev_info_t	 	info;
	gf_layer_info_t		linfo;
	gf_display_info_t	disp_info;
	gf_context_t		context;
	int			width, height;
	int			xpos = 0, ypos = 0;
	GLenum			tformat;
	GLint			tw, th;
	GLubyte			*image=NULL;
	gf_surface_t		gfsurface;

	/* initialize the graphics device */
	if (gf_dev_attach(&gf_inst, NULL, &info) != GF_ERR_OK) {
		perror("gf_dev_attach()");
		return -1;
	}

	/* Setup the layer we will use */
	if (gf_display_attach(&gf_disp, gf_inst, 0, &disp_info) != GF_ERR_OK) {
		fprintf(stderr, "gf_display_attach() failed\n");
		exit(EXIT_FAILURE);
	}

	layer_idx = disp_info.main_layer_index;

	/* get an EGL display connection */
	display = eglGetDisplay(gf_inst);

	if (display == EGL_NO_DISPLAY) {
		fprintf(stderr, "eglGetDisplay() failed\n");
		return -1;
	}

	width = disp_info.xres;
	height = disp_info.yres;

	for (i = 1; i < argc; i++ ) {
		if (strcmp(argv[i], "-info") == 0) {
			printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
			printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
			printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
			printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
		} else if (strncmp(argv[i], "-layer=", 7) == 0) {
			layer_idx = strtol(&argv[i][7], 0, 0);
		} else if (strncmp(argv[i], "-width=", 7) == 0) {
			width = strtol(&argv[i][7], 0, 0);
		} else if (strncmp(argv[i], "-height=", 8) == 0) {
			height = strtol(&argv[i][8], 0, 0);
		} else if (strncmp(argv[i], "-xpos=", 6) == 0) {
			xpos = strtol(&argv[i][6], 0, 0);
		} else if (strncmp(argv[i], "-ypos=", 6) == 0) {
			ypos = strtol(&argv[i][6], 0, 0);
		} else {
			fprintf(stderr, "Usage: egl-intermix [-info] [-layer=n]\n");
			exit(EXIT_FAILURE);
		}
	}

	if (gf_layer_attach(&layer, gf_disp, layer_idx, 0) != GF_ERR_OK) {
		fprintf(stderr, "gf_layer_attach() failed\n");
		exit(EXIT_FAILURE);
	}

	/* initialize the EGL display connection */
	if (eglInitialize(display, NULL, NULL) != EGL_TRUE) {
		fprintf(stderr, "eglInitialize: error 0x%x\n", eglGetError());
		exit(EXIT_FAILURE);
	}

	for (i = 0; ; i++) {
		/* Walk through all possible pixel formats for this layer */
		if (gf_layer_query(layer, i, &linfo) == -1) {
			fprintf(stderr, "Couldn't find a compatible frame "
			    "buffer configuration on layer %d\n", layer_idx);
			exit(EXIT_FAILURE);
		}

		/*
		 * We want the color buffer format to match the layer format,
		 * so request the layer format through EGL_NATIVE_VISUAL_ID.
		 */
		attribute_list[1] = linfo.format;

		/* Look for a compatible EGL frame buffer configuration */
		if (eglChooseConfig(display,
		    attribute_list, &config, 1, &num_config) == EGL_TRUE) {
			if (num_config > 0) {
				break;
			}
		}
	}

/******************************************************************/
/* Here is my code, which causes black screen in OpenGL ES render */
/******************************************************************/

	/* Allocate displayable surface */
	if (gf_surface_create_layer(&gfsurface, &layer, 1, 0,
		width, height, disp_info.format, NULL,
		GF_SURFACE_CREATE_2D_ACCESSIBLE | GF_SURFACE_CREATE_3D_ACCESSIBLE |
		GF_SURFACE_CREATE_SHAREABLE)!=GF_ERR_OK)
	{
		fprintf(stderr, "Unable to allocate front surface\n");
		return -1;
	}

	/* Update layer */
	gf_layer_set_src_viewport(layer, 0, 0, width-1, height-1);
	gf_layer_set_dst_viewport(layer, xpos, ypos, xpos+width-1, ypos+height-1);

/**************************************************************************/
/* Comment out this line to get rid of bug                                */
/**************************************************************************/
	gf_layer_set_surfaces(layer, &gfsurface, 1);
/**************************************************************************/
/* End of bug :)                                                          */
/**************************************************************************/

	gf_layer_enable(layer);
	gf_layer_update(layer, GF_LAYER_UPDATE_NO_WAIT_IDLE);

	gf_layer_disable(layer);
	gf_layer_update(layer, GF_LAYER_UPDATE_NO_WAIT_IDLE);

	/* Destroy surface and layer */
	gf_surface_free(gfsurface);

	/* Detach layer _after_ disable layer. */
	gf_layer_detach(layer);

	/* Switch video mode */
	width=640;
	height=480;
	gf_display_set_mode(gf_disp, width, height, 60, disp_info.format, 0);

	/* Attach layer again */
	if (gf_layer_attach(&layer, gf_disp, layer_idx, 0) != GF_ERR_OK) {
		fprintf(stderr, "gf_layer_attach() failed\n");
		exit(EXIT_FAILURE);
	}
/******************************************************************/
/* Here is my code ends                                           */
/******************************************************************/

	/* create a 3D rendering target */
	if (gf_3d_target_create(&target, layer,
	    NULL, 0, width, height, linfo.format) != GF_ERR_OK) {
		fprintf(stderr, "Unable to create rendering target\n");
		return -1;
	}

	gf_layer_set_src_viewport(layer, 0, 0, width-1, height-1);
	gf_layer_set_dst_viewport(layer, xpos, ypos, xpos+width-1, ypos+height-1);
	gf_layer_enable(layer);

	/*
	 * The layer settings haven't taken effect yet since we haven't
	 * called gf_layer_update() yet.  This is exactly what we want,
	 * since we haven't supplied a valid surface to display yet.
	 * Later, the OpenGL ES library calls will call gf_layer_update()
	 * internally, when  displaying the rendered 3D content.
	 */

	/* create an EGL rendering context */
	econtext = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);

	/* create an EGL window surface */
	surface = eglCreateWindowSurface(display, config, target, NULL);

	if (surface == EGL_NO_SURFACE) {
		fprintf(stderr, "Create surface failed: 0x%x\n", eglGetError());
		exit(EXIT_FAILURE);
	}

	/* connect the context to the surface */
	if (eglMakeCurrent(display, surface, surface, econtext) == EGL_FALSE) {
		fprintf(stderr, "Make current failed: 0x%x\n", eglGetError());
		exit(EXIT_FAILURE);
	}

	if (gf_context_create(&context) != GF_ERR_OK) {
		fprintf(stderr, "Create context failed\n");
		exit(EXIT_FAILURE);
	}

	image = LoadRGBImage("qnx.rgb", &tw, &th, &tformat);

	if (image == NULL) {
		fprintf(stderr, "Could not load qnx.rgb");
		exit(EXIT_FAILURE);
	}

	if (tformat != GL_RGB) {
		fprintf(stderr, "qnx.rgb should not be GL_RGB format!");
		exit(EXIT_FAILURE);
	}

	demo_init_2d(context);

	if (demo_init_3d(width, height, image, tw, th) != -1) {
		while (1) {
			eglWaitNative(0);
			demo_draw_3d(display, surface, width, height);

			/* Must re-call this after doing a SwapBuffers */
			if (gf_context_set_surface_3d(context, surface) != GF_ERR_OK) {
				fprintf(stderr, "gf_context_set_surface_3d failed\n");
				exit(EXIT_FAILURE);
			}

			eglWaitGL();
			demo_draw_2d(context, surface, width, height);

			eglSwapBuffers(display,surface);
		}
	} else {
		exit(EXIT_FAILURE);
	}
	
	return 0;
}
