/*----------------------------------------------------------------------------*
 | 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_GFXVGA.C - Graphical routines for Linux SVGAlib

 Functions written by Mike thomas and Raphael Quinet and adapted for 
 Linux SVGAlib by Sam Lantinga  (slouken@cs.ucdavis.edu)
*/

/* the includes */
#include "deu.h"
#include <math.h>
#include "d_main.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"

#define LIBRARY	static
#include "vgaxor.c"
#include "keyboard.c"

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

/* almost private: shared with g_colcnv.c */
int         num_colors = -1;    /* number of colors available */
/* almost private: shared with g_moux11.c */
int          x_pointerx;
int          x_pointery;
unsigned int x_buttons;
unsigned int x_modifiers;
long         x_inputeventmask;

/*
   Initialise the graphics display.
*/

void InitGfx(void)
{
  /* We can use a mouse! */
  vga_setmousesupport(1);

  if ( Init_800x600() < 0 )
    ProgError("800 x 600 resolution not supported on display!");
   
  if ( Init_Keyboard() < 0 ) {
    End_800x600();
    ProgError("Can't initialize keyboard!");
  }
  num_colors = 256; /* maybe support other visuals later? */
  if (num_colors >= 256)
    {
      SetDoomPalette(0);
    }
  ScrMaxX = (UInt16)SCREEN_WIDTH - 1;
  ScrMaxY = (UInt16)SCREEN_HEIGHT - 1;
  ScrCenterX = ScrMaxX / 2;
  ScrCenterY = ScrMaxY / 2;
  SetColor(WHITE);

  InitMouseDriver();
}



/*
   Terminate the graphics display.
*/

