#include "evas_gl_private.h"

static void _evas_gl_common_viewport_set(Evas_GL_Context *gc);
static void _evas_gl_common_dither_set(Evas_GL_Context *gc);
static void _evas_gl_common_blend_set(Evas_GL_Context *gc);
static void _evas_gl_common_color_set(Evas_GL_Context *gc);
static void _evas_gl_common_texture_set(Evas_GL_Context *gc);
static void _evas_gl_common_clip_set(Evas_GL_Context *gc);
static void _evas_gl_common_buf_set(Evas_GL_Context *gc);
static void _evas_gl_common_other_set(Evas_GL_Context *gc);

static Evas_GL_Context *_evas_gl_common_context = NULL;

Evas_GL_Context *
evas_gl_common_context_new(void)
{
   Evas_GL_Context *gc;
   
   if (_evas_gl_common_context)
     {
	_evas_gl_common_context->references++;
	return _evas_gl_common_context;
     }
   gc = calloc(1, sizeof(Evas_GL_Context));
   if (!gc) return NULL;
   gc->max_texture_depth = 32;
   gc->max_texture_size = 2048;
   gc->read_buf = GL_BACK;
   gc->write_buf = GL_BACK;
   gc->dither = 1;
   gc->blend = 0;
   gc->references     = 1;
   
   gc->change.size    = 1;
   gc->change.dither  = 1;
   gc->change.blend   = 1;
   gc->change.color   = 1;
   gc->change.texture = 1;
   gc->change.clip    = 1;
   gc->change.buf     = 1;
   gc->change.other   = 1;
   return gc;
}

void
evas_gl_common_context_free(Evas_GL_Context *gc)
{
   gc->references--;
   if (gc->references > 0) return;
   
   if (gc == _evas_gl_common_context) _evas_gl_common_context = NULL;
   free(gc);
}

void
evas_gl_common_context_use(Evas_GL_Context *gc)
{
   if (_evas_gl_common_context == gc) return;
   if (!gc->ext.checked)
     {
	const GLubyte *ext;
	
	ext = glGetString(GL_EXTENSIONS);
	if (ext)
	  {
	     if (strstr(ext, "GL_SGIS_generate_mipmap")) gc->ext.sgis_generate_mipmap = 1;
	     if (strstr(ext, "GL_NV_texture_rectangle")) gc->ext.nv_texture_rectangle = 1;
	     /* technically this should work, as its a compatible */
	     /* implementation of the nvidia texture_rectangle extension */
	     /* since the #define value is the same as is the description */
	     /* it was fixed in the latest (3.2.5) fglrx drivers */
	     if (strstr(ext, "GL_EXT_texture_rectangle")) gc->ext.nv_texture_rectangle = 1;
	     printf("GL EXT supported: GL_SGIS_generate_mipmap = %x\n", gc->ext.sgis_generate_mipmap);
	     printf("GL EXT supported: GL_NV_texture_rectangle = %x\n", gc->ext.nv_texture_rectangle);
	  }
	else
	  {
	     printf("GL EXT supported: No extensions!!!!!\n");
	  }
	gc->ext.checked = 1;
     }
   _evas_gl_common_context = gc;
   _evas_gl_common_viewport_set(gc);
   _evas_gl_common_dither_set(gc);
   _evas_gl_common_blend_set(gc);
   _evas_gl_common_color_set(gc);
   _evas_gl_common_texture_set(gc);
   _evas_gl_common_texture_set(gc);
   _evas_gl_common_clip_set(gc);
   _evas_gl_common_buf_set(gc);
   _evas_gl_common_other_set(gc);
}

void
evas_gl_common_context_resize(Evas_GL_Context *gc, int w, int h)
{
   if ((gc->w == w) && (gc->h == h)) return;
   gc->change.size = 1;
   gc->w = w;
   gc->h = h;
   if (_evas_gl_common_context == gc) _evas_gl_common_viewport_set(gc);
}

void
evas_gl_common_context_color_set(Evas_GL_Context *gc, int r, int g, int b, int a)
{
   if (r < 0) r = 0;
   else if (r > 255) r = 255;
   if (g < 0) g = 0;
   else if (g > 255) g = 255;
   if (b < 0) b = 0;
   else if (b > 255) b = 255;
   if (a < 0) a = 0;
   else if (a > 255) a = 255;
   if ((gc->r == r) && (gc->g == g) && (gc->b == b) && (gc->a == a)) return;
   gc->change.color = 1;
   gc->r = r;
   gc->g = g;
   gc->b = b;
   gc->a = a;
   if (_evas_gl_common_context == gc) _evas_gl_common_color_set(gc);
}

