/*
 * Copyright 2009 Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of either or both of the following licenses:
 *
 * 1) the GNU Lesser General Public License version 3, as published by the
 * Free Software Foundation; and/or
 * 2) the GNU Lesser General Public License version 2.1, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of both the GNU Lesser General Public
 * License version 3 and version 2.1 along with this program.  If not, see
 * <http://www.gnu.org/licenses/>
 *
 * Authored by: Jay Taoko <jay.taoko@canonical.com>
 *
 */
/** 
 * SECTION:ctk-utils
 * @short_description: Utility functions
 *
 * Utility functions
 */

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>

#include <GL/glew.h>
#include <GL/glxew.h>

#include <clutter/clutter.h>
#include "ctk-utils.h"
#include "ctk-gfx-private.h"
#include <gio/gio.h>


void print_shader_object_error(GLuint obj)
{
  int info_length = 0;
  int chars_written  = 0;
  char *infoLog;

  if (glIsShader(obj) == GL_TRUE)
    glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &info_length);
  else
    glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &info_length);

  if (info_length > 1)
    {
      infoLog = (char *)malloc(info_length);
      if (glIsShader(obj) == GL_TRUE)
        {
          glGetShaderInfoLog(obj, info_length, &chars_written, infoLog);
          g_warning ("Shader Error Log: %s\n", infoLog);
        }
      else
        {
          glGetProgramInfoLog(obj, info_length, &chars_written, infoLog);
          g_warning ("Program Error Log: %s\n", infoLog);
        }
      free(infoLog);
    }
}

char *ctk_read_shader_text_file(const char *filename)
{
  GError *error = 0;
  gchar *buffer = 0;
  gsize length = 0;
  g_file_get_contents(filename, &buffer, &length, &error);
  if (error != NULL)
    {
      g_warning("Unable to read file %s: %s\n", filename, error->message);
      g_error_free (error);
      return NULL;
    }
  return buffer;
}

ShaderProgram *ctk_create_shader_program_from_source(const char* vtx_filename, const char* frag_filename)
{
  char *vstxt, *fstxt;
  ShaderProgram* sh = (ShaderProgram*) g_malloc0(sizeof(ShaderProgram));

  sh->shvert = glCreateShader(GL_VERTEX_SHADER);
  vstxt = ctk_read_shader_text_file(vtx_filename);
  glShaderSource(sh->shvert, 1, (const char **)&vstxt, NULL);
  g_free(vstxt);
  glCompileShader(sh->shvert);
  print_shader_object_error(sh->shvert);

  sh->shfrag = glCreateShader(GL_FRAGMENT_SHADER);
  fstxt = ctk_read_shader_text_file (frag_filename);
  glShaderSource(sh->shfrag, 1, (const char **)&fstxt, NULL);
  g_free(fstxt);
  glCompileShader(sh->shfrag);
  print_shader_object_error(sh->shfrag);

  sh->shprog = glCreateProgram();
  glAttachShader(sh->shprog, sh->shvert);
  glAttachShader(sh->shprog, sh->shfrag);
  glLinkProgram(sh->shprog);
  print_shader_object_error(sh->shprog);

  return sh;
}

void ctk_delete_shader_program(ShaderProgram *sh)
{
  if (sh)
    {
      glDetachShader(sh->shprog, sh->shvert);
      glDetachShader(sh->shprog, sh->shfrag);
      glDeleteShader(sh->shvert);
      glDeleteShader(sh->shfrag);
      glDeleteProgram(sh->shprog);
      g_free(sh);
    }
}

void ctk_render_quad_rt(CtkRenderTarget* rt, ShaderProgram *shader, int window_w, int window_h, int x, int y, int w, int h)
{
  guint texid = ctk_render_target_get_color_buffer_ogl_id(rt);
  guint texwidth = ctk_render_target_get_width(rt);
  guint texheight = ctk_render_target_get_height(rt);
  ctk_render_quad(texid, texwidth, texheight, shader, window_w, window_h, x, y, w, h);
}

void ctk_render_quad(guint texid, guint texwidth, guint texheight, ShaderProgram *shader, int window_w, int window_h, int x, int y, int w, int h)
{
  ClutterVertex vtx[4];

  vtx[0].x = x;
  vtx[0].y = y;
  vtx[1].x = x;
  vtx[1].y = y+h;
  vtx[2].x = x+w;
  vtx[2].y = y+h;
  vtx[3].x = x+w;
  vtx[3].y = y;

  ctk_render_custom_quad(texid, texwidth, texheight, shader, window_w, window_h, vtx);
}

