/*
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"

extern void R_InitBubble();

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

// create a simple checkerboard texture for the default
  r_notexture_mip = 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},
};
void R_InitParticleTexture (void)
{
  int   x,y;
  byte  data[8][8][4];

  //
  // particle texture
  //
  particletexture = texture_extension_number++;
    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;
    }
  }
  glTexImage2D (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);
}

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

Grab six views for environment mapping tests
===============
*/
void R_Envmap_f (void)
{
  byte  buffer[256*256*4];

  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 ();
}

/*
===============
R_Init
===============
*/
void R_Init (void)
{ 
  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 (&r_netgraph);

  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_finish);

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

  R_InitBubble();
  
  R_InitParticles ();
  R_InitParticleTexture ();

#ifdef GLTEST
  Test_Init ();
#endif

  netgraphtexture = texture_extension_number;
  texture_extension_number++;

  playertextures = texture_extension_number;
  texture_extension_number += MAX_CLIENTS;
}

/*
===============
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;
  byte  *original;
  unsigned  pixels[512*256], *out;
  unsigned  scaled_width, scaled_height;
  int     inwidth, inheight;
  int     tinwidth, tinheight;
  byte    *inrow;
  unsigned  frac, fracstep;
  player_info_t *player;
  extern  byte    player_8bit_texels[320*200];
  char s[512];

  GL_DisableMultitexture();

  player = &cl.players[playernum];
  if (!player->name[0])
    return;

  strcpy(s, Info_ValueForKey(player->userinfo, "skin"));
  COM_StripExtension(s, s);
  if (player->skin && !stricmp(s, player->skin->name))
    player->skin = NULL;

  if (player->_topcolor != player->topcolor ||
    player->_bottomcolor != player->bottomcolor || !player->skin) {
    player->_topcolor = player->topcolor;
    player->_bottomcolor = player->bottomcolor;

    top = player->topcolor;
    bottom = player->bottomcolor;
    top = (top < 0) ? 0 : ((top > 13) ? 13 : top);
    bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom);
    top *= 16;
    bottom *= 16;

    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
    //
    // real model width
    tinwidth = 296;
    tinheight = 194;

    if (!player->skin)
      Skin_Find(player);
    if ((original = Skin_Cache(player->skin)) != NULL) {
      //skin data width
      inwidth = 320;
      inheight = 200;
    } else {
      original = player_8bit_texels;
      inwidth = 296;
      inheight = 194;
    }


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

  #if 0
    s = 320*200;
    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);
  #endif

    scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512;
    scaled_height = 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;

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

      out2 = (byte *)pixels;
      memset(pixels, 0, sizeof(pixels));
      fracstep = tinwidth*0x10000/scaled_width;
      for (i=0 ; i<scaled_height ; i++, out2 += scaled_width)
      {
        inrow = original + inwidth*(i*tinheight/scaled_height);
        frac = fracstep >> 1;
        for (j=0 ; j<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);
      return;
    }

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

    out = pixels;
    memset(pixels, 0, sizeof(pixels));
    fracstep = tinwidth*0x10000/scaled_width;
    for (i=0 ; i<scaled_height ; i++, out += scaled_width)
    {
      inrow = original + inwidth*(i*tinheight/scaled_height);
      frac = fracstep >> 1;
      for (j=0 ; j<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;
      }
    }

    glTexImage2D (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);
  }
}

/*
===============
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)
{
  int     i;
  float   start, stop, time;

  glDrawBuffer  (GL_FRONT);
  glFinish ();

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

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

  glDrawBuffer  (GL_BACK);
  GL_EndRendering ();
}

void D_FlushCaches (void)
{
}


