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

 M_MAPDRW.C - Map drawing routines for the map editor

*/

/* the includes */
#include "deu.h"
#include <math.h>
#if defined(__TURBOC__)
#include <alloc.h> /* farcoreleft() */
#elif defined(__GO32__)
#include <dpmi.h>  /* _go32_dpmi...() */
#endif
#ifdef DEU_UNIX
#include <unistd.h>
#endif
#include "d_main.h"
#include "d_misc.h"
#include "d_config.h"
#include "d_wads.h"
#include "g_gfx.h"
#include "g_mouse.h"
#include "w_levels.h"
#include "w_names.h"
#include "w_things.h"
#include "w_object.h"
#include "w_select.h"
#include "w_cutpas.h"
#include "i_menus.h"
#include "m_checks.h"
#include "m_object.h"
#include "m_edmenu.h"
#include "m_edobj.h"
#include "m_info.h"
#include "m_print.h"
#include "m_mapdrw.h"


/*
   Display the help screen.
*/

void DisplayHelp(int objtype, Int16 grid)
{
  Int16 x0 = 137;
  Int16 y0 = 50;

  if (UseMouse)
    HideMousePointer();
  /* put in the instructions */
  DrawScreenBox3D(x0, y0, x0 + 364, y0 + 355);
  SetColor(LIGHTCYAN);
  DrawScreenText(x0 + 100, y0 + 20, "Doom Editor Utility");
  DrawScreenText(269 - strlen(GetEditModeName(objtype)) * 4, y0 + 32, "- %s Editor -", GetEditModeName(objtype));
  SetColor(BLACK);
  DrawScreenText(x0 + 10, y0 + 60, "Use the mouse or the cursor keys to move");
  DrawScreenText(-1, -1, "around.  The map scrolls when the pointer");
  DrawScreenText(-1, -1, "reaches the edge of the screen.");
  DrawScreenText(-1, y0 + 100, "Other useful keys are:");
  if ((DoomVersion & 15) != 0)
    DrawScreenText(-1, y0 + 115, "Q     - Quit, saving changes");
  else
    {
      SetColor(DARKGRAY);
      DrawScreenText(-1, y0 + 115, "Q     - Quit without saving changes");
      SetColor(BLACK);
    }
  DrawScreenText(-1, -1, "Esc   - Exit without saving changes");
  DrawScreenText(-1, -1, "Tab   - Switch to the next editing mode");
  DrawScreenText(-1, -1, "Space - Change the move/scroll speed");
  DrawScreenText(-1, -1, "+/-   - Change the map scale (current: %d)", (int) (1.0 / Scale + 0.5));
  DrawScreenText(-1, -1, "G     - Change the grid scale (cur.: %d)", grid);
  DrawScreenText(-1, -1, "N, >  - Jump to the next object.");
  DrawScreenText(-1, -1, "P, <  - Jump to the previous object.");
  DrawScreenText(-1, -1, "J, #  - Jump to a specific object (enter #)");
  DrawScreenText(-1, -1, "M     - Mark/unmark current object (select)");
  if (objtype == OBJ_THINGS || objtype == OBJ_VERTEXES)
    DrawScreenText(-1, -1, "D     - Toggle drag mode");
  else
    DrawScreenText(-1, -1, "C     - Clear all marks and redraw map");
  DrawScreenText(-1, -1, "Ins   - Insert a new object");
  DrawScreenText(-1, -1, "Del   - Delete the current object");
  DrawScreenText(-1, -1, "Enter - Edit the current/selected object(s)");
  DrawScreenText(-1, y0 + 265, "Mouse buttons:");
  if (Config.swapButtons)
    {
      DrawScreenText(-1, y0 + 280, "Left  - Edit the current/selected object(s)");
      DrawScreenText(-1, -1, "Middle- Mark/unmark the current object.");
    }
  else
    {
      DrawScreenText(-1, y0 + 280, "Left  - Mark/unmark the current object");
      DrawScreenText(-1, -1, "Middle- Edit the current/selected object(s)");
    }
  DrawScreenText(-1, -1, "Right - Drag the current/selected object(s)");
  SetColor(LIGHTCYAN);
  DrawScreenText(-1, y0 + 320, "Please read DEU.TXT for more information");
  SetColor(YELLOW);
  DrawScreenText(-1, y0 + 340, "Press any key to return to the editor...");
  (void) WaitForKey();
  if (UseMouse)
    ShowMousePointer();
}


/*
   Display the current coordinates of the mouse pointer.
*/