void ctk_render_custom_quad(guint texid, guint texwidth, guint texheight, ShaderProgram *shader, int window_w, int window_h, ClutterVertex vtx[4])
{
  GLint loc;

  /* Set texture 0 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE0) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );

    CHECKGL( glBindTexture(GL_TEXTURE_2D, texid) );
  }

  /* Use the shader */
  CHECKGL( glUseProgram(shader->shprog) );
  loc = glGetUniformLocationARB(shader->shprog, "Tex0");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 0) );
  loc = glGetUniformLocationARB(shader->shprog, "TextureSize");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform2fARB(loc, texwidth, texheight) );

  /*int blend;
  int blend_src;
  int blend_dst;

  glGetIntegerv(GL_BLEND, &blend);
  glGetIntegerv(GL_BLEND_SRC, &blend_src);
  glGetIntegerv(GL_BLEND_DST, &blend_dst);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);*/

  /* Preserve model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
    CHECKGL( glOrtho(0, window_w, window_h, 0, -1, 1) );
    
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
  }

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 1.0f);
    glVertex4f(vtx[0].x, vtx[0].y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
    glVertex4f(vtx[1].x, vtx[1].y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 0.0f);
    glVertex4f(vtx[2].x, vtx[2].y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 1.0f);
    glVertex4f(vtx[3].x, vtx[3].y, 0, 1.0f);

  }
  glEnd();

  /* Restore model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPopMatrix() );
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPopMatrix() );
  }
  /*glBlendFunc(blend_src, blend_dst);*/

  /* Stop using the shader program */
  CHECKGL( glUseProgram(0) );

}

void ctk_render_quad_alpha_mask(
      unsigned int texid,
      unsigned int texture_width,
      unsigned int texture_height,
      gfloat red, gfloat green, gfloat blue, gfloat alpha,
      int window_w, int window_h, int x, int y, int w, int h)
{
  GLint loc;

  /* Set texture 0 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE0) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );

    CHECKGL( glBindTexture(GL_TEXTURE_2D, texid /*ctk_render_target_get_color_buffer_ogl_id(rt)*/) );
  }

  /* Use the shader */
  CHECKGL( glUseProgram(g_shTextureAlpha->shprog) );
  loc = glGetUniformLocationARB(g_shTextureAlpha->shprog, "Tex0");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 0) );
  loc = glGetUniformLocationARB(g_shTextureAlpha->shprog, "TextureSize");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform2fARB(loc, texture_width, texture_height) );
  loc = glGetUniformLocationARB(g_shTextureAlpha->shprog, "MaskColor");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform4fARB(loc, 
            (gfloat)red,
            (gfloat)green,
            (gfloat)blue,
            (gfloat)alpha) );

  int blend;
  int blend_src;
  int blend_dst;

  glGetIntegerv(GL_BLEND, &blend);
  glGetIntegerv(GL_BLEND_SRC, &blend_src);
  glGetIntegerv(GL_BLEND_DST, &blend_dst);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  /* Preserve model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
    CHECKGL( glOrtho(0, window_w, window_h, 0, -1, 1) );
    
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
  }

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 1.0f);
    glVertex4f(x, y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 1.0f);
    glVertex4f(x+w, y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 0.0f);
    glVertex4f(x+w, y+h, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
    glVertex4f(x, y+h, 0, 1.0f);
  }
  glEnd();

  /* Restore model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPopMatrix() );
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPopMatrix() );
  }
  glBlendFunc(blend_src, blend_dst);

  /* Stop using the shader program */
  CHECKGL( glUseProgram(0) );
}

