/*----------------------------------------------------------------------------*
 | This file is part of DEU (Doom Editing Utilities), created by the DEU team:|
 | Raphael Quinet, Brendon Wyber, Ted Vessenes and others.  See README.1ST or |
 | the "about" dialog box for full credits.                                   |
 |                                                                            |
 | DEU is an open project: if you think that you can contribute, please join  |
 | the DEU team.  You will be credited for any code (or ideas) included in    |
 | the next version of the program.                                           |
 |                                                                            |
 | If you want to make any modifications and re-distribute them on your own,  |
 | you must follow the conditions of the DEU license.  Read the file LICENSE  |
 | in this directory or README.1ST in the top directory.  If do not have a    |
 | copy of these files, you can request them from any member of the DEU team, |
 | or by mail: Raphael Quinet, Rue des Martyrs 9, B-4550 Nandrin (Belgium).   |
 |                                                                            |
 | This program comes with absolutely no warranty.  Use it at your own risks! |
 *----------------------------------------------------------------------------*

 I_TEXTUR.C - Routines for displaying textures and sprites

 Created by Raphael Quinet, Trevor Phillips and Christian Johannes Schladetsch
*/

/*
   Note from CJS:
      DisplayPic() could be further speeded up by avoiding reading
      the same column numerous times. However, this approach involves
      exhorbitant memory usage for certain pictures.
*/

/* the includes */
#include "deu.h"
#include "g_mouse.h"
#include "g_gfx.h"
#include "d_main.h"
#include "d_misc.h"
#include "d_wads.h"
#include "i_menus.h"
#include "i_textur.h"

/*! should this really be a global variable? */
static int remap;


/*
   Display a floor or ceiling texture at coords x0, y0 and not beyond x1, y1.
*/

void DisplayFloorTexture(Int16 x0, Int16 y0, Int16 x1, Int16 y1, char *texname)
{
  MDirPtr    dir;
  UInt8 far *pixels;

  if (x1 - x0 != 63 || y1 - y0 != 63)
    ProgError("BUG: DEU doesn't resize the bitmaps yet... (%dx%d) instead of (64x64)",
              x1 - x0 + 1, y1 - y0 + 1);
  /* F_SKY1 is not a real texture */
  if (! strcmp(texname, "F_SKY1"))
    {
      SetColor(BLACK);
      DrawScreenBox(x0, y0, x1, y1);
      SetColor(DARKGRAY);
      DrawScreenLine(x0, y0, x1, y1);
      DrawScreenLine(x0, y1, x1, y0);
      SetColor(LIGHTGRAY);
      DrawScreenText((x0 + x1) / 2 - 20, (y0 + y1) / 2 - 5, "(SKY)");
      return;
    }
  dir = FindMasterDir(MasterDir, texname);
  if (dir == NULL)
    {
      SetColor(BLACK);
      DrawScreenBox(x0, y0, x1, y1);
      SetColor(DARKGRAY);
      DrawScreenLine(x0, y0, x1, y1);
      DrawScreenLine(x0, y1, x1, y0);
      return;
    }
  BasicWadSeek(dir->wadfile, dir->dir.start);
  pixels = (UInt8 far *) GetFarMemory(4096L);
  BasicWadRead(dir->wadfile, pixels, 4096L);
  DrawScreenBitmap(x0, y0, x1, y1, pixels);
  FreeFarMemory(pixels);
}



/*
   Display a thing picture.
*/
void DisplayThingPic(Int16 x0, Int16 y0, Int16 x1, Int16 y1, char *picname)
{
/*!
  remap = picname[0];
  if (remap < '1' || remap > '4' || strnicmp(&(picname[1]), "PLAY", 4))
    remap = 0;
  else
    {
      picname++;
      switch (remap)
        {
        case '1':
          remap =  0x00;
          break;
        case '2':
          remap = -0x10;
          break;
        case '3':
          remap = -0x30;
          break;
        case '4':
          remap =  0x40;
          break;
        }
    }
*/
  DisplayPic(x0, y0, x1, y1, picname, TRUE);
}



