#define	LIBQDISPLAY_CORE
#include "../include/libqdisplay.h"

struct texture **cachedFaces;

void InitFaceCache(__memBase)
{
  if (!(cachedFaces = (struct texture **)kmalloc(bspMem->shared.quake1.numfaces * sizeof(struct texture *))))
    Error(failed_memory, bspMem->shared.quake1.numfaces * sizeof(struct texture *), "face-cache");
}

static void GetMipMaps(__memBase, struct texture *Text, int TMipMap)
{
  struct dmiptexlump_t *mtl = (struct dmiptexlump_t *)bspMem->shared.quake1.dtexdata;
  struct mipmap *mip = (struct mipmap *)(bspMem->shared.quake1.dtexdata + mtl->dataofs[TMipMap]);
  short int i;

  if (mip->name[0] == WARP_MIPMAP) {
    switch (mip->name[1]) {
      case 'w':	Text->textureType = WATER_TYPE;	   break;		/* *w(ater)... */
      case 's':	Text->textureType = SLIME_TYPE;	   break;		/* *s(lime)... */
      case 'l':	Text->textureType = LAVA_TYPE;	   break;		/* *l(ava)... */
      case 't':	Text->textureType = TELEPORT_TYPE; break;		/* *t(eleport)... */
      default:	Text->textureType = OTHER_TYPE;	   break;		/* *... */
    }
    Text->mipMaps[MIPMAP_0].rawBody.width = WARP_X;
    Text->mipMaps[MIPMAP_0].rawBody.height = WARP_Y;
    Text->mipMaps[MIPMAP_0].rawBody.size = WARP_X * WARP_Y;
    Text->mipMaps[MIPMAP_0].newBody.width = WARP_X;
    Text->mipMaps[MIPMAP_0].newBody.height = WARP_Y;
    Text->mipMaps[MIPMAP_0].newBody.size = WARP_X * WARP_Y;
    Text->textGradient.u = 0;
    Text->textGradient.v = 0;
  }
  else if (!__strncmp(mip->name, SKY_MIPMAP, 3)) {
    Text->textureType = SKY_TYPE;
    Text->mipMaps[MIPMAP_0].rawBody.width = SKY_X;
    Text->mipMaps[MIPMAP_0].rawBody.height = SKY_Y;
    Text->mipMaps[MIPMAP_0].rawBody.size = SKY_X * SKY_Y;
    Text->mipMaps[MIPMAP_0].newBody.width = SKY_X;
    Text->mipMaps[MIPMAP_0].newBody.height = SKY_Y;
    Text->mipMaps[MIPMAP_0].newBody.size = SKY_X * SKY_Y;
    Text->textGradient.u = 0;
    Text->textGradient.v = 0;
  }
  else {
    if (mip->name[0] == ANIM_MIPMAP)
      Text->textureType = ANIM_TYPE;
    else
      Text->textureType = WALL_TYPE;

    Text->mipMaps[MIPMAP_0].rawBody.width = LittleLong(mip->width);
    Text->mipMaps[MIPMAP_0].rawBody.height = LittleLong(mip->height);
    Text->mipMaps[MIPMAP_0].rawBody.size = Text->mipMaps[MIPMAP_0].rawBody.width * Text->mipMaps[MIPMAP_0].rawBody.height;
    Text->mipMaps[MIPMAP_0].newBody.width = Text->faceExtent.u10;
    Text->mipMaps[MIPMAP_0].newBody.height = Text->faceExtent.v10;
    Text->mipMaps[MIPMAP_0].newBody.size = Text->mipMaps[MIPMAP_0].newBody.width * Text->mipMaps[MIPMAP_0].newBody.height;
    Text->textGradient.u = Text->faceExtent.u0;
    Text->textGradient.v = Text->faceExtent.v0;
  }
  Text->mipMaps[MIPMAP_0].rawBody.data = (unsigned char *)mip + LittleLong(mip->offsets[MIPMAP_0]);
  Text->mipMaps[MIPMAP_0].step = 16;
  Text->mipMaps[MIPMAP_0].shift = 4;
  Text->mipMaps[MIPMAP_0].row = Text->mipMaps[MIPMAP_0].newBody.width - Text->mipMaps[MIPMAP_0].step;
  if ((Text->mipMaps[MIPMAP_0].y = Text->faceExtent.v0 % Text->mipMaps[MIPMAP_0].rawBody.height) < 0)
    Text->mipMaps[MIPMAP_0].y += Text->mipMaps[MIPMAP_0].rawBody.height;
  if ((Text->mipMaps[MIPMAP_0].x0 = Text->faceExtent.u0 % Text->mipMaps[MIPMAP_0].rawBody.width) < 0)
    Text->mipMaps[MIPMAP_0].x0 += Text->mipMaps[MIPMAP_0].rawBody.width;
  Text->mipMaps[MIPMAP_0].rescale = scalw((double)(8), -3);	/* / 8.0; */

  for (i = MIPMAP_1; i < MIPMAP_MAX; i++) {			/* an enum cycles, produces no overflow */
    Text->mipMaps[i].rawBody.data = (unsigned char *)mip + LittleLong(mip->offsets[i]);
    Text->mipMaps[i].rawBody.width = Text->mipMaps[MIPMAP_0].rawBody.width >> i;
    Text->mipMaps[i].rawBody.height = Text->mipMaps[MIPMAP_0].rawBody.height >> i;
    Text->mipMaps[i].rawBody.size = Text->mipMaps[MIPMAP_0].rawBody.size >> i >> i;
    Text->mipMaps[i].newBody.width = Text->mipMaps[MIPMAP_0].newBody.width >> i;
    Text->mipMaps[i].newBody.height = Text->mipMaps[MIPMAP_0].newBody.height >> i;
    Text->mipMaps[i].newBody.size = Text->mipMaps[MIPMAP_0].newBody.size >> i >> i;
    Text->mipMaps[i].step = 16 >> i;
    Text->mipMaps[i].shift = 4 - i;
    Text->mipMaps[i].row = Text->mipMaps[i].newBody.width - Text->mipMaps[i].step;
    Text->mipMaps[i].rescale = scalw(Text->mipMaps[MIPMAP_0].rescale, -i);
    Text->mipMaps[i].y = Text->mipMaps[MIPMAP_0].y >> i;
    Text->mipMaps[i].x0 = Text->mipMaps[MIPMAP_0].x0 >> i;
  }

  {
    int j;
    int r = 0, g = 0, b = 0;
    struct rgb rgb;
    unsigned char *textFlow = Text->mipMaps[MIPMAP_0].rawBody.data;

    for (j = Text->mipMaps[MIPMAP_0].rawBody.size; j > 0; j--) {
      int k = (int)*textFlow++;

      r += cachedPalette[k].r;
      g += cachedPalette[k].g;
      b += cachedPalette[k].b;
    }
    rgb.r = r / Text->mipMaps[MIPMAP_0].rawBody.size;
    rgb.g = g / Text->mipMaps[MIPMAP_0].rawBody.size;
    rgb.b = b / Text->mipMaps[MIPMAP_0].rawBody.size;
    Text->textureColor = (unsigned char)Match(&rgb, cachedPalette);
  }

  if (!(Text->tiled = (void *)kmalloc((Text->mipMaps[MIPMAP_0].newBody.size + 1) * localDim.frameBPP)))
    Error(failed_memory, (Text->mipMaps[MIPMAP_0].newBody.size + 1) * localDim.frameBPP, mip->name);

  Text->texChanged = TRUE;					/* be very safe */
}