void TermGfx(void)
{
  if (num_colors > 0)
    {
      if (UseMouse)
        HideMousePointer();
      End_Keyboard();
      End_800x600();
      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)
{
  Clear_Area(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}



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

void SetLineDrawingMode(int mode)
{
  switch (mode)
    {
    case DRAW_NORMAL:
      Set_DrawMode(DRAW_COPY);
      break;
    case DRAW_XOR:
      Set_DrawMode(DRAW_MXOR);
      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);
  Set_LineWidth(width);
  switch (pattern)
    {
    case SOLID_PATTERN:
      Set_LineIncr(1, 0);
      break;
    case DOTTED_PATTERN:
      Set_LineIncr(2, 2);
      break;
    case DASHED_PATTERN:
      Set_LineIncr(5, 3);
      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)
{
  Draw_Line(SCREENX(mapXstart), SCREENY(mapYstart),
	    				SCREENX(mapXend), SCREENY(mapYend));
}



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

void DrawMapCircle(Int16 mapXcenter, Int16 mapYcenter, Int16 mapRadius)
{
  Draw_Circle(SCREENX(mapXcenter), SCREENY(mapYcenter),
						(int)(mapRadius * Scale));
}



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

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



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

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



/*
   Draw a pixel on the screen.
*/

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



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

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



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

void DrawScreenRectangle(Int16 Xstart, Int16 Ystart, Int16 Xend, Int16 Yend)
{
  Draw_Rect(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)
{
  Fill_Rect(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)
{
  ++Xstart; ++Ystart; ++Xend; ++Yend;
  SetColor(LIGHTGRAY);
  Fill_Rect(Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(DARKGRAY);
  Draw_Line(Xstart, Yend, Xend, Yend);
  Draw_Line(Xend, Ystart, Xend, Yend);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      Draw_Line(Xstart + 1, Yend - 1, Xend - 1, Yend - 1);
      Draw_Line(Xend - 1, Ystart + 1, Xend - 1, Yend - 1);
      SetColor(WHITE);
      Draw_Line(Xstart + 1, Ystart + 1, Xstart + 1, Yend - 1);
      Draw_Line(Xstart + 1, Ystart + 1, Xend - 1, Ystart + 1);
    }
  SetColor(WHITE);
  Draw_Line(Xstart, Ystart, Xend, Ystart);
  Draw_Line(Xstart, Ystart, 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);
  Fill_Rect(Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(WHITE);
  Draw_Line(Xstart, Yend, Xend, Yend);
  Draw_Line(Xend, Ystart, Xend, Yend);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      Draw_Line(Xstart + 1, Yend - 1, Xend - 1, Yend - 1);
      Draw_Line(Xend - 1, Ystart + 1, Xend - 1, Yend - 1);
      SetColor(DARKGRAY);
      Draw_Line(Xstart + 1, Ystart + 1, Xstart + 1, Yend - 1);
      Draw_Line(Xstart + 1, Ystart + 1, Xend - 1, Ystart + 1);
    }
  SetColor(DARKGRAY);
  Draw_Line(Xstart, Ystart, Xend, Ystart);
  Draw_Line(Xstart, Ystart, 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);
  Fill_Rect(Xstart + 1, Ystart + 1, Xend - 1, Yend - 1);
  SetColor(WHITE);
  Draw_Line(Xstart, Yend, Xend, Yend);
  Draw_Line(Xend, Ystart, Xend, Yend);
  if (Xend - Xstart > 20 && Yend - Ystart > 20)
    {
      Draw_Line(Xstart + 1, Yend - 1, Xend - 1, Yend - 1);
      Draw_Line(Xend - 1, Ystart + 1, Xend - 1, Yend - 1);
      SetColor(DARKGRAY);
      Draw_Line(Xstart + 1, Ystart + 1, Xstart + 1, Yend - 1);
      Draw_Line(Xstart + 1, Ystart + 1, Xend - 1, Ystart + 1);
    }
  SetColor(DARKGRAY);
  Draw_Line(Xstart, Ystart, Xend, Ystart);
  Draw_Line(Xstart, Ystart, 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);
  Fill_Rect(Xstart + 1 + (Int16) ((Xend - Xstart - 2) * value),
				Ystart + 1, Xend - 1, Yend - 1);
  SetColor(LIGHTGREEN);
  Fill_Rect(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;
  Draw_Text(Xstart, Ystart, 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, Int8 far *pixels)
{
  Int16 i, j;

  if (num_colors == 256)
    {
      /*! this is very slow and has to be optimized later */
      for (i = Ystart; i <= Yend; i++)
        for (j = Xstart; j <= Xend; j++)
          {
            Set_Color(*pixels);
	    Draw_Pixel(j, i);
            pixels++;
          }
    }
  else
    {
      for (i = Ystart; i <= Yend; i++)
        for (j = Xstart; j <= Xend; j++)
          {
            Set_Color(TranslateTo16Color(*pixels));
	    Draw_Pixel(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 */
  SetLineDrawingMode(DRAW_XOR);
  /* draw the pointer */
  /*! optimize this later: replace DrawScreenLine by direct GL funcs. */
  if (rulers)
    {
      SetColor(MAGENTA);
      r = (UInt16) (512 * Scale);
      Draw_Circle(PointerX, PointerY, r);
      r >>= 1;
      Draw_Circle(PointerX, PointerY, r);
      r >>= 1;
      Draw_Circle(PointerX, PointerY, r);
      r >>= 1;
      Draw_Circle(PointerX, PointerY, r);
      r = (UInt16) (1024 * Scale);
      Draw_Line(PointerX - r, PointerY, PointerX + r, PointerY);
      Draw_Line(PointerX, PointerY - r, PointerX, PointerY + r);
    }
  else
    {
      SetColor(YELLOW);
      Draw_Line(PointerX - 15, PointerY - 13, PointerX + 15, PointerY + 13);
      Draw_Line(PointerX - 15, PointerY + 13, PointerX + 15, PointerY - 13);
    }
   /* restore normal write mode */
  SetLineDrawingMode(DRAW_NORMAL);
}



/*
   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?
      for (n = 0; n < 768; n++)
        dpal[n] = (UInt8) (pow(((double) dpal[n] / 255.0), 0.7) * 255);
*/
      if (Config.forceWhite == TRUE)
        {
          dpal[3 * WHITE] = 255;
          dpal[3 * WHITE + 1] = 255;
          dpal[3 * WHITE + 2] = 255;
        }

      Set_Palette((Color *)dpal);
      FreeFarMemory(dpal);
    }
}



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

void SetColor(int color16)
{
  if (num_colors >= 256)
    Set_Color(TranslateToDoomColor(color16));
}



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

void SetDoomColor(int color256)
{
  if (num_colors < 256)
    Set_Color(TranslateTo16Color(color256));
}

/* end of file */