void DisplayPointerCoords(void)
{
  if (UseMouse)
    HideMousePointer();
  SetColor(LIGHTGRAY);
  DrawScreenBox(ScrMaxX - 170, 4, ScrMaxX - 50, 12);
  SetColor(BLUE);
  DrawScreenText(ScrMaxX - 170, 4, "%d, %d", MAPX(PointerX), MAPY(PointerY));
  if (UseMouse)
    ShowMousePointer();
}


/*
   Highlight one object.
*/

void HighlightObject(LevelPtr level, int objtype, Int16 objnum, Int16 color)
{
  Int16  n, m;
  
  /* use XOR mode : drawing any line twice erases it */
  SetLineDrawingMode(DRAW_XOR);
  SetColor(color);
  switch (objtype)
    {
    case OBJ_THINGS:
      m = (GetThingRadius(level->things[objnum].type) * 3) / 2;
      DrawMapLine(level->things[objnum].xpos - m, level->things[objnum].ypos - m, level->things[objnum].xpos - m, level->things[objnum].ypos + m);
      DrawMapLine(level->things[objnum].xpos - m, level->things[objnum].ypos + m, level->things[objnum].xpos + m, level->things[objnum].ypos + m);
      DrawMapLine(level->things[objnum].xpos + m, level->things[objnum].ypos + m, level->things[objnum].xpos + m, level->things[objnum].ypos - m);
      DrawMapLine(level->things[objnum].xpos + m, level->things[objnum].ypos - m, level->things[objnum].xpos - m, level->things[objnum].ypos - m);
      DrawMapArrow(level->things[objnum].xpos, level->things[objnum].ypos, level->things[objnum].angle * 182);
      break;
    case OBJ_LINEDEFS:
      n = (level->vertexes[level->linedefs[objnum].start].x + level->vertexes[level->linedefs[objnum].end].x) / 2;
      m = (level->vertexes[level->linedefs[objnum].start].y + level->vertexes[level->linedefs[objnum].end].y) / 2;
      DrawMapLine(n, m, n + (level->vertexes[level->linedefs[objnum].end].y - level->vertexes[level->linedefs[objnum].start].y) / 3, m + (level->vertexes[level->linedefs[objnum].start].x - level->vertexes[level->linedefs[objnum].end].x) / 3);
      SetLinePatternAndWidth(SOLID_PATTERN, 3);
      DrawMapVector(level->vertexes[level->linedefs[objnum].start].x, level->vertexes[level->linedefs[objnum].start].y,
                    level->vertexes[level->linedefs[objnum].end].x, level->vertexes[level->linedefs[objnum].end].y);
      if (color != LIGHTRED && level->linedefs[objnum].tag > 0)
        {
          for (m = 0; m < level->num_sectors; m++)
            if (level->sectors[m].tag == level->linedefs[objnum].tag)
              HighlightObject(level, OBJ_SECTORS, m, LIGHTRED);
        }
      SetLinePatternAndWidth(SOLID_PATTERN, 1);
      break;
    case OBJ_VERTEXES:
      DrawMapLine(level->vertexes[objnum].x - OBJSIZE * 2, level->vertexes[objnum].y - OBJSIZE * 2, level->vertexes[objnum].x - OBJSIZE * 2, level->vertexes[objnum].y + OBJSIZE * 2);
      DrawMapLine(level->vertexes[objnum].x - OBJSIZE * 2, level->vertexes[objnum].y + OBJSIZE * 2, level->vertexes[objnum].x + OBJSIZE * 2, level->vertexes[objnum].y + OBJSIZE * 2);
      DrawMapLine(level->vertexes[objnum].x + OBJSIZE * 2, level->vertexes[objnum].y + OBJSIZE * 2, level->vertexes[objnum].x + OBJSIZE * 2, level->vertexes[objnum].y - OBJSIZE * 2);
      DrawMapLine(level->vertexes[objnum].x + OBJSIZE * 2, level->vertexes[objnum].y - OBJSIZE * 2, level->vertexes[objnum].x - OBJSIZE * 2, level->vertexes[objnum].y - OBJSIZE * 2);
      break;
    case OBJ_SECTORS:
      SetLinePatternAndWidth(SOLID_PATTERN, 3);
      for (n = 0; n < level->num_linedefs; n++)
        if ((level->linedefs[n].sidedef1>=0 && level->sidedefs[level->linedefs[n].sidedef1].sector == objnum ) ||
            (level->linedefs[n].sidedef2>=0 && level->sidedefs[level->linedefs[n].sidedef2].sector == objnum ))
          DrawMapLine(level->vertexes[level->linedefs[n].start].x, level->vertexes[level->linedefs[n].start].y,
                      level->vertexes[level->linedefs[n].end].x, level->vertexes[level->linedefs[n].end].y);
      if (color != LIGHTRED && level->sectors[objnum].tag > 0)
        {
          for (m = 0; m < level->num_linedefs; m++)
            if (level->linedefs[m].tag == level->sectors[objnum].tag)
              HighlightObject(level, OBJ_LINEDEFS, m, LIGHTRED);
        }
      SetLinePatternAndWidth(SOLID_PATTERN, 1);
      break;
    }
  /* restore normal write mode */
  SetLineDrawingMode(DRAW_NORMAL);
}


