/*
Copyright (C) 1996-1997 Id Software, Inc.

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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// r_misc.c

#include "quakedef.h"



/*
==================
R_InitTextures
==================
*/
void	R_InitTextures (void)
{
  int		x,y, m;
  byte	*dest;

// create a simple checkerboard texture for the default
  r_notexture_mip = (texture_t*) Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture");

  r_notexture_mip->width = r_notexture_mip->height = 16;
  r_notexture_mip->offsets[0] = sizeof(texture_t);
  r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;
  r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;
  r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;

  for (m=0 ; m<4 ; m++)
  {
    dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
    for (y=0 ; y< (16>>m) ; y++)
      for (x=0 ; x< (16>>m) ; x++)
      {
        if (  (y< (8>>m) ) ^ (x< (8>>m) ) )
          *dest++ = 0;
        else
          *dest++ = 0xff;
      }
  }
}

byte	dottexture[8][8] =
{
  {0,1,1,0,0,0,0,0},
  {1,1,1,1,0,0,0,0},
  {1,1,1,1,0,0,0,0},
  {0,1,1,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0},
};
// Initialize particle texture, can be called multiple times.
void R_InitParticleTexture2 (void)
{
  int		x,y;
  byte	data[8][8][4];

  //
  // particle texture
  //
    GL_Bind(particletexture);

  for (x=0 ; x<8 ; x++)
  {
    for (y=0 ; y<8 ; y++)
    {
      data[y][x][0] = 255;
      data[y][x][1] = 255;
      data[y][x][2] = 255;
      data[y][x][3] = dottexture[x][y]*255;
    }
  }
  glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void R_InitParticleTexture (void)
{
  particletexture = texture_extension_number++;
  R_InitParticleTexture2();
}

/*
===============
R_Envmap_f

Grab six views for environment mapping tests
===============
*/
void R_Envmap_f (void)
{
#ifdef USE_OPENGLES
  // Not implemented
#else
  byte	buffer[256*256*4];
  char	name[1024];

  glDrawBuffer  (GL_FRONT);
  glReadBuffer  (GL_FRONT);
  envmap = true;

  r_refdef.vrect.x = 0;
  r_refdef.vrect.y = 0;
  r_refdef.vrect.width = 256;
  r_refdef.vrect.height = 256;

  r_refdef.viewangles[0] = 0;
  r_refdef.viewangles[1] = 0;
  r_refdef.viewangles[2] = 0;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env0.rgb", buffer, sizeof(buffer));

  r_refdef.viewangles[1] = 90;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env1.rgb", buffer, sizeof(buffer));

  r_refdef.viewangles[1] = 180;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env2.rgb", buffer, sizeof(buffer));

  r_refdef.viewangles[1] = 270;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env3.rgb", buffer, sizeof(buffer));

  r_refdef.viewangles[0] = -90;
  r_refdef.viewangles[1] = 0;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env4.rgb", buffer, sizeof(buffer));

  r_refdef.viewangles[0] = 90;
  r_refdef.viewangles[1] = 0;
  GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
  R_RenderView ();
  glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  COM_WriteFile ("env5.rgb", buffer, sizeof(buffer));

  envmap = false;
  glDrawBuffer  (GL_BACK);
  glReadBuffer  (GL_BACK);
  GL_EndRendering ();
#endif
}

/*
===============
R_Init
===============
*/
void R_Init (void)
{
  extern byte *hunk_base;
  extern cvar_t gl_finish;

  Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);
  Cmd_AddCommand ("envmap", R_Envmap_f);
  Cmd_AddCommand ("pointfile", R_ReadPointFile_f);

  Cvar_RegisterVariable (&r_norefresh);
  Cvar_RegisterVariable (&r_lightmap);
  Cvar_RegisterVariable (&r_fullbright);
  Cvar_RegisterVariable (&r_drawentities);
  Cvar_RegisterVariable (&r_drawviewmodel);
  Cvar_RegisterVariable (&r_shadows);
  Cvar_RegisterVariable (&r_mirroralpha);
  Cvar_RegisterVariable (&r_wateralpha);
  Cvar_RegisterVariable (&r_dynamic);
  Cvar_RegisterVariable (&r_novis);
  Cvar_RegisterVariable (&r_speeds);

  Cvar_RegisterVariable (&gl_finish);
  Cvar_RegisterVariable (&gl_clear);
  Cvar_RegisterVariable (&gl_texsort);

   if (gl_mtexable)
    Cvar_SetValue ("gl_texsort", 0.0);

  Cvar_RegisterVariable (&gl_cull);
  Cvar_RegisterVariable (&gl_smoothmodels);
  Cvar_RegisterVariable (&gl_affinemodels);
  Cvar_RegisterVariable (&gl_polyblend);
  Cvar_RegisterVariable (&gl_flashblend);
  Cvar_RegisterVariable (&gl_playermip);
  Cvar_RegisterVariable (&gl_nocolors);

  Cvar_RegisterVariable (&gl_keeptjunctions);
  Cvar_RegisterVariable (&gl_reporttjunctions);

  Cvar_RegisterVariable (&gl_doubleeyes);

  R_InitParticles ();
  R_InitParticleTexture ();

#ifdef GLTEST
  Test_Init ();
#endif

  playertextures = texture_extension_number;
  texture_extension_number += 16;
}