void custom_render_quad_gaussian_blur_separable(CtkRenderTarget* rt,
                                    ShaderProgram *shader, gfloat sigma,
                                    int window_w, int window_h, int x, int y, int w, int h)
{
  GLint loc;
  guint num_tap = 7; /* must be odd number */
  gfloat W[num_tap];
  gfloat sum = 0.0f;
  guint i = 0;

  guint half = (num_tap-1)/2;

  W[half] = (1.0f/(sqrt(2.0f*3.14159265358f)*sigma)) * exp(-0.0f/(2.0f*sigma*sigma));
  sum += W[half];
  for(i = 0; i < half; i++)
  {
    float X = (i + 1)*(i + 1);
    W[half - i - 1] = W[half + i + 1] = (1.0f/(sqrt(2.0f*3.14159265358f)*sigma)) * exp(-X/(2.0f*sigma*sigma));
    sum += 2.0f * W[half - i - 1];
  }

  /* normalization */
  for(i = 0; i < num_tap; i++)
  {
    W[i] = W[i] / sum;
  }

  /* Set texture 0 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE0) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );

    CHECKGL( glBindTexture(GL_TEXTURE_2D, ctk_render_target_get_color_buffer_ogl_id(rt)) );
  }

  /* Use the shader */
  CHECKGL( glUseProgram(shader->shprog) );
  loc = glGetUniformLocationARB(shader->shprog, "Tex0");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 0) );
  loc = glGetUniformLocationARB(shader->shprog, "TextureSize");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform2fARB(loc, ctk_render_target_get_width(rt), ctk_render_target_get_height(rt)) );
  loc = glGetUniformLocationARB(shader->shprog, "W");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1fv(loc, num_tap, W) );

  /*int blend;
  int blend_src;
  int blend_dst;

  glGetIntegerv(GL_BLEND, &blend);
  glGetIntegerv(GL_BLEND_SRC, &blend_src);
  glGetIntegerv(GL_BLEND_DST, &blend_dst);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);*/

  /* Preserve model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
    CHECKGL( glOrtho(0, window_w, window_h, 0, -1, 1) );
    
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
  }

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 1.0f);
    glVertex4f(x, y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 1.0f);
    glVertex4f(x+w, y, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 0.0f);
    glVertex4f(x+w, y+h, 0, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
    glVertex4f(x, y+h, 0, 1.0f);
  }
  glEnd();

  /* Restore model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPopMatrix() );
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPopMatrix() );
  }
  /*glBlendFunc(blend_src, blend_dst);*/

  /* Stop using the shader program */
  CHECKGL( glUseProgram(0) );
}

void custom_render_quad_texture_mask(CtkRenderTarget* fbo,
      unsigned int texid,
      unsigned int texture_width,
      unsigned int texture_height,
      ShaderProgram *shader,
      int window_w,
      int window_h,
      int x,
      int y,
      int w,
      int h)
{
  GLint loc;

  /* Use the shader */
  CHECKGL( glUseProgram(shader->shprog) );

  /* Set texture 0 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE0) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable  (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );
    CHECKGL( glBindTexture(GL_TEXTURE_2D, ctk_render_target_get_color_buffer_ogl_id(fbo)) );
  }

  /*  Set texture 1 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE1) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable  (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );
    CHECKGL( glBindTexture(GL_TEXTURE_2D, texid) );
  }

  /* Preserve model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
    CHECKGL( glOrtho(0, window_w, window_h, 0, -1, 1) );
    
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
  }

  loc = glGetUniformLocationARB(shader->shprog, "Tex0");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 0) );

  loc = glGetUniformLocationARB(shader->shprog, "Tex1");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 1) );

  loc = glGetUniformLocationARB(shader->shprog, "TextureSize");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform2fARB(loc, ctk_render_target_get_width(fbo), ctk_render_target_get_height(fbo)) );

  int blend;
  int blend_src;
  int blend_dst;

  glGetIntegerv(GL_BLEND, &blend);
  glGetIntegerv(GL_BLEND_SRC, &blend_src);
  glGetIntegerv(GL_BLEND_DST, &blend_dst);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

  float tile_u = (float)w/texture_width;
  float tile_v = (float)h/texture_height;

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE1, 0.0f, tile_v);
    glVertex4f(x, y, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 1.0f);
    glMultiTexCoord2f(GL_TEXTURE1, tile_u, tile_v);
    glVertex4f(x+w, y, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, 1.0f, 0.0f);
    glMultiTexCoord2f(GL_TEXTURE1, tile_u, 0.0f);
    glVertex4f(x+w, y+h, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
    glMultiTexCoord2f(GL_TEXTURE1, 0.0f, 0.0f);
    glVertex4f(x, y+h, 0, 1.0f);
  }
  glEnd();

  glBlendFunc(blend_src, blend_dst);

  /* Restore model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPopMatrix() );
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPopMatrix() );
  }

  /*  Disable texture 1 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE1) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glDisable (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );
    CHECKGL( glBindTexture(GL_TEXTURE_2D, 0) );
  }

  /* Stop using the shader program */
  CHECKGL( glUseProgram(0) );
}

