/*
 * Copyright © 2013 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */


/** @file clear-color.c
 *
 * Section 4.4.7(Framebuffer Objects) From GL spec 3.2 core:
 * When the Clear or ClearBuffer* commands are used to clear a layered
 * framebuffer attachment, all layers of the attachment are cleared.
 *
 * Test Layout
 *         Tex1     Tex2
 *      *--------*--------*    Each Layer for both tex1 and tex2 will be
 *      | layer3 | layer3 |   different colors.
 *      *--------*--------*
 *      | layer2 | layer2 |    Tex1 will be cleared using glClear()
 *      *--------*--------*
 *      | layer1 | layer1 |    Tex2 will be cleared using glClearBuffer()
 *      *--------*--------*
 *
 *      Result:
 *        Layer 1-3 of both tex1 and tex2 should be the clearColor
 */

#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 32;
	config.supports_gl_core_version = 32;

	config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE;
	config.khr_no_error_support = PIGLIT_NO_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

static GLuint fbo[2];
static GLuint texture[2];
static const int layers = 3;

bool
display_layered_texture(int x, int y, int w, int h, int texWidth, int texHeight,
			GLenum textureType, GLuint texture, int layers) {
	GLuint tempFBO;
	int i;
	int dx1, dy1, dx2, dy2;

	dx1 = x;
	dx2 = x+w;

	/* Gen temp fbo */
	glGenFramebuffers(1, &tempFBO);

	/* Loop through each layer, attaching the individual layer to the
	 * temp fbo, then blit fbo to the correct location on screen
	 */
	for(i = 0; i < layers; i++) {
		GLenum framebufferStatus;

		dy1 = y + (i)  *(h/layers);
		dy2 = y + (i+1)*(h/layers);

		glBindFramebuffer(GL_FRAMEBUFFER, tempFBO);
		glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					  texture, 0, i);

		framebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
		if(framebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
			printf("Framebuffer Status: %s\n",
			       piglit_get_gl_enum_name(framebufferStatus));
			return false;
		}

		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, piglit_winsys_fbo);
		glBindFramebuffer(GL_READ_FRAMEBUFFER, tempFBO);
		glBlitFramebuffer(0, 0, texWidth, texHeight,
				  dx1, dy1, dx2, dy2, GL_COLOR_BUFFER_BIT,
				  GL_NEAREST);
	}

	/* Cleanup temp fbo */
	glBindFramebuffer(GL_FRAMEBUFFER, piglit_winsys_fbo);
	glDeleteFramebuffers(1, &tempFBO);

	return piglit_check_gl_error(GL_NO_ERROR);
}

void
piglit_init(int argc, char **argv)
{
	int i, j;
	GLenum fbstatus;
	float *colorLayers =
		malloc(sizeof(float) * layers *
		       piglit_width * piglit_height * 3);
	float colors[3][3] = {
		{0.0, 0.0, 1.0},
		{0.0, 1.0, 0.0},
		{1.0, 0.0, 0.0}
	};

	/* Create color data for texture */
	for(j = 0; j < layers; j++) {
		float *thisLayer =
			&colorLayers[j * piglit_width * piglit_height * 3];
		for(i = 0; i < piglit_width*piglit_height; i++) {
			thisLayer[i*3+0] = colors[j][0];
			thisLayer[i*3+1] = colors[j][1];
			thisLayer[i*3+2] = colors[j][2];
		}
	}

	glGenTextures(2, texture);
	glGenFramebuffers(2, fbo);
	for(i = 0; i < 2; i++) {
		/* Create texture */
		glBindTexture(GL_TEXTURE_2D_ARRAY, texture[i]);
		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_REPEAT);
		glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, piglit_width, piglit_height,
			     layers, 0, GL_RGB, GL_FLOAT, colorLayers);

		/* Gen Framebuffer */
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
				     texture[i], 0);

		fbstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
		if(fbstatus != GL_FRAMEBUFFER_COMPLETE){
			printf("%s\n", piglit_get_gl_enum_name(fbstatus));
			piglit_report_result(PIGLIT_FAIL);
		}
	}

	if(!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);
}


enum piglit_result
piglit_display(void)
{
	bool pass = true;
	const float clearColor[3] = { 1, 1, 0 };

	/* Clear Default Framebuffer */
	glBindFramebuffer(GL_FRAMEBUFFER, piglit_winsys_fbo);
	glClearColor(1,1,0,1);
	glClear(GL_COLOR_BUFFER_BIT);

	/* Clear texture[0] with glClear() */
	glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
	glClearColor(clearColor[0], clearColor[1], clearColor[2], 1);
	glClear(GL_COLOR_BUFFER_BIT);

	/* Clear texture[1] with glClearBuffer() */
	glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
	glClearBufferfv(GL_COLOR, 0, clearColor);

	/* Display glClear texture */
	if(!display_layered_texture(0, 0, piglit_width/2, piglit_height,
				    piglit_width, piglit_height, GL_TEXTURE_2D_ARRAY,
				    texture[0], layers)) {
		printf("Failed to display layered texture for glClear\n");
		pass = false;
	}

	/* Display glClearBuffer texture */
	if(!display_layered_texture(piglit_width/2, 0, piglit_width/2,
				    piglit_height, piglit_width, piglit_height,
				    GL_TEXTURE_2D_ARRAY, texture[1], layers)) {
		printf("Failed to display layered texture for glClearBuffer\n");
		pass = false;
	}

	/* Check for passing conditions for glClear*/
	if(!piglit_probe_rect_rgb(0, 0, piglit_width/2, piglit_height,
				  clearColor)) {
		printf("Incorrect probe value for glClear test.\n");
		pass = false;
	}

	/* Check for passing conditions for glClearBuffer*/
	if(!piglit_probe_rect_rgb(piglit_width/2, 0, piglit_width/2,
				  piglit_height, clearColor)) {
		printf("Incorrect probe value for glClearBuffer test.\n");
		pass = false;
	}

	if(!piglit_check_gl_error(GL_NO_ERROR))
		pass = false;

	piglit_present_results();
	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}