/*
===============
R_TranslatePlayerSkin

Translates a skin texture by the per-player color lookup
===============
*/
void R_TranslatePlayerSkin (int playernum)
{
  int		top, bottom;
  byte	translate[256];
  unsigned	translate32[256];
  int		i, j, s;
  model_t	*model;
  aliashdr_t *paliashdr;
  byte	*original;
  unsigned*	pixels;
  unsigned    *out;
  unsigned	scaled_width, scaled_height;
  int			inwidth, inheight;
  byte		*inrow;
  unsigned	frac, fracstep;
  extern	byte		**player_8bit_texels_tbl;

  GL_DisableMultitexture();

  top = cl.scores[playernum].colors & 0xf0;
  bottom = (cl.scores[playernum].colors &15)<<4;

  for (i=0 ; i<256 ; i++)
    translate[i] = i;

  for (i=0 ; i<16 ; i++)
  {
    if (top < 128)	// the artists made some backwards ranges.  sigh.
      translate[TOP_RANGE+i] = top+i;
    else
      translate[TOP_RANGE+i] = top+15-i;

    if (bottom < 128)
      translate[BOTTOM_RANGE+i] = bottom+i;
    else
      translate[BOTTOM_RANGE+i] = bottom+15-i;
  }

  //
  // locate the original skin pixels
  //
  currententity = &cl_entities[1+playernum];
  model = currententity->model;
  if (!model)
    return;		// player doesn't have a model yet
  if (model->type != mod_alias)
    return; // only translate skins on alias models

  paliashdr = (aliashdr_t *)Mod_Extradata (model);
  s = paliashdr->skinwidth * paliashdr->skinheight;
  if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins) {
    Con_Printf("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum);
    original = (byte *)paliashdr + paliashdr->texels[0];
  } else
    original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum];
  if (s & 3)
    Sys_Error ("R_TranslateSkin: s&3");

  inwidth = paliashdr->skinwidth;
  inheight = paliashdr->skinheight;

  // because this happens during gameplay, do it fast
  // instead of sending it through gl_upload 8
    GL_Bind(playertextures + playernum);

#if 0
  byte	translated[320*200];

  for (i=0 ; i<s ; i+=4)
  {
    translated[i] = translate[original[i]];
    translated[i+1] = translate[original[i+1]];
    translated[i+2] = translate[original[i+2]];
    translated[i+3] = translate[original[i+3]];
  }


  // don't mipmap these, because it takes too long
  GL_Upload8 (translated, paliashdr->skinwidth, paliashdr->skinheight, false, false, true);
#else
  scaled_width = (unsigned int) (gl_max_size.value < 512 ? gl_max_size.value : 512);
  scaled_height = (unsigned int) (gl_max_size.value < 256 ? gl_max_size.value : 256);

  // allow users to crunch sizes down even more if they want
  scaled_width >>= (int)gl_playermip.value;
  scaled_height >>= (int)gl_playermip.value;