void ctk_copy_rendertarget_to_rendertarget(CtkRenderTarget* rt_src, float u0, float v0, float u1, float v1,
                    CtkRenderTarget* rt_dst, int x_dst, int y_dst, int w_dst, int h_dst)
{

  GLint loc;

  ctk_render_target_bind(rt_dst);
  /* Use the shader */
  CHECKGL( glUseProgram(g_shTexture->shprog) );

  /* Set texture 0 environment mode */
  {
    CHECKGL( glActiveTextureARB(GL_TEXTURE0) );
    CHECKGL( glDisable (GL_TEXTURE_1D) );
    CHECKGL( glEnable  (GL_TEXTURE_2D) );
    CHECKGL( glDisable (GL_TEXTURE_3D) );
    CHECKGL( glDisable (GL_TEXTURE_CUBE_MAP_EXT) );
    CHECKGL( glDisable (GL_TEXTURE_RECTANGLE_ARB) );
    CHECKGL( glBindTexture(GL_TEXTURE_2D, ctk_render_target_get_color_buffer_ogl_id(rt_src)) );
  }

  /* Preserve model-view and projection matrices */
  {
    CHECKGL( glViewport(0, 0, ctk_render_target_get_width(rt_dst), ctk_render_target_get_height(rt_dst)) );
    CHECKGL( glScissor(0, 0, ctk_render_target_get_width(rt_dst), ctk_render_target_get_height(rt_dst)) );
    CHECKGL( glDisable(GL_SCISSOR_TEST) );
  
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
    CHECKGL( glOrtho(0, ctk_render_target_get_width(rt_dst), ctk_render_target_get_height(rt_dst), 0, -1, 1) );
    
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPushMatrix() );
    CHECKGL( glLoadIdentity() );
  }

  loc = glGetUniformLocationARB(g_shTexture->shprog, "Tex0");
  CHECKGL_MSG( "glGetUniformLocationARB" );
  CHECKGL( glUniform1iARB(loc, 0) );

//   loc = glGetUniformLocationARB(shader->shprog, "TextureSize");
//   CHECKGL_MSG( "glGetUniformLocationARB" );
//   CHECKGL( glUniform2fARB(loc, ctk_render_target_get_width(fbo), ctk_render_target_get_height(fbo)) );

  glDisable(GL_BLEND);

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2f(GL_TEXTURE0, u0, v0);
    glVertex4f(x_dst, y_dst, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, u0, v1);
    glVertex4f(x_dst, y_dst+h_dst, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, u1, v1);
    glVertex4f(x_dst+w_dst, y_dst+h_dst, 0, 1.0f);

    glMultiTexCoord2f(GL_TEXTURE0, u1, v0);
    glVertex4f(x_dst+w_dst, y_dst, 0, 1.0f);
  }
  glEnd();

  /* Restore model-view and projection matrices */
  {
    CHECKGL( glMatrixMode(GL_PROJECTION) );
    CHECKGL( glPopMatrix() );
    CHECKGL( glMatrixMode(GL_MODELVIEW) );
    CHECKGL( glPopMatrix() );
  }

  glEnable(GL_BLEND);

  /* Stop using the shader program */
  CHECKGL( glUseProgram(0) );
}

void ctk_copy_render_target_to_cached_texture(CtkEffectContext *fxctx, CtkRenderTarget* rt_src, guint texid)
{
  CtkRenderTarget *rt_cache = ctk_effect_context_get_utility_render_target(fxctx);
  guint dest_fbo = ctk_render_target_get_frame_buffer_ogl_id (rt_cache);

  guint rt_width = ctk_render_target_get_width(rt_src);
  guint rt_height = ctk_render_target_get_height(rt_src);
  CHECKGL (glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, dest_fbo));

