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

 G_GFXWAT.C - Graphical routines for Watcom C

 Functions written by Aaron Faircloth
*/

/* the includes */
#include "deu.h"
#include <math.h>
#include <dos.h>
#include <bios.h>
#include <graph.h>
#include "d_misc.h"
#include "d_config.h"
#include "d_wads.h"
#include "g_mouse.h"
#include "g_colcnv.h"
#include "g_gfx.h"

/* the global variables */
Int16  OrigX;         /* the X origin */
Int16  OrigY;         /* the Y origin */
float  Scale;         /* the scale value */
UInt16 PointerX;      /* X position of pointer */
UInt16 PointerY;      /* Y position of pointer */
UInt16 ScrMaxX;       /* maximum X screen coord */
UInt16 ScrMaxY;       /* maximum Y screen coord */
UInt16 ScrCenterX;    /* X coord of screen center */
UInt16 ScrCenterY;    /* Y coord of screen center */

/* private */
static int current_color;   /* current color */
/* almost private: shared with g_colcnv.c */
int        num_colors = -1; /* number of colors available */

/*
   Initialise the graphics display.
*/

void InitGfx(void)
{
  static Bool first_time = TRUE;
  static int  gdriver;
  static int  gmode;
  int         errorcode;
  struct videoconfig vc;

  printf("Switching to graphics mode...\n");
  if (Config.videoMode < 0)
    Config.videoMode = 0;

  /*! should add support for 16 colors modes too */
  if (VideoMode == 0)
    _setvideomode(_VRES256COLOR);
  else if (VideoMode == 1) 
    _setvideomode(_SVRES256COLOR);
  else
    _setvideomode(_XRES256COLOR);

  if (_grstatus() != 0)
    ProgError( "Error Initializing graphics mode: %d!\n", VideoMode); 

  /*! should get the real number of colors */
  num_colors = 256;
  if (num_colors >= 256)
    SetDoomPalette(0);
  _getvideoconfig(&vc);
  SetColor(WHITE);
  _setbkcolor(TranslateToDoomColor(BLACK));
  /*! setlinestyle(0, 0, 1); */
  /*! settextstyle(0, 0, 1); */
  ScrMaxX = vc.numxpixels;
  ScrMaxY = vc.numypixels;
  ScrCenterX = ScrMaxX / 2;
  ScrCenterY = ScrMaxY / 2;
  /* Initialise the mouse driver */
  InitMouseDriver();
  if (UseMouse)
    {
      ResetMouseLimits();
      SetMouseCoords(ScrCenterX, ScrCenterY);
      ShowMousePointer();
    }
}



/*
   Terminate the graphics display.
*/

void TermGfx(void)
{
  if (num_colors > 0)
    {
      if (UseMouse)
        HideMousePointer();
      _setvideomode(_DEFAULTMODE);
      num_colors = -1;
    }
}



/*
   Returns TRUE if DEU is in graphics mode, FALSE if it is in text mode.
*/

Bool InGfxMode(void)
{
  return (num_colors > 0);
}



/*
   Clear the screen.
*/

void ClearScreen(void)
{
  _clearscreen(_GCLEARSCREEN);
}


/*
   Set the line drawing mode: normal or XOR.
*/

void SetLineDrawingMode(int mode)
{
  switch (mode)
    {
    case DRAW_NORMAL:
      _setplotaction(_GPSET);
      break;
    case DRAW_XOR:
      _setplotaction(_GXOR);
      break;
    default:
      ProgError("BUG: Invalid line drawing mode: %d", mode);
      break;
    }
}



/*
   Set the line style: width and pattern (solid, dotted, dashed).
*/

void SetLinePatternAndWidth(int pattern, int width)
{
  if (width != 1 && width != 3)
    ProgError("BUG: Invalid line width: %d", width);
  switch (pattern)
    {
    case SOLID_PATTERN:
      /*! setlinestyle(SOLID_LINE, 0, width); */
      break;
    case DOTTED_PATTERN:
      /*! setlinestyle(DOTTED_LINE, 0, width); */
      break;
    case DASHED_PATTERN:
      /*! setlinestyle(DASHED_LINE, 0, width); */
      break;
    default:
      ProgError("BUG: Invalid line pattern: %d", pattern);
      break;
    }
}