/*
   display a picture "picname" at coords x0, y0 and not beyond x1, y1
*/

void DisplayPic(Int16 x0, Int16 y0, Int16 x1, Int16 y1, char *picname,
                Bool center)
{
#ifndef DEBUG_NOPICTURES
  MDirPtr             dir;
  Int16               xsize, ysize, xofs, yofs;
  Int16               x, y;
  unsigned char huge *lpColumnData;
  unsigned char huge *lpColumn;
  long          huge *lpNeededOffsets;
  Int16               nColumns, nCurrentColumn;
  long                lCurrentOffset;
  Int16               fColumnInMemory;
  Int16               i, n;
  unsigned char       bRowStart, bColored;

  if (IsKeyPressed())
    return; /* speedup */

  /* Eg: For a picture of player x, send xPLAYA0.
     Send PLAYA1 for the normal picture. */

  dir = FindMasterDir(MasterDir, picname);
  if (dir == NULL)
    {
      SetColor(DARKGRAY);
      DrawScreenLine(x0, y0, x1, y1);
      DrawScreenLine(x0, y1, x1, y0);
      return;
    }
  BasicWadSeek(dir->wadfile, dir->dir.start);
  WadReadInt16(dir->wadfile, (UInt16 *)&xsize);
  WadReadInt16(dir->wadfile, (UInt16 *)&ysize);
  WadReadInt16(dir->wadfile, (UInt16 *)&xofs);
  WadReadInt16(dir->wadfile, (UInt16 *)&yofs);
/*! This buffer size causes a BUG when reading a texture from a PWAD: there is
    less than 60K to read, so BasicWadRead fails and DEU exits. */
#define TEX_COLUMNBUFFERSIZE    (60L * 1024L)
#define TEX_COLUMNSIZE          512L
  if (center)
    {
      xofs = (256 - xsize) / 2;
      yofs = (128 - ysize) / 2;
    }
  else
    xofs = yofs = 0;


  nColumns = xsize;

  lpColumnData    = (unsigned char huge*) GetMemory(TEX_COLUMNBUFFERSIZE);
  lpNeededOffsets = (long huge*) GetMemory(nColumns * 4L);

  BasicWadRead(dir->wadfile, lpNeededOffsets, nColumns * 4L);
#ifdef FAT_ENDIAN
  for (i = 0; i < nColumns; i++)
    lpNeededOffsets[i] = SwapInt32(lpNeededOffsets[i]);
#endif

  /* read first column data, and subsequent column data */
  BasicWadSeek(dir->wadfile, dir->dir.start + lpNeededOffsets[0]);
  /*! fix BUG */
  BasicWadRead(dir->wadfile, lpColumnData, TEX_COLUMNBUFFERSIZE);

  for (nCurrentColumn = 0; nCurrentColumn < nColumns; nCurrentColumn++)
    {
      lCurrentOffset = lpNeededOffsets[nCurrentColumn];
      fColumnInMemory = lCurrentOffset >= lpNeededOffsets[0] && lCurrentOffset < (long)(lpNeededOffsets[0] + TEX_COLUMNBUFFERSIZE - TEX_COLUMNSIZE);
      if (fColumnInMemory)
        {
          lpColumn = &lpColumnData[lCurrentOffset - lpNeededOffsets[0]];
        }
      else
        {
          lpColumn = (unsigned char huge*) GetFarMemory(TEX_COLUMNSIZE);
          BasicWadSeek(dir->wadfile, dir->dir.start + lCurrentOffset);
          /*! check byte order */
          BasicWadRead(dir->wadfile, lpColumn, TEX_COLUMNSIZE);
        }

      /* we now have the needed column data, one way or another, so write it */
      n = 1;
      bRowStart = lpColumn[0];
      while (bRowStart != 255 && n < TEX_COLUMNSIZE)
        {
          bColored = lpColumn[n];
          n += 2;                         /* skip over 'null' pixel in data */
          for (i = 0; i < bColored; i++)
            {
              x = x0 + xofs + nCurrentColumn;
              y = y0 + yofs + bRowStart + i;

              if (remap && lpColumn[i + n] >= 0x70 && lpColumn[i + n] < 0x80)
                lpColumn[i + n] += remap;

/*! The following instructions put the pixels on the screen.  This is
    extremely slow, but it is independant of the graphics system and
    number of colors.  This will of course be changed later, when this
    whole function is rewritten to return a bitmap (including all patches)
    and the picture is draw with one call to DrawScreenBitmap (which will
    also be optimized for each graphics system).
*/
              if (x >= x0 && y >= y0 && x <= x1 && y <= y1)
                {
                  SetDoomColor(lpColumn[i + n]);
                  DrawScreenPixel(x, y);
                }
            }
          n += bColored + 1;      /* skip over written pixels, and the 'null' one */
          bRowStart = lpColumn[n++];
        }
      if (bRowStart != 255)
        ProgError("BUG: bRowStart != 255.");

      if (!fColumnInMemory)
        FreeFarMemory(lpColumn);
    }
  FreeMemory(lpColumnData);
  FreeMemory(lpNeededOffsets);

#else /* DEBUG_NOPICTURES */
  SetColor(BLUE);
  DrawScreenRectangle(x0, y0, x1, y1);
  DrawScreenLine(x0, y0, x1, y1);
  DrawScreenLine(x0, y1, x1, y0);
  SetColor(LIGHTBLUE);
  DrawScreenText((x0 + x1) / 2 - 24, (y0 + y1) / 2 - 5, picname);
#endif /* DEBUG_NOPICTURES */
}



