
/*
 * Texture Manager
 */


#include "TinyGL/gl_zgl.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>


static GLTexture *find_texture( GLContext *c, int h ){
	GLTexture *t;

	t=c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
	while (t!=NULL) {
		if (t->handle == h) return t;
		t=t->next;
	}
	return NULL;
}


static void free_texture( GLContext *c, int h ){
  GLTexture *t,**ht;
  GLImage *im;
  int i;

  t=find_texture(c,h);
  if (t->prev==NULL) {
    ht=&c->shared_state.texture_hash_table
      [t->handle % TEXTURE_HASH_TABLE_SIZE];
    *ht=t->next;
  } else {
    t->prev->next=t->next;
  }
  if (t->next!=NULL) t->next->prev=t->prev;

  for(i=0;i<MAX_TEXTURE_LEVELS;i++) {
    im=&t->images[i];
    if (im->pixmap != NULL) free(im->pixmap);
  }

  free(t);
}

GLTexture *alloc_texture( GLContext *c, int h ){
  GLTexture *t,**ht;
  
  t = (GLTexture*)calloc(1,sizeof(GLTexture));

  ht=&c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];

  t->next=*ht;
  t->prev=NULL;
  if (t->next != NULL) t->next->prev=t;
  *ht=t;

  t->handle=h;
  
  return t;
}


void glInitTextures( GLContext *c ){
  /* textures */

  c->texture_2d_enabled=0;
  c->current_texture=find_texture(c,0);
}

void glGenTextures( int n, unsigned int *textures ){
  GLContext *c=gl_get_context();
  int max,i;
  GLTexture *t;

  max=0;
  for(i=0;i<TEXTURE_HASH_TABLE_SIZE;i++) {
    t=c->shared_state.texture_hash_table[i];
    while (t!=NULL) {
      if (t->handle>max) max=t->handle;
      t=t->next;
    }

  }
  for(i=0;i<n;i++) {
    textures[i]=max+i+1;
  }
}


void glDeleteTextures( int n, const unsigned int *textures ){
  GLContext *c=gl_get_context();
  int i;
  GLTexture *t;

  for(i=0;i<n;i++) {
    t=find_texture(c,textures[i]);
    if (t!=NULL && t!=0) {
      if (t==c->current_texture) {
	glBindTexture(GL_TEXTURE_2D,0);
      }
      free_texture(c,textures[i]);
    }
  }
}


void glopBindTexture( GLContext *c, GLParam *p ){
  int        target  = p[1].i;
  int        texture = p[2].i;
  GLTexture *t;

  assert( target == GL_TEXTURE_2D && texture >= 0 );

  t=find_texture(c,texture);
  if (t==NULL) {
    t=alloc_texture(c,texture);
  }
  c->current_texture=t;
}

void glopTexImage2D( GLContext *c, GLParam *p ){
  int target=p[1].i;
  int level=p[2].i;
  int components=p[3].i;
  int width=p[4].i;
  int height=p[5].i;
  int border=p[6].i;
  int format=p[7].i;
  int type=p[8].i;
  void *pixels=p[9].p;
  GLImage *im;
  unsigned char *pixels1;
  int do_free;

  if (!(target == GL_TEXTURE_2D && level == 0 && components == 3 && 
        border == 0 && format == GL_RGB &&
        type == GL_UNSIGNED_BYTE)) {
    gl_fatal_error("glTexImage2D: combinaison of parameters not handled");
  }
  
  do_free=0;
  if (width != 256 || height != 256) {
    pixels1 = (unsigned char*)malloc(256 * 256 * 3);
    /* no interpolation is done here to respect the original image aliasing ! */
    gl_resizeImageNoInterpolate((unsigned char*)pixels1,256,256,(unsigned char*)pixels,width,height);
    do_free=1;
    width=256;
    height=256;
  } else {
    pixels1=(unsigned char*)pixels;
  }

  im=&c->current_texture->images[level];
  im->xsize=width;
  im->ysize=height;
  if (im->pixmap!=NULL) free(im->pixmap);
  im->pixmap=malloc(width*height*2);
  gl_convertRGB_to_5R6G5B((short unsigned int *)im->pixmap,pixels1,width,height);

  if (do_free) free(pixels1);
}


/* TODO: not all tests are done */
void glopTexEnv( GLContext *c, GLParam *p ){
  int target=p[1].i;
  int pname=p[2].i;
  int param=p[3].i;

  if (target != GL_TEXTURE_ENV) {
  error:
    gl_fatal_error("glTexParameter: unsupported option");
  }

  if (pname != GL_TEXTURE_ENV_MODE) goto error;

  if (param != GL_DECAL) goto error;
}

/* TODO: not all tests are done */
void glopTexParameter( GLContext *c, GLParam *p ){
  int target=p[1].i;
  int pname=p[2].i;
  int param=p[3].i;
  
  if (target != GL_TEXTURE_2D) {
  error:
    gl_fatal_error("glTexParameter: unsupported option");
  }

  switch(pname) {
  case GL_TEXTURE_WRAP_S:
  case GL_TEXTURE_WRAP_T:
    if (param != GL_REPEAT) goto error;
    break;
  }
}

void glopPixelStore( GLContext *c, GLParam *p ){
  int pname = p[1].i;
  int param = p[2].i;

  if (pname != GL_UNPACK_ALIGNMENT ||
      param != 1) {
    gl_fatal_error("glPixelStore: unsupported option");
  }
}