/*
   Draw a line on the screen from map coords.
*/

void DrawMapLine(Int16 mapXstart, Int16 mapYstart, Int16 mapXend, Int16 mapYend)
{
  _moveto(SCREENX(mapXstart), SCREENY(mapYstart));
  _lineto(SCREENX(mapXend),   SCREENY(mapYend));
}



/*
   Draw a circle on the screen from map coords.
*/

void DrawMapCircle(Int16 mapXcenter, Int16 mapYcenter, Int16 mapRadius)
{
  Int16 rad = (Int16)(mapRadius * Scale);

  _ellipse(_GBORDER, SCREENX(mapXcenter) - rad, SCREENY(mapYcenter) - rad,
	   SCREENX(mapXcenter) + rad, SCREENY(mapYcenter) + rad);
}



/*
   Draw an arrow on the screen from map coords.
*/

void DrawMapVector(Int16 mapXstart, Int16 mapYstart, Int16 mapXend, Int16 mapYend)
{
  Int16  scrXstart = SCREENX(mapXstart);
  Int16  scrYstart = SCREENY(mapYstart);
  Int16  scrXend   = SCREENX(mapXend);
  Int16  scrYend   = SCREENY(mapYend);
  double r         = hypot((double) (scrXstart - scrXend),
                           (double) (scrYstart - scrYend));
  Int16  scrXoff   = (r >= 1.0) ? (Int16) ((scrXstart - scrXend) * 8.0
                                           / r * Scale) : 0;
  Int16  scrYoff   = (r >= 1.0) ? (Int16) ((scrYstart - scrYend) * 8.0
                                           / r * Scale) : 0;

  _moveto(scrXstart, scrYstart);
  _lineto(scrXend, scrYend);
  scrXstart = scrXend + 2 * scrXoff;
  scrYstart = scrYend + 2 * scrYoff;
  _moveto(scrXstart - scrYoff, scrYstart + scrXoff);
  _lineto(scrXend, scrYend);
  _lineto(scrXstart + scrYoff, scrYstart - scrXoff);
}



/*
   Draw an arrow on the screen from map coords and angle (0 - 65535).
*/

void DrawMapArrow(Int16 mapXstart, Int16 mapYstart, UInt16 angle)
{
  Int16  mapXend   = mapXstart + (Int16) (50 * cos(angle / 10430.37835));
  Int16  mapYend   = mapYstart + (Int16) (50 * sin(angle / 10430.37835));
  Int16  scrXstart = SCREENX(mapXstart);
  Int16  scrYstart = SCREENY(mapYstart);
  Int16  scrXend   = SCREENX(mapXend);
  Int16  scrYend   = SCREENY(mapYend);
  double r         = hypot(scrXstart - scrXend, scrYstart - scrYend);
  Int16  scrXoff   = (r >= 1.0) ? (Int16) ((scrXstart - scrXend) * 8.0
                                           / r * Scale) : 0;
  Int16  scrYoff   = (r >= 1.0) ? (Int16) ((scrYstart - scrYend) * 8.0
                                           / r * Scale) : 0;

  _moveto(scrXstart, scrYstart);
  _lineto(scrXend, scrYend);
  scrXstart = scrXend + 2 * scrXoff;
  scrYstart = scrYend + 2 * scrYoff;
  _moveto(scrXstart - scrYoff, scrYstart + scrXoff);
  _lineto(scrXend, scrYend);
  _lineto(scrXstart + scrYoff, scrYstart - scrXoff);
}



/*
   Draw a pixel on the screen.
*/

void DrawScreenPixel(Int16 X, Int16 Y)
{
  _setpixel(X, Y);
}



/*
   Draw a line on the screen from screen coords.
*/

void DrawScreenLine(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  _moveto(Xstart, Ystart);
  _lineto(Xend, Yend);
}



/*
   Draw a rectangle on the screen from screen coords.
*/

void DrawScreenRectangle(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  /*! _rectangle(_G??????, Xstart, Ystart, Xend, Yend); */
}



/*
   Draw a filled in box on the screen from screen coords.
*/

void DrawScreenBox(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  _rectangle(_GFILLINTERIOR, Xstart, Ystart, Xend, Yend);
}



/*
   Draw a filled-in 3D-box on the screen from screen coords.
*/

