/*
  texture.cc
  By Wayne Piekarski - wayne@cs.unisa.edu.au
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  See the file LICENSE included in this directory for more information.
*/


#include <GL/gl.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "texture.h"




/* Global variables for texture storage */
GLuint texid;
int tex_width, tex_height;
int raw_width, raw_height;



/* Calculate a power of two value for an integer */
int get_power_two (int in)
{
  int value;
  
  for (value = 1; value < in; value *= 2)
    ;
  
  return (value);
}




/* Initialise a texture map with the specified dimensions */
void init_texture (int in_width, int in_height)
{
  /* Fill in the buffer with a blank colour */
  raw_width = in_width;
  tex_width = get_power_two (in_width);
  raw_height = in_height;
  tex_height = get_power_two (in_height);
  int tex_bytes = tex_width * tex_height * 3;
  char round_buffer [tex_bytes];
  memset (round_buffer, 0xFF, tex_bytes);
  
  /* Debug */
  fprintf (stderr, "Initialising live texture map with size (%d, %d) rounded to (%d, %d)\n",
	   raw_width, raw_height, tex_width, tex_height);
  
  /* Tell OpenGL the length of each row in pixels */
  glPixelStorei (GL_UNPACK_ROW_LENGTH, tex_width);
  
  /* Allocate a texture id */
  glGenTextures (1, &texid);
  glBindTexture (GL_TEXTURE_2D, texid);
  
  /* Set texture drawing parameters */
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  
  /* Load in the image we previously cleared out */
  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, round_buffer);
  
  /* Turn texturing off for now */
  glDisable (GL_TEXTURE_2D);
}




/* Take in a new image of the camera dimensions and load it into the texture memory */
void change_texture (char *in_data)
{
  /* Enable texture mapping */
  glEnable (GL_TEXTURE_2D);
  
  /* Activate the previously allocated texid */
  glBindTexture (GL_TEXTURE_2D, texid);
  
  /* Configure the width of the incoming image */
  glPixelStorei (GL_UNPACK_ROW_LENGTH, raw_width);
  
  /* Get the input image and use OpenGL to apply it to the existing texture map, keeps things simple */
  glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, raw_width, raw_height, GL_RGB, GL_UNSIGNED_BYTE, in_data);
  
  /* Disable texturing */
  glDisable (GL_TEXTURE_2D);
}




/* Draw a 1.0 x 1.0 unit square using the texture map */
void square_texture (void)
{
  /* Enable texture mapping and our texid */
  glEnable (GL_TEXTURE_2D);
  glBindTexture (GL_TEXTURE_2D, texid);
  
  /* Calculate scaling factors for the texture coordinates to take into account the padding around the perimeter */
  double scale_width  = (double)raw_width  / (double)tex_width;
  double scale_height = (double)raw_height / (double)tex_height;
  
  /* Now draw the quad and map the texture to it */
  glBegin (GL_QUADS);
  glTexCoord2f (0.0, 0.0);                     glVertex2f (0.0, 1.0);
  glTexCoord2f (0.0, scale_height);            glVertex2f (0.0, 0.0);
  glTexCoord2f (scale_width, scale_height);    glVertex2f (1.0, 0.0);
  glTexCoord2f (scale_width, 0.0);             glVertex2f (1.0, 1.0);
  glEnd ();
  
  /* Disable texture mapping */
  glDisable (GL_TEXTURE_2D);
}





/* Draw an arbitrary four vertex polygon using the texture map. Make sure you take care that all the points are
   on the same plane and that the polygon is square, otherwise you will get a very badly warped image output. Also
   make sure you specify the points in a clockwise order to match the texture map! */
void polygon_texture (double x1, double y1, double z1,
		      double x2, double y2, double z2,
		      double x3, double y3, double z3,
		      double x4, double y4, double z4)
{
  /* Enable texture mapping and our texid */
  glEnable (GL_TEXTURE_2D);
  glBindTexture (GL_TEXTURE_2D, texid);
  
  /* Calculate scaling factors for the texture coordinates to take into account the padding around the perimeter */
  double scale_width  = (double)raw_width  / (double)tex_width;
  double scale_height = (double)raw_height / (double)tex_height;
  
  /* Now draw the quad and map the texture to it */
  glBegin (GL_QUADS);
  glTexCoord2f (0.0, 0.0);                     glVertex3f (x1, y1, z1);
  glTexCoord2f (0.0, scale_height);            glVertex3f (x2, y2, z2);
  glTexCoord2f (scale_width, scale_height);    glVertex3f (x3, y3, z3);
  glTexCoord2f (scale_width, 0.0);             glVertex3f (x4, y4, z4);
  glEnd ();
  
  /* Disable texture mapping */
  glDisable (GL_TEXTURE_2D);
}