void
evas_gl_common_context_blend_set(Evas_GL_Context *gc, int blend)
{
   if (((blend) && (gc->blend)) || ((!blend) && (!gc->blend))) return;
   gc->change.blend = 1;
   gc->blend = blend;
   if (_evas_gl_common_context == gc) _evas_gl_common_blend_set(gc);
}

void
evas_gl_common_context_dither_set(Evas_GL_Context *gc, int dither)
{
   if (((dither) && (gc->dither)) || ((!dither) && (!gc->dither))) return;
   gc->change.dither = 1;
   gc->dither = dither;
   if (_evas_gl_common_context == gc) _evas_gl_common_dither_set(gc);
}

void
evas_gl_common_context_texture_set(Evas_GL_Context *gc, Evas_GL_Texture *tex, int smooth, int w, int h)
{
   if (gc->font_texture > 0)
     {
	gc->font_texture = 0;
	gc->change.texture = 1;
     }
   if (gc->texture != tex)
     {
	if (gc->texture) gc->texture->references--;
	gc->texture = tex;
	gc->change.texture = 1;
	if (tex) tex->references++;
     }
   if (tex)
     {
	if (((smooth) && (!tex->smooth)) || 
	    ((!smooth) && (tex->smooth)))
	  {
	     tex->smooth = smooth;
	     tex->changed = 1;
	  }
	tex->uw = w;
	tex->uh = h;
     }
   if (_evas_gl_common_context == gc) _evas_gl_common_texture_set(gc);
}

void
evas_gl_common_context_font_texture_set(Evas_GL_Context *gc, Evas_GL_Font_Texture *ft)
{
   if (gc->texture)
     {
	if (gc->texture) gc->texture->references--;
	gc->texture = NULL;
	gc->change.texture = 1;
     }
   if (gc->font_texture != ft->texture)
     {
	gc->font_texture = ft->texture;
	gc->font_texture_not_power_of_two = ft->pool->not_power_of_two;
	gc->change.texture = 1;
     }
   if (!gc->change.texture) return;
   if (_evas_gl_common_context == gc) _evas_gl_common_texture_set(gc);
}

void
evas_gl_common_context_clip_set(Evas_GL_Context *gc, int on, int x, int y, int w, int h)
{
   if (x < 0)
     {
	w += x; 
	x = 0;
     }
   if (y < 0)
     {
	h += y; 
	y = 0;
     }
   if (w < 0) w = 0;
   if (h < 0) h = 0;
   if (((!on) && (!gc->clip.active)) ||
       ((on) && (gc->clip.active) && 
	(x == gc->clip.x) && (y == gc->clip.y) &&
	(w == gc->clip.w) && (h == gc->clip.h)))
     return;
   gc->change.clip = 1;
   gc->clip.active = on;
   gc->clip.x = x;
   gc->clip.y = y;
   gc->clip.w = w;
   gc->clip.h = h;
   if (_evas_gl_common_context == gc) _evas_gl_common_clip_set(gc);
}

void
evas_gl_common_context_read_buf_set(Evas_GL_Context *gc, GLenum buf)
{
   if (gc->read_buf == buf) return;
   gc->change.buf = 1;
   gc->read_buf = buf;
   if (_evas_gl_common_context == gc) _evas_gl_common_buf_set(gc);
}

void
evas_gl_common_context_write_buf_set(Evas_GL_Context *gc, GLenum buf)
{
   if (gc->write_buf == buf) return;
   gc->change.buf = 1;
   gc->write_buf = buf;
   if (_evas_gl_common_context == gc) _evas_gl_common_buf_set(gc);
}

static void
_evas_gl_common_viewport_set(Evas_GL_Context *gc)
{
   if (!gc->change.size) return;
   glViewport(0, 0, gc->w, gc->h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, gc->w, 0, gc->h, -1, 1);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glScalef(1, -1, 1);
   glTranslatef(0, - gc->h, 0);	
   gc->change.size = 0;
}