static void GetExtents(__memBase, int face, struct texture *Text, int TInfo)
{
  float uv[32][2], *u, *v, umin, umax, vmin, vmax;
  short int i, n = bspMem->shared.quake1.dfaces[face].numedges;
  int *se = &bspMem->shared.quake1.dsurfedges[bspMem->shared.quake1.dfaces[face].firstedge + n];

  u = bspMem->shared.quake1.texinfo[TInfo].vecs[0];
  v = bspMem->shared.quake1.texinfo[TInfo].vecs[1];

  for (i = n - 1; i >= 0; --i) {
    int j = *--se;
    float *loc;

    if (j < 0)
      loc = bspMem->shared.quake1.dvertexes[bspMem->shared.quake1.dedges[-j].v[1]].point;
    else
      loc = bspMem->shared.quake1.dvertexes[bspMem->shared.quake1.dedges[ j].v[0]].point;

    uv[i][0] = DotProduct(loc, u) + u[3];
    uv[i][1] = DotProduct(loc, v) + v[3];
  }
  umin = umax = uv[0][0];
  vmin = vmax = uv[0][1];
  for (i = n - 1; i >= 0; --i) {
    if (uv[i][0] < umin)
      umin = uv[i][0];
    else if (uv[i][0] > umax)
      umax = uv[i][0];
    if (uv[i][1] < vmin)
      vmin = uv[i][1];
    else if (uv[i][1] > vmax)
      vmax = uv[i][1];
  }

  Text->faceExtent.u0 = (int)(umin) & ~15;
  Text->faceExtent.v0 = (int)(vmin) & ~15;
  Text->faceExtent.u1 = (int)(ceil(scalw(umax, -4))) << 4;	/* / 16 */
  Text->faceExtent.v1 = (int)(ceil(scalw(vmax, -4))) << 4;	/* / 16 */
  Text->faceExtent.u10 = Text->faceExtent.u1 - Text->faceExtent.u0;
  Text->faceExtent.v10 = Text->faceExtent.v1 - Text->faceExtent.v0;

  if ((bspMem->shared.quake1.dfaces[face].lightofs != -1) && (bspMem->shared.quake1.lightdatasize)) {
    Text->lightdata = &bspMem->shared.quake1.dlightdata[bspMem->shared.quake1.dfaces[face].lightofs];

    Text->lightmap.width = ((Text->faceExtent.u10) >> 4) + 1;
    Text->lightmap.height = ((Text->faceExtent.v10) >> 4) + 1;
    Text->lightmap.size = Text->lightmap.width * Text->lightmap.height;
    if (!(Text->lightmap.data = (unsigned char *)kmalloc((Text->lightmap.size + 1) * sizeof(int))))
      Error(failed_memory, (Text->lightmap.size + 1) * sizeof(int), "lightmap");

    for (i = 0; i < MAXLIGHTMAPS; i++) {
      short int lightStyle;

      lightStyle = (short int)bspMem->shared.quake1.dfaces[face].styles[i];

      if (lightStyle == 255)
	break;
      if (lightStyle > 11)
	lightStyle = 0;

      Text->lightSString[i] = &lightstyleStrings[lightStyle][0];
      Text->lightSLength[i] = lightstyleLengths[lightStyle];
    }
  }
  else
    Text->lightdata = 0;

  Text->texChanged = TRUE;					/* be very safe */
}