/*
   Draw the actual game map.
*/

void DrawMap(LevelPtr level, int editmode, Int16 grid, Bool drawgrid, int thingmask)
{
  Int16  n, m;
  UInt32 memavail;

  /* clear the screen */
  ClearScreen();

  /* draw the grid */
  if (drawgrid == TRUE && grid > 0)
    {
      Int16 mapx0 = (Int16)(((int)MAPX(0)) & ((int)~(grid - 1)));
      Int16 mapx1 = (Int16)(((int)(MAPX(ScrMaxX) + grid)) & ((int)~(grid - 1)));
      Int16 mapy0 = (Int16)(((int)MAPY(ScrMaxY)) & ((int)~(grid - 1)));
      Int16 mapy1 = (Int16)(((int)(MAPY(0) + grid)) & ((int)~(grid - 1)));
      Int16 grid2;

      if (((SCREENX(mapx0 + grid) - SCREENX(mapx0)) >= 7)
          && ((SCREENY(mapy0) - SCREENY(mapy0 + grid)) >= 7))
        {
          SetColor(BLUE);
          for (n = mapx0; n <= mapx1; n += grid)
            DrawMapLine(n, mapy0, n, mapy1);
          for (n = mapy0; n <= mapy1; n += grid)
            DrawMapLine(mapx0, n, mapx1, n);
        }
      else
        {
          /* draw fewer grid lines if they would be too close to each other */
          grid2 = grid * 2;
          while (SCREENX(mapx0 + grid2) - SCREENX(mapx0) < 7)
            grid2 *= 2;
          SetLinePatternAndWidth(DOTTED_PATTERN, 1);
          SetColor(MAGENTA);
          for (n = mapx0; n <= mapx1; n += grid2)
            DrawMapLine(n, mapy0, n, mapy1);
          for (n = mapy0; n <= mapy1; n += grid2)
            DrawMapLine(mapx0, n, mapx1, n);
          SetLinePatternAndWidth(SOLID_PATTERN, 1);
        }
    }

  /* draw the linedefs to form the map */
  switch (editmode)
    {
    case OBJ_THINGS:
      for (n = 0; n < level->num_linedefs; n++)
        {
          if (level->linedefs[n].flags & 1)
            SetColor(WHITE);
          else
            SetColor(LIGHTGRAY);
          DrawMapLine(level->vertexes[level->linedefs[n].start].x, level->vertexes[level->linedefs[n].start].y,
                      level->vertexes[level->linedefs[n].end].x, level->vertexes[level->linedefs[n].end].y);
        }
      break;
    case OBJ_VERTEXES:
      SetColor(LIGHTGRAY);
      for (n = 0; n < level->num_linedefs; n++)
        DrawMapVector(level->vertexes[level->linedefs[n].start].x, level->vertexes[level->linedefs[n].start].y,
                      level->vertexes[level->linedefs[n].end].x, level->vertexes[level->linedefs[n].end].y);
      break;
    case OBJ_LINEDEFS:
      for (n = 0; n < level->num_linedefs; n++)
        {
          if (level->linedefs[n].type > 0)
            {
              if (level->linedefs[n].tag > 0)
                SetColor(LIGHTMAGENTA);
              else
                SetColor(LIGHTGREEN);
            }
          else if (level->linedefs[n].tag > 0)
            SetColor(LIGHTRED);
          else if (level->linedefs[n].flags & 1)
            SetColor(WHITE);
          else
            SetColor(LIGHTGRAY);
          DrawMapLine(level->vertexes[level->linedefs[n].start].x, level->vertexes[level->linedefs[n].start].y,
                      level->vertexes[level->linedefs[n].end].x, level->vertexes[level->linedefs[n].end].y);
        }
      break;
    case OBJ_SECTORS:
      for (n = 0; n < level->num_linedefs; n++)
        {
          if ((m = level->linedefs[n].sidedef1) < 0 || (m = level->sidedefs[m].sector) < 0)
            SetColor(LIGHTRED);
          else
            {
              if (level->sectors[m].tag > 0)
                SetColor(LIGHTGREEN);
              else if (level->sectors[m].type > 0)
                SetColor(LIGHTCYAN);
              else if (level->linedefs[n].flags & 1)
                SetColor(WHITE);
              else
                SetColor(LIGHTGRAY);
              if ((m = level->linedefs[n].sidedef2) >= 0)
                {
                  if ((m = level->sidedefs[m].sector) < 0)
                    SetColor(LIGHTRED);
                  else if (level->sectors[m].tag > 0)
                    SetColor(LIGHTGREEN);
                  else if (level->sectors[m].type > 0)
                    SetColor(LIGHTCYAN);
                }
            }
          DrawMapLine(level->vertexes[level->linedefs[n].start].x, level->vertexes[level->linedefs[n].start].y,
                      level->vertexes[level->linedefs[n].end].x, level->vertexes[level->linedefs[n].end].y);
        }
      break;
    }

  /* draw in the vertices */
  if (editmode == OBJ_VERTEXES)
    {
      SetColor(LIGHTGREEN);
      if (Scale < 1.0)
        {
          for (n = 0; n < level->num_vertexes; n++)
            {
              DrawMapLine(level->vertexes[n].x - OBJSIZE, level->vertexes[n].y - OBJSIZE, level->vertexes[n].x + OBJSIZE, level->vertexes[n].y + OBJSIZE);
              DrawMapLine(level->vertexes[n].x + OBJSIZE, level->vertexes[n].y - OBJSIZE, level->vertexes[n].x - OBJSIZE, level->vertexes[n].y + OBJSIZE);
            }
        }
      else
        {
          for (n = 0; n < level->num_vertexes; n++)
            {
              DrawMapLine(level->vertexes[n].x - 3, level->vertexes[n].y - 3, level->vertexes[n].x + 3, level->vertexes[n].y + 3);
              DrawMapLine(level->vertexes[n].x + 3, level->vertexes[n].y - 3, level->vertexes[n].x - 3, level->vertexes[n].y + 3);
            }
        }
    }

  /* draw in the things */
  if (editmode == OBJ_THINGS)
    {
      for (n = 0; n < level->num_things; n++)
        {
          if (((level->things[n].when ^ 0x10) & thingmask) == 0)
            continue;
          m = GetThingRadius(level->things[n].type);
          SetColor(GetThingColour(level->things[n].type));
          DrawMapLine(level->things[n].xpos - m, level->things[n].ypos, level->things[n].xpos + m, level->things[n].ypos);
          DrawMapLine(level->things[n].xpos, level->things[n].ypos - m, level->things[n].xpos, level->things[n].ypos + m);
          DrawMapCircle(level->things[n].xpos, level->things[n].ypos, m);
        }
    }
  else
    {
      SetColor(LIGHTGRAY);
      for (n = 0; n < level->num_things; n++)
        {
          DrawMapLine(level->things[n].xpos - OBJSIZE, level->things[n].ypos, level->things[n].xpos + OBJSIZE, level->things[n].ypos);
          DrawMapLine(level->things[n].xpos, level->things[n].ypos - OBJSIZE, level->things[n].xpos, level->things[n].ypos + OBJSIZE);
        }
    }

  /* draw in the title bar */
  DrawScreenBox3D(0, 0, ScrMaxX, 16);
  SetColor(WHITE);
/*
  DrawScreenText(20,  4, "File  Edit  Search  Modes  Misc  Objects  Check");
  DrawScreenText(20,  6, "_     _     _       _       _    _        _    ");
*/
  DrawScreenText(20,  4, "File  Edit  Search  Modes  Objects  Check  %sTools",
                 GetObjectTypeName(editmode));
  DrawScreenText(20,  6, "_     _     _       _      _        _");
  if (editmode == OBJ_THINGS)
    DrawScreenText(404, 6, "_");
  else if (editmode == OBJ_LINEDEFS)
    DrawScreenText(420, 6, "_");
  else
    DrawScreenText(412, 6, "_");
  DrawScreenText(ScrMaxX - 45, 4, "Help");
  DrawScreenText(ScrMaxX - 45, 6, "_   ");

  /* draw the bottom line, if needed */
  if (Config.infoShown)
    {
      DrawScreenBox3D(0, ScrMaxY - 11, ScrMaxX, ScrMaxY);
      if (level->made_map_changes == TRUE)
        DrawScreenText(5, ScrMaxY - 8, "Editing %s on %s #", GetEditModeName(editmode), level->dir->dir.name);
      else if (level->made_changes == TRUE)
        DrawScreenText(5, ScrMaxY - 8, "Editing %s on %s *", GetEditModeName(editmode), level->dir->dir.name);
      else
        DrawScreenText(5, ScrMaxY - 8, "Editing %s on %s", GetEditModeName(editmode), level->dir->dir.name);
      if (Scale < 1.0)
        DrawScreenText(ScrMaxX - 176, ScrMaxY - 8, "Scale: 1/%d  Grid: %d", (int) (1.0 / Scale + 0.5), grid);
      else
        DrawScreenText(ScrMaxX - 176, ScrMaxY - 8, "Scale: %d/1  Grid: %d", (int) Scale, grid);
#ifdef DEU_DOS
#ifdef __GO32__
      memavail = (UInt32) _go32_dpmi_remaining_physical_memory();
#else
      memavail = farcoreleft();
#endif

      if (memavail < 50000L)
        {
          if (memavail < 20000L)
            SetColor(LIGHTRED);
          else
            SetColor(RED);
        }
      DrawScreenText(ScrCenterX - ((editmode == OBJ_LINEDEFS) ? 10 : 50), ScrMaxY - 8, "Free mem: %lu", memavail);
#endif /* DEU_DOS */
    }
}