void DrawScreenBox3D(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  SetColor(LIGHTGRAY);
  _rectangle(_GFILLINTERIOR, Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(DARKGRAY);
  _moveto(Xstart, Yend);
  _lineto(Xend, Yend);
  _lineto(Xend, Ystart);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xend - 1, Yend - 1);
      _lineto(Xend - 1, Ystart + 1);
      SetColor(WHITE);
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xstart + 1, Ystart + 1);
      _lineto(Xend - 1, Ystart + 1);
    }
  SetColor(WHITE);
  _moveto(Xend, Ystart);
  _lineto(Xstart, Ystart);
  _lineto(Xstart, Yend);
  SetColor(BLACK);
}



/*
   Draw a sunken 3D-box on the screen from screen coords.
*/

void DrawScreenBoxSunken(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  SetColor(LIGHTGRAY);
  _rectangle(_GFILLINTERIOR, Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(WHITE);
  _moveto(Xstart, Yend);
  _lineto(Xend, Yend);
  _lineto(Xend, Ystart);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xend - 1, Yend - 1);
      _lineto(Xend - 1, Ystart + 1);
      SetColor(DARKGRAY);
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xstart + 1, Ystart + 1);
      _lineto(Xend - 1, Ystart + 1);
    }
  SetColor(DARKGRAY);
  _moveto(Xend, Ystart);
  _lineto(Xstart, Ystart);
  _lineto(Xstart, Yend);
  SetColor(WHITE);
}



/*
   Draw a hollow 3D-box on the screen from screen coords.
*/

void DrawScreenBoxHollow(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  SetColor(BLACK);
  _rectangle(_GFILLINTERIOR, Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(WHITE);
  _moveto(Xstart, Yend);
  _lineto(Xend, Yend);
  _lineto(Xend, Ystart);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xend - 1, Yend - 1);
      _lineto(Xend - 1, Ystart + 1);
      SetColor(DARKGRAY);
      _moveto(Xstart + 1, Yend - 1);
      _lineto(Xstart + 1, Ystart + 1);
      _lineto(Xend - 1, Ystart + 1);
    }
  SetColor(DARKGRAY);
  _moveto(Xend, Ystart);
  _lineto(Xstart, Ystart);
  _lineto(Xstart, Yend);
  SetColor(WHITE);
}



/*
   Draw a meter bar on the screen from screen coords (in a hollow box); max. value = 1.0
*/