/*
   display a wall texture ("texture1" or "texture2" object) at coords x0, y0
*/

void DisplayWallTexture(Int16 x0, Int16 y0, Int16 x1, Int16 y1, char *texname)
{
#ifndef DEBUG_NOPICTURES
  MDirPtr  dir, pdir;
  long    *offsets;
  Int16    n, xsize, ysize, xofs, yofs, fields, pnameind, junk;
  long     numtex, texofs;
  char     tname[9], picname[9];

  /*!
  SetColor(BLUE);
  DrawScreenRectangle(x0, y0, x1, y1);
  DrawScreenLine(x0, y0, x1, y1);
  DrawScreenLine(x0, y1, x1, y0);
  return; 
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/

  if (IsKeyPressed())
    return; /* speedup */

  /* offset for texture we want. */
  texofs = 0;
  /* search for texname in texture1 names */
  dir = FindMasterDir(MasterDir, "TEXTURE1");
  BasicWadSeek(dir->wadfile, dir->dir.start);
  WadReadInt32(dir->wadfile, (UInt32 *)&numtex);
  /* read in the offsets for texture1 names and info. */
  offsets = (long *) GetMemory(numtex * sizeof(long));
  for (n = 0; n < numtex; n++)
    WadReadInt32(dir->wadfile, (UInt32 *)&(offsets[n]));
  for (n = 0; n < numtex && !texofs; n++)
    {
      BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
      BasicWadRead(dir->wadfile, &tname, 8);
      if (!strnicmp(tname, texname, 8))
        texofs = dir->dir.start + offsets[n];
    }
  FreeMemory(offsets);
  if (DoomVersion == 1 && texofs == 0)
    {
      /* search for texname in texture2 names */
      dir = FindMasterDir(MasterDir, "TEXTURE2");
      BasicWadSeek(dir->wadfile, dir->dir.start);
      WadReadInt32(dir->wadfile, (UInt32 *)&numtex);
      /* read in the offsets for texture2 names */
      offsets = (long *) GetMemory(numtex * sizeof(long));
      for (n = 0; n < numtex; n++)
        WadReadInt32(dir->wadfile, (UInt32 *)&(offsets[n]));
      for (n = 0; n < numtex && !texofs; n++)
        {
          BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
          BasicWadRead(dir->wadfile, &tname, 8);
          if (!strnicmp(tname, texname, 8))
            texofs = dir->dir.start + offsets[n];
        }
      FreeMemory(offsets);
    }

  /* texture name not found */
  if (texofs == 0)
    return;

  /* read the info for this texture */
  BasicWadSeek(dir->wadfile, texofs + 12L);
  WadReadInt16(dir->wadfile, (UInt16 *)&xsize);
  WadReadInt16(dir->wadfile, (UInt16 *)&ysize);
  BasicWadSeek(dir->wadfile, texofs + 20L);
  WadReadInt16(dir->wadfile, (UInt16 *)&fields);

  if (IsKeyPressed())
    return; /* speedup */

  /* display the texture size - yes, you can laugh at the way I did it... */
  /*F*/ /* Fix this once DBSC_OSPECIFICs are implimented */
  SetColor(LIGHTGRAY);
  DrawScreenBox(x0 + 288, y0 + 10, x0 + 288 + 7 * TEXT_W, y0 + 20);
  SetColor(BLACK);
  DrawScreenText(x0 + 288, y0 + 10, "%dx%d", xsize, ysize);

  if (x1 - x0 > xsize)
    x1 = x0 + xsize;
  if (y1 - y0 > ysize)
    y1 = y0 + ysize;
#ifdef OLD_PIC_CODE
  /* not really necessary, except when xofs or yofs < 0 */
  setviewport(x0, y0, x1, y1, TRUE);
#endif
  /* display the texture */
  for (n = 0; n < fields; n++)
    {
      BasicWadSeek(dir->wadfile, texofs + 22L + n * 10L);
      WadReadInt16(dir->wadfile, (UInt16 *)&xofs);
      WadReadInt16(dir->wadfile, (UInt16 *)&yofs);
      WadReadInt16(dir->wadfile, (UInt16 *)&pnameind);
      BasicWadRead(dir->wadfile, &junk, 2L);  /* Junk should = 1. */
      BasicWadRead(dir->wadfile, &junk, 2L);  /* Junk should = 0. */
      /* OK, now look up the pic's name in the PNAMES entry. */
      pdir = FindMasterDir(MasterDir, "PNAMES");
      BasicWadSeek(pdir->wadfile, pdir->dir.start + 4L + pnameind * 8L);
      BasicWadRead(pdir->wadfile, &picname, 8L);
      picname[8] = '\0';
#ifdef OLD_PIC_CODE
      /* coords changed because of the "setviewport" */
      DisplayPic(xofs, yofs, x1 - x0, y1 - y0, strupr(picname), FALSE);
#else
      if (xofs < 0)
        xofs = 0;
      if (yofs < 0)
        yofs = 0;
      DisplayPic(xofs + x0, yofs + y0, x1, y1, strupr(picname), FALSE);
#endif
    }
#ifdef OLD_PIC_CODE
  /* restore the normal viewport */
  setviewport(0, 0, ScrMaxX, ScrMaxY, TRUE);
#endif

#else /* DEBUG_NOPICTURES */
  SetColor(BLUE);
  DrawScreenRectangle(x0, y0, x1, y1);
  DrawScreenLine(x0, y0, x1, y1);
  DrawScreenLine(x0, y1, x1, y0);
  SetColor(LIGHTBLUE);
  DrawScreenText((x0 + x1) / 2 - 24, (y0 + y1) / 2 - 5, texname);
#endif /* DEBUG_NOPICTURES */
}