#define PIXEL_COUNT (512*256)
#define PIXELS_SIZE (PIXEL_COUNT * sizeof(unsigned))

  pixels = (unsigned*) malloc(PIXELS_SIZE);
  if(!pixels)
  {
    Sys_Error("Out of memory.");
  }

  if (VID_Is8bit()) { // 8bit texture upload
    byte *out2;

    out2 = (byte *)pixels;
    memset(pixels, 0, PIXELS_SIZE);
    fracstep = inwidth*0x10000/scaled_width;
    for (i=0 ; i< (int) scaled_height ; i++, out2 += scaled_width)
    {
      inrow = original + inwidth*(i*inheight/scaled_height);
      frac = fracstep >> 1;
      for (j=0 ; j< (int) scaled_width ; j+=4)
      {
        out2[j] = translate[inrow[frac>>16]];
        frac += fracstep;
        out2[j+1] = translate[inrow[frac>>16]];
        frac += fracstep;
        out2[j+2] = translate[inrow[frac>>16]];
        frac += fracstep;
        out2[j+3] = translate[inrow[frac>>16]];
        frac += fracstep;
      }
    }

    GL_Upload8_EXT ((byte *)pixels, scaled_width, scaled_height, false, false);
  }
  else
  {

    for (i=0 ; i<256 ; i++)
      translate32[i] = d_8to24table[translate[i]];

    out = pixels;
    fracstep = inwidth*0x10000/scaled_width;
    for (i=0 ; i< (int) scaled_height ; i++, out += scaled_width)
    {
      inrow = original + inwidth*(i*inheight/scaled_height);
      frac = fracstep >> 1;
      for (j=0 ; j< (int) scaled_width ; j+=4)
      {
        out[j] = translate32[inrow[frac>>16]];
        frac += fracstep;
        out[j+1] = translate32[inrow[frac>>16]];
        frac += fracstep;
        out[j+2] = translate32[inrow[frac>>16]];
        frac += fracstep;
        out[j+3] = translate32[inrow[frac>>16]];
        frac += fracstep;
      }
    }
    glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  }
#endif
  free(pixels);
}


/*
===============
R_NewMap
===============
*/
void R_NewMap (void)
{
  int		i;

  for (i=0 ; i<256 ; i++)
    d_lightstylevalue[i] = 264;		// normal light value

  memset (&r_worldentity, 0, sizeof(r_worldentity));
  r_worldentity.model = cl.worldmodel;

// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
  for (i=0 ; i<cl.worldmodel->numleafs ; i++)
    cl.worldmodel->leafs[i].efrags = NULL;

  r_viewleaf = NULL;
  R_ClearParticles ();

  GL_BuildLightmaps ();

  // identify sky texture
  skytexturenum = -1;
  mirrortexturenum = -1;
  for (i=0 ; i<cl.worldmodel->numtextures ; i++)
  {
    if (!cl.worldmodel->textures[i])
      continue;
    if (!Q_strncmp(cl.worldmodel->textures[i]->name,"sky",3) )
      skytexturenum = i;
    if (!Q_strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) )
      mirrortexturenum = i;
     cl.worldmodel->textures[i]->texturechain = NULL;
  }
#ifdef QUAKE2
  R_LoadSkys ();
#endif
}


/*
====================
R_TimeRefresh_f

For program optimization
====================
*/
void R_TimeRefresh_f (void)
{
#ifdef USE_OPENGLES
  // Not implemented
  Con_Printf("TimeRefresh not implemented.\n");
#else
  int			i;
  float		start, stop, time;
  int			startangle;
  vrect_t		vr;

  glDrawBuffer  (GL_FRONT);
  glFinish ();

  start = Sys_FloatTime ();
  for (i=0 ; i<128 ; i++)
  {
    r_refdef.viewangles[1] = i/128.0*360.0;
    R_RenderView ();
  }

  glFinish ();
  stop = Sys_FloatTime ();
  time = stop-start;
  Con_Printf ("%f seconds (%f fps)\n", time, 128/time);

  glDrawBuffer  (GL_BACK);
  GL_EndRendering ();
#endif
}

void D_FlushCaches (void)
{
}