//CHECKGL (glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, priv->cached_renderbuffer_texture));
//CHECKGL (glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT,
//                                   GL_DEPTH_COMPONENT,
//                                   actor_screen_width,
//                                   actor_screen_height));
//CHECKGL (glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
//                                       GL_DEPTH_ATTACHMENT_EXT,
//                                       GL_RENDERBUFFER_EXT,
//                                       priv->cached_renderbuffer_texture));

  CHECKGL (glActiveTextureARB(GL_TEXTURE0) );
  CHECKGL (glBindTexture (GL_TEXTURE_2D, texid) );
  CHECKGL (glTexImage2D (GL_TEXTURE_2D,
                             0,
                             GL_RGBA8,
                             rt_width,
                             rt_height,
                             0,
                             GL_RGBA,
                             GL_UNSIGNED_BYTE,
                             NULL));
      
  CHECKGL (glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
                                      GL_COLOR_ATTACHMENT0_EXT,
                                      GL_TEXTURE_2D,
                                      texid,
                                      0));
  
  guint src_fbo = ctk_render_target_get_frame_buffer_ogl_id (rt_src);

  CHECKGL (glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src_fbo));
  CHECKGL (glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dest_fbo));
  CHECKGL (glBlitFramebufferEXT(0, 0, rt_width, rt_height,
                        0, 0, rt_width, rt_height,
                        GL_COLOR_BUFFER_BIT, GL_NEAREST));

  CHECKGL (glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, dest_fbo));
  CHECKGL (glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
                                      GL_COLOR_ATTACHMENT0_EXT,
                                      GL_TEXTURE_2D,
                                      0,
                                      0));
  CHECKGL (glActiveTextureARB(GL_TEXTURE0) );
  CHECKGL (glBindTexture (GL_TEXTURE_2D, 0) );

  CHECKGL (glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, 0));
  CHECKGL (glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, 0));
  CHECKGL (glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0));
}

void ctk_get_actor_screen_position (CtkActor* actor, gfloat *x, gfloat *y, gfloat *width, gfloat *height, ClutterVertex vertex[4])
{
  float viewport[4];
  float X, Y, Z, W;

  ClutterActorBox box;

  CoglMatrix mod_mtx;
  CoglMatrix proj_mtx;

  cogl_push_matrix();

  cogl_get_modelview_matrix (&mod_mtx);
  cogl_get_projection_matrix (&proj_mtx);
  cogl_get_viewport (viewport);

  cogl_pop_matrix();

  ctk_actor_recurse_get_stored_allocation_box ( actor, &box);

  *width =  box.x2 - box.x1;
  *height = box.y2 - box.y1;


  X = 0.0f; Y = 0.0f; Z = 0.0f; W = 1.0f;
  cogl_matrix_transform_point (&mod_mtx, &X, &Y, &Z, &W);
  cogl_matrix_transform_point (&proj_mtx, &X, &Y, &Z, &W);
  vertex[0].x = (((X/W) + 1.0f)/2)*viewport[2] + viewport[0];
  vertex[0].y = ((1.0f - (Y/W))/2)*viewport[3] + viewport[1];
  
  X = 0.0f; Y = *height; Z = 0.0f; W = 1.0f;
  cogl_matrix_transform_point (&mod_mtx, &X, &Y, &Z, &W);
  cogl_matrix_transform_point (&proj_mtx, &X, &Y, &Z, &W);
  vertex[1].x = (((X/W) + 1.0f)/2)*viewport[2] + viewport[0];
  vertex[1].y = ((1.0f - (Y/W))/2)*viewport[3] + viewport[1];

  X = *width; Y = *height; Z = 0.0f; W = 1.0f;
  cogl_matrix_transform_point (&mod_mtx, &X, &Y, &Z, &W);
  cogl_matrix_transform_point (&proj_mtx, &X, &Y, &Z, &W);
  vertex[2].x = (((X/W) + 1.0f)/2)*viewport[2] + viewport[0];
  vertex[2].y = ((1.0f - (Y/W))/2)*viewport[3] + viewport[1];

  X = *width; Y = 0.0f; Z = 0.0f; W = 1.0f;
  cogl_matrix_transform_point (&mod_mtx, &X, &Y, &Z, &W);
  cogl_matrix_transform_point (&proj_mtx, &X, &Y, &Z, &W);
  vertex[3].x = (((X/W) + 1.0f)/2)*viewport[2] + viewport[0];
  vertex[3].y = ((1.0f - (Y/W))/2)*viewport[3] + viewport[1];

  float x_min, x_max, y_min, y_max;
  x_min = x_max = vertex[0].x;
  y_min = y_max = vertex[0].y;
  int i = 0;
  for (i = 1; i < 4; i++)
    {
      if (x_min > vertex[i].x)
        x_min = vertex[i].x;
      if (x_max < vertex[i].x)
        x_max = vertex[i].x;

      if (y_min > vertex[i].y)
        y_min = vertex[i].y;
      if (y_max < vertex[i].y)
        y_max = vertex[i].y;
    }

 /* X, y represents the position of the boundung box surounding the actor on screen 
  * while, width and height are the dimensions of the bounding box
  */

  *x = x_min;
  *y = y_min;

  *width =  x_max - x_min;
  *height = y_max - y_min;
}