/*
    Function to get the size of a wall texture
*/

void GetWallTextureSize(Int16 *xsize_r, Int16 *ysize_r, char *texname)
{
  MDirPtr  dir;      /* pointer in main directory to texname */
  long    *offsets;  /* array of offsets to texture names */
  Int16    n;        /* general counter */
  long     numtex;   /* number of texture names in TEXTURE* list */
  long     texofs;   /* offset in doom.wad for the texture data */
  char     tname[9]; /* texture name */

  /* offset for texture we want. */
  texofs = 0;
  /* search for texname in texture1 names */
  dir = FindMasterDir(MasterDir, "TEXTURE1");
  BasicWadSeek(dir->wadfile, dir->dir.start);
  WadReadInt32(dir->wadfile, (UInt32 *)&numtex);
  /* read in the offsets for texture1 names and info. */
  offsets = (long *)GetMemory(numtex * sizeof(long));
  for (n = 0; n < numtex; n++)
    WadReadInt32(dir->wadfile, (UInt32 *)&(offsets[n]));
  for (n = 0; n < numtex && !texofs; n++)
    {
      BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
      BasicWadRead(dir->wadfile, &tname, 8);
      if (!strnicmp(tname, texname, 8))
        texofs = dir->dir.start + offsets[n];
    }
  FreeMemory(offsets);
  if (DoomVersion == 1 && texofs == 0)
    {
      /* search for texname in texture2 names */
      dir = FindMasterDir(MasterDir, "TEXTURE2");
      BasicWadSeek(dir->wadfile, dir->dir.start);
      WadReadInt32(dir->wadfile, (UInt32 *)&numtex);
      /* read in the offsets for texture2 names */
      offsets = (long *)GetMemory(numtex * sizeof(long));
      for (n = 0; n < numtex; n++)
        WadReadInt32(dir->wadfile, (UInt32 *)&(offsets[n]));
      for (n = 0; n < numtex && !texofs; n++)
        {
          BasicWadSeek(dir->wadfile, dir->dir.start + offsets[n]);
          BasicWadRead(dir->wadfile, &tname, 8);
          if (!strnicmp(tname, texname, 8))
            texofs = dir->dir.start + offsets[n];
        }
      FreeMemory(offsets);
    }

  if (texofs != 0)
    {
      /* read the info for this texture */
      BasicWadSeek(dir->wadfile, texofs + 12L);
      WadReadInt16(dir->wadfile, (UInt16 *)xsize_r);
      WadReadInt16(dir->wadfile, (UInt16 *)ysize_r);
    }
  else
    {
      /* texture data not found */
      *xsize_r = -1;
      *ysize_r = -1;
    }
}



