/*
 * Copyright (c) 2010 VMware, Inc.
 *
 * 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
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, 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
 * NON-INFRINGEMENT.  IN NO EVENT SHALL VMWARE AND/OR THEIR SUPPLIERS
 * 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
 * Tests GL_ARB_texture_rg extension (red/green texture formats)
 */

#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 10;

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

PIGLIT_GL_TEST_CONFIG_END

static const char *TestName = "texture-rg";

static GLint TexWidth = 128, TexHeight = 128;

struct format_info
{
   GLenum IntFormat, BaseFormat;
   GLboolean IsInteger;
   GLboolean IsFloat;
   GLboolean IsCompressed;
};


static const struct format_info IntFormats[] = {
   { GL_R8, GL_RED, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_R16, GL_RED, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_RG8, GL_RG, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_RG16, GL_RG, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_R16F, GL_RED, GL_FALSE, GL_TRUE, GL_FALSE },
   { GL_R32F, GL_RED, GL_FALSE, GL_TRUE, GL_FALSE },
   { GL_RG16F, GL_RG, GL_FALSE, GL_TRUE, GL_FALSE },
   { GL_RG32F, GL_RG, GL_FALSE, GL_TRUE, GL_FALSE },
   { GL_R8I, GL_R8, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_R8UI, GL_RED, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_R16I, GL_RED, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_R16UI, GL_RED, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_R32I, GL_RED, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_R32UI, GL_RED, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG8I, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG8UI, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG16I, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG16UI, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG32I, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RG32UI, GL_RG, GL_TRUE, GL_FALSE, GL_FALSE },
   { GL_RED, GL_RED, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_RG, GL_RG, GL_FALSE, GL_FALSE, GL_FALSE },
   { GL_COMPRESSED_RED, GL_RED, GL_FALSE, GL_FALSE, GL_TRUE },
   { GL_COMPRESSED_RG, GL_RG, GL_FALSE, GL_FALSE, GL_TRUE }
};

#define NUM_INT_FORMATS  (sizeof(IntFormats) / sizeof(IntFormats[0]))


static GLboolean
check_error(const char *file, int line)
{
   GLenum err = glGetError();
   if (err) {
      fprintf(stderr, "%s: error 0x%x at %s:%d\n", TestName, err, file, line);
      return GL_TRUE;
   }
   return GL_FALSE;
}


static void
fill_texture_image(GLint w, GLint h, GLint comps, GLubyte *buf)
{
   GLint i, j, k;
   for (i = 0; i < h; i++) {
      for (j = 0; j < w; j++) {
         for (k = 0; k < comps; k++) {
            GLfloat val;
            if (k == 0) {
               /* left/right = red gradient */
               val = (int) (255 * j / (float) (w - 1));
            }
            else {
               /* up/down = green gradient */
               val = (int) (255 * i / (float) (h - 1));
            }
            *buf++ = val;
         }
      }
   }
}