static void
_evas_gl_common_dither_set(Evas_GL_Context *gc)
{
   if (!gc->change.dither) return;
   if (gc->dither)
     glEnable(GL_DITHER);
   else
     glDisable(GL_DITHER);
   gc->change.dither = 0;
}

static void
_evas_gl_common_blend_set(Evas_GL_Context *gc)
{
   if (!gc->change.blend) return;
   if (gc->blend) 
     {
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     }
   else
     glDisable(GL_BLEND);
   gc->change.blend = 0;
}

static void
_evas_gl_common_color_set(Evas_GL_Context *gc)
{
   int r, g, b, a;
   
   if (!gc->change.color) return;
   glColor4d((double)gc->r / 255.0,
	     (double)gc->g / 255.0,
	     (double)gc->b / 255.0,
	     (double)gc->a / 255.0);
   gc->change.color = 0;
}

static void
_evas_gl_common_texture_set(Evas_GL_Context *gc)
{
   if (!gc->change.texture) return;
   if (gc->font_texture > 0)
     {
	if (gc->font_texture_not_power_of_two)
	  {
	     glEnable(GL_TEXTURE_2D);
	     glEnable(GL_TEXTURE_RECTANGLE_NV);
	     glBindTexture(GL_TEXTURE_RECTANGLE_NV, gc->font_texture);
	  }
	else
	  {
	     if (gc->ext.nv_texture_rectangle)
	       glDisable(GL_TEXTURE_RECTANGLE_NV);
	     glEnable(GL_TEXTURE_2D);
	     glBindTexture(GL_TEXTURE_2D, gc->font_texture);
	  }
     }
   else if (gc->texture)
     {
	if (gc->texture->not_power_of_two)
	  {
	     glEnable(GL_TEXTURE_2D);
	     glEnable(GL_TEXTURE_RECTANGLE_NV);
	     glBindTexture(GL_TEXTURE_RECTANGLE_NV, gc->texture->texture);
	  }
	else
	  {
	     if (gc->ext.nv_texture_rectangle) glDisable(GL_TEXTURE_RECTANGLE_NV);
	     glEnable(GL_TEXTURE_2D);
	     glBindTexture(GL_TEXTURE_2D, gc->texture->texture);
	  }
	if (gc->texture->not_power_of_two)
	  {
	     if (gc->texture->changed)
	       {
		  gc->texture->changed = 0;
	       }
	  }
	else
	  {
	     if (gc->texture->changed)
	       {
		  if (gc->texture->not_power_of_two)
		    {
		       if (gc->texture->smooth)
			 {
			    glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			    glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			 }
		       else
			 {
			    glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			    glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			 }
		    }
		  else
		    {
		       if (gc->texture->smooth)
			 {
			    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			    if (gc->texture->have_mipmaps)
			      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			    else
			      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			 }
		       else
			 {
			    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			 }
		    }
		  gc->texture->changed = 0;
	       }
	  }
     }
   else if (gc->font_texture == 0)
     {
	glDisable(GL_TEXTURE_2D);
	if (gc->ext.nv_texture_rectangle) glDisable(GL_TEXTURE_RECTANGLE_NV);
     }
   gc->change.texture = 0;
}

static void
_evas_gl_common_clip_set(Evas_GL_Context *gc)
{
   if (!gc->change.clip) return;
   /* might be faster using clip planes ??? glClipPlane() */
   if (gc->clip.active)
     {
	glEnable(GL_SCISSOR_TEST);
	glScissor(gc->clip.x,
		  gc->h - gc->clip.y - gc->clip.h, 
		  gc->clip.w, 
		  gc->clip.h);
     }
   else
     glDisable(GL_SCISSOR_TEST);
   gc->change.clip = 0;
}

static void
_evas_gl_common_buf_set(Evas_GL_Context *gc)
{
   if (!gc->change.buf) return;
   glDrawBuffer(gc->write_buf);
   glReadBuffer(gc->read_buf);
   gc->change.buf = 0;
}

static void
_evas_gl_common_other_set(Evas_GL_Context *gc)
{
   if (!gc->change.other) return;
   glShadeModel(GL_FLAT);
   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
//   glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
   glDisable(GL_LINE_SMOOTH);
   glDisable(GL_CULL_FACE);
   glDepthMask(GL_FALSE);
   glPixelStorei(GL_PACK_ALIGNMENT, 1);
   gc->change.other = 0;
}