static void GetGradients(__memBase, int face, struct texture *Text, int TInfo)
{
  struct dplane_t *plane = Text->textGradient.plane = &bspMem->shared.quake1.dplanes[bspMem->shared.quake1.dfaces[face].planenum];
  float dot, dot0, dot1;
  vec3_t norm;
  float *vec0 = bspMem->shared.quake1.texinfo[TInfo].vecs[0];
  float *vec1 = bspMem->shared.quake1.texinfo[TInfo].vecs[1];

  CrossProduct(vec0, vec1, norm);

  dot = DotProduct(norm, plane->normal);

  if ((dot0 = -DotProduct(vec0, plane->normal) / dot))		/* for setup_uv_vector */
    VectorMA(vec0, dot0, norm, Text->textGradient.uv0);
  else
    VectorCopy(vec0, Text->textGradient.uv0);

  if ((dot1 = -DotProduct(vec1, plane->normal) / dot))		/* for setup_uv_vector */
    VectorMA(vec1, dot1, norm, Text->textGradient.uv1);
  else
    VectorCopy(vec1, Text->textGradient.uv1);

  VectorScale(norm, (plane->dist / dot), Text->textGradient.scaled);	/* for setup_origin_vector; */

  Text->textGradient.u -= vec0[3];
  Text->textGradient.v -= vec1[3];
  Text->texChanged = TRUE;					/* be very safe */
}

struct texture *CacheFace(__memBase, int face)
{
  struct texture *Text;
  int TInfo = bspMem->shared.quake1.dfaces[face].texinfo;
  int TMipMap = bspMem->shared.quake1.texinfo[TInfo].miptex;

  if (!(Text = (struct texture *)kmalloc(sizeof(struct texture))))
    Error(failed_memory, sizeof(struct texture), "texture cache");

  GetExtents(bspMem, face, Text, TInfo);
  GetMipMaps(bspMem, Text, TMipMap);
  GetGradients(bspMem, face, Text, TInfo);
  Text->texChanged = TRUE;					/* be very safe */

  return Text;
}