static GLboolean
test_teximage_formats(void)
{
   GLint i;
   GLubyte *image;

   GLuint tex;

   glGenTextures(1, &tex);
   glBindTexture(GL_TEXTURE_2D, tex);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   glEnable(GL_TEXTURE_2D);

   image = (GLubyte *) malloc(TexWidth * TexHeight * 2 * sizeof(GLubyte));

   for (i = 0; i < NUM_INT_FORMATS; i++) {
      const struct format_info *info = &IntFormats[i];
      const GLuint comps = (info->BaseFormat == GL_RED) ? 1 : 2;
      GLfloat expected[4], result[4];
      GLfloat error = 2.0 / 255.0; /* XXX fix */

      if (info->IsInteger) {
#if 0
         /* XXX we don't test integer texture formats yet */
	      if (!piglit_is_extension_supported("GL_EXT_texture_integer")) {
            continue;
         }
         else {
            /* test integer format */
         }
#else
         continue;
#endif
      }
      else if (info->IsFloat) {
	      if (!piglit_is_extension_supported("GL_ARB_texture_float")) {
            continue;
         }
      }

      if (info->IsCompressed) {
         /* use extra large error margin */
         error = 1.0 / 15.0;
      }

      fill_texture_image(TexWidth, TexHeight, comps, image);

      glTexImage2D(GL_TEXTURE_2D, 0, info->IntFormat,
                   TexWidth, TexHeight, 0,
                   info->BaseFormat, GL_UNSIGNED_BYTE, image);

      if (check_error(__FILE__, __LINE__)) {
         fprintf(stderr, "%s: Error in glTexImage2D for "
                 "internalFormat = %s\n", TestName,
                 piglit_get_gl_enum_name(info->IntFormat));
         return GL_FALSE;
      }

      if (0) {
         GLint f;
         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
                                  GL_TEXTURE_INTERNAL_FORMAT, &f);
         assert(f == info->IntFormat);
      }

      glClear(GL_COLOR_BUFFER_BIT);
      glBegin(GL_POLYGON);
      glTexCoord2f(0, 0);   glVertex2f(0, 0);
      glTexCoord2f(1, 0);   glVertex2f(TexWidth, 0);
      glTexCoord2f(1, 1);   glVertex2f(TexWidth, TexHeight);
      glTexCoord2f(0, 1);   glVertex2f(0, TexHeight);
      glEnd();

      /* setup expected polygon color */
      if (comps == 1) {
         expected[0] = 0.5;
         expected[1] = 0.0;
         expected[2] = 0.0;
         expected[3] = 1.0;
      }
      else {
         expected[0] = 0.5;
         expected[1] = 0.5;
         expected[2] = 0.0;
         expected[3] = 1.0;
      }

      /* test center pixel */
      result[0] = result[1] = result[2] = 0.0;
      result[3] = 1.0;
      glReadPixels(TexWidth/2, TexHeight/2, 1, 1, GL_RG, GL_FLOAT, result);

      if (check_error(__FILE__, __LINE__)) {
         fprintf(stderr, "%s: Error in glReadPixels(format = GL_RG)\n",
                 TestName);
         return GL_FALSE;
      }

      if (fabsf(result[0] - expected[0]) > error ||
          fabsf(result[1] - expected[1]) > error ||
          fabsf(result[2] - expected[2]) > error ||
          fabsf(result[3] - expected[3]) > error) {
         fprintf(stderr, "%s: failure with format %s:\n", TestName,
                 piglit_get_gl_enum_name(info->IntFormat));
         fprintf(stderr, "  expected color = %g, %g, %g, %g\n",
                 expected[0], expected[1], expected[2], expected[3]);
         fprintf(stderr, "  result color = %g, %g, %g, %g\n",
                 result[0], result[1], result[2], result[3]);
         return GL_FALSE;
      }

      piglit_present_results();
   }

   free(image);

   glDisable(GL_TEXTURE_2D);

   return GL_TRUE;
}


static GLboolean
test_drawpixels_formats(void)
{
   GLubyte *image;
   GLfloat result[4], expected[4];
   GLfloat error = 2.0 / 255.0; /* XXX fix */

   image = (GLubyte *) malloc(TexWidth * TexHeight * 2 * sizeof(GLubyte));

   fill_texture_image(TexWidth, TexHeight, 2, image);

   glWindowPos2iARB(0, 0);
   glDrawPixels(TexWidth, TexHeight, GL_RG, GL_UNSIGNED_BYTE, image);

   if (check_error(__FILE__, __LINE__)) {
      fprintf(stderr, "%s: Error in glDrawPixels(format = GL_RG)\n",
              TestName);
      return GL_FALSE;
   }

   /* test center pixel */
   result[0] = result[1] = result[2] = 0.0;
   result[3] = 1.0;
   glReadPixels(TexWidth/2, TexHeight/2, 1, 1, GL_RG, GL_FLOAT, result);
   
   expected[0] = 0.5;
   expected[1] = 0.5;
   expected[2] = 0.0;
   expected[3] = 1.0;

   if (fabsf(result[0] - expected[0]) > error ||
       fabsf(result[1] - expected[1]) > error ||
       fabsf(result[2] - expected[2]) > error ||
       fabsf(result[3] - expected[3]) > error) {
      fprintf(stderr, "%s: glDrawPixels failure with format GL_RG:\n", TestName);
      fprintf(stderr, "  expected color = %g, %g, %g, %g\n",
              expected[0], expected[1], expected[2], expected[3]);
      fprintf(stderr, "  result color = %g, %g, %g, %g\n",
              result[0], result[1], result[2], result[3]);
      return GL_FALSE;
   }


   free(image);

   return GL_TRUE;
}


enum piglit_result
piglit_display(void)
{
   if (test_teximage_formats() && test_drawpixels_formats())
      return PIGLIT_PASS;
   else
      return PIGLIT_FAIL;
}


void
piglit_init(int argc, char **argv)
{
   piglit_require_extension("GL_ARB_texture_rg");

   piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
}