/*
   Center the map around the given coords.
*/

static void CenterMapAroundCoords(Int16 xpos, Int16 ypos)
{
  OrigX = xpos;
  OrigY = ypos;
  PointerX = ScrCenterX;
  PointerY = ScrCenterY;
}


/*
   Center the map around the object and zoom in if necessary.
*/

void GoToObject(LevelPtr level, int objtype, int objnum)
{
  Int16 xpos, ypos;
  Int16 xpos2, ypos2;
  Int16 n;
  Int16 sd1, sd2;
  float oldscale;

  GetObjectCoords(level, objtype, objnum, &xpos, &ypos);
  CenterMapAroundCoords(xpos, ypos);
  oldscale = Scale;

  /* zoom in until the object can be selected */
  while (Scale < 4.0 && GetCurObject(level, objtype, MAPX(PointerX - 4), MAPY(PointerY - 4), MAPX(PointerX + 4), MAPY(PointerY + 4)) != objnum)
    {
      if (Scale < 1.0)
        Scale = 1.0 / ((1.0 / Scale) - 1.0);
      else
        Scale = Scale * 2.0;
    }
  /* Special case for Sectors: if several Sectors are one inside another, then     */
  /* zooming in on the center won't help.  So I choose a LineDef that borders the  */
  /* the Sector and move a few pixels towards the inside of the Sector.            */
  if (objtype == OBJ_SECTORS && GetCurObject(level, OBJ_SECTORS, OrigX, OrigY, OrigX, OrigY) != objnum)
    {
      /* restore the Scale */
      Scale = oldscale;
      for (n = 0; n < level->num_linedefs; n++)
        {
          sd1 = level->linedefs[n].sidedef1;
          sd2 = level->linedefs[n].sidedef2;
          if (sd1 >= 0 && level->sidedefs[sd1].sector == objnum)
            break;
          if (sd2 >= 0 && level->sidedefs[sd2].sector == objnum)
            break;
        }
      if (n < level->num_linedefs)
        {
          GetObjectCoords(level, OBJ_LINEDEFS, n, &xpos2, &ypos2);
          n = (Int16) hypot((double) abs(xpos - xpos2),
                            (double) abs(ypos - ypos2)) / 7;
          if (n <= 1)
            n = 2;
          xpos = xpos2 + (xpos - xpos2) / n;
          ypos = ypos2 + (ypos - ypos2) / n;
          CenterMapAroundCoords(xpos, ypos);
          /* zoom in until the sector can be selected */
          while (Scale > 4.0 && GetCurObject(level, OBJ_SECTORS, OrigX, OrigY, OrigX, OrigY) != objnum)
            {
              if (Scale < 1.0)
                Scale = 1.0 / ((1.0 / Scale) - 1.0);
              else
                Scale = Scale / 2.0;
            }
        }
    }
  if (UseMouse)
    SetMouseCoords(PointerX, PointerY);
}

/* end of file */