/*
   choose a floor or ceiling texture
*/

void ChooseFloorTexture(Int16 x0, Int16 y0, char *prompt, Int16 listsize, char **list, char *name)
{
  if (UseMouse)
    HideMousePointer(); 
  InputNameFromListWithFunc(x0, y0, prompt, listsize, list, 5, name, 64, 64, DisplayFloorTexture);
  if (UseMouse)
    ShowMousePointer();
}



/*
   choose a wall texture
*/

void ChooseWallTexture(Int16 x0, Int16 y0, char *prompt, Int16 listsize, char **list, char *name)
{
  if (UseMouse)
    HideMousePointer();
  InputNameFromListWithFunc(x0, y0, prompt, listsize, list, 11, name, 256, 128, DisplayWallTexture);
  if (UseMouse)
    ShowMousePointer();
}



/*
   function used by qsort to sort the sprite names
*/
int SortSprites(const void *a, const void *b)
{
  return strcmp(*((char **)a), *((char **)b));
}



/*
   choose a "sprite"
*/

void ChooseSprite(Int16 x0, Int16 y0, char *prompt, char *sname)
{
  MDirPtr dir;
  Int16 n, listsize;
  char **list;
  char name[9];
  
  /* count the names */
  dir = FindMasterDir(MasterDir, "S_START");
  dir = dir->next;
  for (n = 0; dir && strcmp(dir->dir.name, "S_END"); n++)
    dir = dir->next;
  listsize = n;
  /* get the actual names from master dir. */
  dir = FindMasterDir(MasterDir, "S_START");
  dir = dir->next;
  list = (char**) GetMemory(listsize * sizeof(char *));
  for (n = 0; n < listsize; n++)
    {
      list[n] = (char*) GetMemory(9 * sizeof(char));
      strncpy(list[n], dir->dir.name, 8);
      list[n][8] = '\0';
      dir = dir->next;
    }
  qsort(list, listsize, sizeof(char *), SortSprites);
  if (sname != NULL)
    strncpy(name, sname, 8);
  else
    strcpy(name, list[0]);
  InputNameFromListWithFunc(x0, y0, prompt, listsize, list, 11, name, 256, 128, DisplayThingPic);
  for (n = 0; n < listsize; n++)
    FreeMemory(list[n]);
  FreeMemory(list);
}


/* end of file */