void DrawScreenMeter(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend, float value)
{
  if (value < 0.0)
    value = 0.0;
  if (value > 1.0)
    value = 1.0;
  SetColor(BLACK);
  _rectangle(_GFILLINTERIOR, (Xstart + 1 + (Int16) ((Xend - Xstart - 2) * value), Ystart + 1, Xend - 1, Yend - 1);
  SetColor(LIGHTGREEN);
  _rectangle(_GFILLINTERIOR, Xstart + 1, Ystart + 1, Xstart + 1 + (Int16) ((Xend - Xstart - 2) * value), Yend - 1);
}



/*
   Write text to the screen.
*/

void DrawScreenText(Int16 Xstart, Int16 Ystart, char *msg, ...)
{
  static Int16 lastX;
  static Int16 lastY;
  char temp[120];
  va_list args;

  va_start(args, msg);
  vsprintf(temp, msg, args);
  va_end(args);
  if (Xstart < 0)
    Xstart = lastX;
  if (Ystart < 0)
    Ystart = lastY;
  _moveto(Xstart, Ystart);
  _outgtext(temp);
  lastX = Xstart;
  lastY = Ystart + 10;  /* or textheight("W") ? */
}


/*
   Draw a bitmap on the screen (from data in "flat" format).
*/

void DrawScreenBitmap(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend, UInt8 far *pixels)
{
  Int16 i, j;

  if (num_colors == 256)
    {
      /*! later, replace this by a call to _putimage() */
      for (i = Ystart; i <= Yend; i++)
        for (j = Xstart; j <= Xend; j++)
          {
	    _setcolor(*pixels);
	    _setpixel(j, i);
            pixels++;
          }
    }
  else
    {
      for (i = Ystart; i <= Yend; i++)
        for (j = Xstart; j <= Xend; j++)
          {
            _setcolor(TranslateTo16Color(*pixels));
	    _setpixel(j, i);
            pixels++;
          }
    }
}



/*
   Draw (or erase) the pointer if we aren't using the mouse.
*/

void DrawPointer(Bool rulers)
{
  UInt16 r;

  /* use XOR mode : drawing the pointer twice erases it */
   _setplotaction( _GXOR);
  /* draw the pointer */
  if (rulers)
    {
      SetColor(MAGENTA);
      r = (UInt16) (512 * Scale);
      /*! circle(PointerX, PointerY, r); */
      r >>= 1;
      /*! circle(PointerX, PointerY, r); */
      r >>= 1;
      /*! circle(PointerX, PointerY, r); */
      r >>= 1;
      /*! circle(PointerX, PointerY, r); */
      r = (UInt16) (1024 * Scale);
      _moveto(PointerX - r, PointerY);
      _lineto(PointerX + r, PointerY);
      _moveto(PointerX, PointerY - r);
      _lineto(PointerX, PointerY + r);
    }
  else
    {
      SetColor(YELLOW);
      _moveto(PointerX - 15, PointerY - 13);
      _lineto(PointerX + 15, PointerY + 13);
      _moveto(PointerX - 15, PointerY + 13);
      _lineto(PointerX + 15, PointerY - 13);
    }
   /* restore normal write mode */
   _setplotaction( _GPSET);
}



/*
   Load one "playpal" palette and change all palette colours.
*/

void SetDoomPalette(int playpalnum)
{
  MDirPtr     dir;
  UInt8 huge *dpal;
  int         n;

  if (playpalnum < 0 && playpalnum > 13)
    return;
  dir = FindMasterDir(MasterDir, "PLAYPAL");
  if (dir)
    {
      dpal = (UInt8 huge *)GetFarMemory(768 * sizeof(UInt8));
      BasicWadSeek(dir->wadfile, dir->dir.start);
      for (n = 0; n <= playpalnum; n++)
        BasicWadRead(dir->wadfile, dpal, 768L);

      /* gamma correction */
      if (Config.gamma > 0 && Config.gamma <= 4)
        {
          float gmult[5] = {1.0, 0.75, 0.55, 0.4, 0.3};

          for (n = 0; n < 768; n++)
            dpal[n] = (UInt8) (pow(((double) dpal[n] / 255.0), gmult[Config.gamma])
                               * 255.0);
        }

      if (Config.forceWhite == TRUE)
        {
          dpal[3 * WHITE] = 255;
          dpal[3 * WHITE + 1] = 255;
          dpal[3 * WHITE + 2] = 255;
        }

      outp(0x03c8, 0);     
      for (n = 0; n < 256; n++)
	{
	  outp(0x03c9, *dpal++);
	  outp(0x03c9, *dpal++);
	  outp(0x03c9, *dpal++);
	}
      
      FreeFarMemory(dpal);
    }
}




   Set the current drawing color (from one of the 16 color constants).
*/

void SetColor(int color16)
{
  if (num_colors >= 256)
    color16 = TranslateToDoomColor(color16);
  _setcolor(color16);
  /* update the global variable */
  current_color = color16;
}



/*
   Set the current drawing color (from Doom palette #0).
*/

void SetDoomColor(int color256)
{
  if (num_colors < 256)
    color256 = TranslateTo16Color(color256);
  _setcolor(color256);
  /* update the global variable */
  current_color = color256;
}



/*
   Wait for a key.
*/

UInt16 WaitForKey(void)
{
  UInt16 key;

  key = _bios_keybrd(0);
  if ((key & 0x00FF) != 0)
    return key & 0x00FF;
  else
    {
      key = key & 0xFF00;
      if (key == PCKEY_INS && (bioskey(2) & 0x03) != 0)
        return PCKEY_SHIFT_INS;
      else if (key == PCKEY_DEL && (bioskey(2) & 0x03) != 0)
        return PCKEY_SHIFT_DEL;
      else
        return key;
    }
}


/*
   Test is a key has been pressed (non-blocking).
*/

Bool IsKeyPressed(void)
{
  if (_bios_keybrd(1) != 0)
    return TRUE;
  else
    return FALSE;
}


/*
   Get the status of the modifiers: Shift, Alt, Ctrl,... (non-blocking).
*/

UInt16 GetAltKeys(void)
{
  return _bios_keybrd(2);
}



/* end of file */
