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

 W_CUTPAS.C - Cut, Copy and Paste operations on level structures

*/

#include "deu.h"
#include "d_misc.h"
#include "d_config.h"
#include "w_select.h"
#include "w_object.h"
#include "w_levels.h"

/* private variable */
static Bool cut_things = FALSE; /* see LevelCut and the end of LevelCopy */


/*
   Add a new Thing to a level.
*/

static void LevelAddThing(LevelPtr level, TPtr t)
{
  if (level->num_things++ == 0)
    level->things = (TPtr) GetFarMemory(sizeof(struct Thing));
  else
    level->things = (TPtr) ResizeFarMemory(level->things,
                                           (UInt32) (level->num_things) * sizeof(struct Thing));
  if (t != NULL)
    memcpy(level->things + (level->num_things - 1), t, sizeof(struct Thing));
}


/*
   Add a new Vertex to a level.
*/

static void LevelAddVertex(LevelPtr level, VPtr v)
{
  if (level->num_vertexes++ == 0)
    level->vertexes = (VPtr) GetFarMemory(sizeof(struct Vertex));
  else
    level->vertexes = (VPtr) ResizeFarMemory(level->vertexes,
                                             (UInt32) (level->num_vertexes) * sizeof(struct Vertex));
  if (v != NULL)
    memcpy(level->vertexes + (level->num_vertexes - 1), v, sizeof(struct Vertex));
}



/*
   Add a new LineDef to a level.
*/

static void LevelAddLineDef(LevelPtr level, LDPtr ld)
{
  if (level->num_linedefs++ == 0)
    level->linedefs = (LDPtr) GetFarMemory(sizeof(struct LineDef));
  else
    level->linedefs = (LDPtr) ResizeFarMemory(level->linedefs,
                                              (UInt32) (level->num_linedefs) * sizeof(struct LineDef));
  if (ld != NULL)
    memcpy(level->linedefs + (level->num_linedefs - 1), ld, sizeof(struct LineDef));
}



/*
   Add a new SideDef to a level.
*/

static void LevelAddSideDef(LevelPtr level, SDPtr sd)
{
  if (level->num_sidedefs++ == 0)
    level->sidedefs = (SDPtr) GetFarMemory(sizeof(struct SideDef));
  else
    level->sidedefs = (SDPtr) ResizeFarMemory(level->sidedefs,
                                              (UInt32) (level->num_sidedefs) * sizeof(struct SideDef));
  if (sd != NULL)
    memcpy(level->sidedefs + (level->num_sidedefs - 1), sd, sizeof(struct SideDef));
}



/*
   Add a new Sector to a level.
*/

static void LevelAddSector(LevelPtr level, SPtr s)
{
  if (level->num_sectors++ == 0)
    level->sectors = (SPtr) GetFarMemory(sizeof(struct Sector));
  else
    level->sectors = (SPtr) ResizeFarMemory(level->sectors,
                                           (UInt32) (level->num_sectors) * sizeof(struct Sector));
  if (s != NULL)
    memcpy(level->sectors + (level->num_sectors - 1), s, sizeof(struct Sector));
}



/*
   Dulicate a level structure.
*/

LevelPtr LevelDuplicate(LevelPtr level)
{
  LevelPtr newlevel;
  UInt32   size;

  size = sizeof(struct LevelInfo);
  newlevel = (LevelPtr) GetMemory(size);
  /* copy all the variables and pointers */
  memcpy(newlevel, level, size);
  /* copy the Things */
  if (newlevel->num_things > 0)
    {
      size = (UInt32) (level->num_things) * sizeof(struct Thing);
      newlevel->things = (TPtr) GetFarMemory(size);
      memcpy(newlevel->things, level->things, size);
    }
  /* copy the Vertices */
  if (newlevel->num_vertexes > 0)
    {
      size = (UInt32) (level->num_vertexes) * sizeof(struct Vertex);
      newlevel->vertexes = (VPtr) GetFarMemory(size);
      memcpy(newlevel->vertexes, level->vertexes, size);
    }
  /* copy the LineDefs */
  if (newlevel->num_linedefs > 0)
    {
      size = (UInt32) (level->num_linedefs) * sizeof(struct LineDef);
      newlevel->linedefs = (LDPtr) GetFarMemory(size);
      memcpy(newlevel->linedefs, level->linedefs, size);
    }
  /* copy the SideDefs */
  if (newlevel->num_sidedefs > 0)
    {
      size = (UInt32) (level->num_sidedefs) * sizeof(struct SideDef);
      newlevel->sidedefs = (SDPtr) GetFarMemory(size);
      memcpy(newlevel->sidedefs, level->sidedefs, size);
    }
  /* copy the Sectors */
  if (newlevel->num_sectors > 0)
    {
      size = (UInt32) (level->num_sectors) * sizeof(struct Sector);
      newlevel->sectors = (SPtr) GetFarMemory(size);
      memcpy(newlevel->sectors, level->sectors, size);
    }
  /* do not copy the undo buffer! */
  newlevel->undo_buffer = NULL;
  return newlevel;
}


/*
   Copy several objects from one level into a new level structure.
*/

LevelPtr LevelCopy(LevelPtr level, int objtype, SelPtr objs)
{
  LevelPtr newlevel;
  SelPtr   cur;
  SelPtr   sub;
  LDPtr    ld;
  SDPtr    sd;
  Int16    n, d;
  Bool     added;

  newlevel = CreateNewLevel(GetLevelMapName(level));
  switch (objtype)
    {
    case OBJ_THINGS:
      /* copy only the selected Things */
      for (cur = objs; cur; cur = cur->next)
        LevelAddThing(newlevel, &(level->things[cur->objnum]));
      break;
    case OBJ_VERTEXES:
      /* copy only the selected Vertices */
      for (cur = objs; cur; cur = cur->next)
        LevelAddVertex(newlevel, &(level->vertexes[cur->objnum]));
      break;
    case OBJ_LINEDEFS:
      /* copy the selected LineDefs and their Vertices */
      sub = NULL;
      for (cur = objs; cur; cur = cur->next)
        {
          ld = &(level->linedefs[cur->objnum]);
          LevelAddLineDef(newlevel, ld);
          if (! IsSelected(sub, ld->start))
            SelectObject(&sub, ld->start);
          if (! IsSelected(sub, ld->end))
            SelectObject(&sub, ld->end);
        }
      goto CVERT; /* yes, this is a goto - yuck! */
    case OBJ_SECTORS:
      /* copy the selected Sectors and their contents */
      /* create the sectors */
      for (cur = objs; cur; cur = cur->next)
        LevelAddSector(newlevel, &(level->sectors[cur->objnum]));
      /* create the sidedefs (with the correct sector reference */
      sub = NULL;
      sd = level->sidedefs;
      for (n = 0; n < level->num_sidedefs; n++)
        {
          d = 0;
          for (cur = objs; cur; cur = cur->next)
            {
              if (cur->objnum == sd->sector)
                {
                  LevelAddSideDef(newlevel, sd);
                  newlevel->sidedefs[newlevel->num_sidedefs - 1].sector = d;
                  SelectObject(&sub, n);
                  break;
                }
              else
                d++;
            }
#ifdef BUGGY_TURBOC_3
          sd = sd + 1;
#else
          sd++;
#endif
        }
      /* create the linedefs (with the correct sidedef references */
      ld = level->linedefs;
      for (n = 0; n < level->num_linedefs; n++)
        {
          added = FALSE;
          d = 0;
          for (cur = sub; cur; cur = cur->next)
            {
              if (cur->objnum == ld->sidedef1)
                {
                  LevelAddLineDef(newlevel, ld);
                  newlevel->linedefs[newlevel->num_linedefs - 1].sidedef1 = d;
                  added = TRUE;
                  break;
                }
              d++;
            }
          d = 0;
          for (cur = sub; cur; cur = cur->next)
            {
              if (cur->objnum == ld->sidedef2)
                {
                  if (! added)
                    LevelAddLineDef(newlevel, ld);
                  newlevel->linedefs[newlevel->num_linedefs - 1].sidedef2 = d;
                  if (! added)
                    newlevel->linedefs[newlevel->num_linedefs - 1].sidedef1 = -1;
                  break;
                }
              d++;
            }
          if (cur == NULL && added)
            newlevel->linedefs[newlevel->num_linedefs - 1].sidedef2 = -1;
#ifdef BUGGY_TURBOC_3
          ld = ld + 1;
#else
          ld++;
#endif
        }
      ForgetSelection(&sub);
      /* select the vertices */
      ld = newlevel->linedefs;
      for (n = newlevel->num_linedefs; n > 0; n--)
        {
          if (! IsSelected(sub, ld->start))
            SelectObject(&sub, ld->start);
          if (! IsSelected(sub, ld->end))
            SelectObject(&sub, ld->end);
#ifdef BUGGY_TURBOC_3
          ld = ld + 1;
#else
          ld++;
#endif
        }
CVERT:
      /* create the vertices */
      for (cur = sub; cur; cur = cur->next)
        LevelAddVertex(newlevel, &(level->vertexes[cur->objnum]));
      /* re-number the starts and ends of all lines */
      ld = newlevel->linedefs;
      for (n = newlevel->num_linedefs; n > 0; n--)
        {
          d = 0;
          for (cur = sub; cur; cur = cur->next)
            {
              if (cur->objnum == ld->start)
                {
                  ld->start = d;
                  break;
                }
              else
                d++;
            }
          d = 0;
          for (cur = sub; cur; cur = cur->next)
            {
              if (cur->objnum == ld->end)
                {
                  ld->end = d;
                  break;
                }
              else
                d++;
            }
#ifdef BUGGY_TURBOC_3
          ld = ld + 1;
#else
          ld++;
#endif
        }
      ForgetSelection(&sub);
      /* create the things */
      if (objtype == OBJ_SECTORS && Config.moveThings == TRUE)
        {
          for (n = 0; n < level->num_things; n++)
            {
              d = GetSectorForThing(level, n);
              if (IsSelected(objs, d))
                {
                  LevelAddThing(newlevel, &(level->things[n]));
                  /* if LevelCopy was called from LevelCut, cut the things */
                  if (cut_things == TRUE)
                    DeleteObject(level, OBJ_THINGS, n);
                }
            }
        }
      break;
    }
  /* find the boundaries of the new level */
  newlevel->map_maxX = -32767;
  newlevel->map_maxY = -32767;
  newlevel->map_minX = 32767;
  newlevel->map_minY = 32767;
  for (n = 0; n < level->num_things; n++)
    {
      if (newlevel->things[n].xpos < newlevel->map_minX)
        newlevel->map_minX = newlevel->things[n].xpos;
      if (newlevel->things[n].xpos > newlevel->map_maxX)
        newlevel->map_maxX = newlevel->things[n].xpos;
      if (newlevel->things[n].ypos < newlevel->map_minY)
        newlevel->map_minY = newlevel->things[n].ypos;
      if (newlevel->things[n].ypos > newlevel->map_maxY)
        newlevel->map_maxY = newlevel->things[n].ypos;
    }
  for (n = 0; n < level->num_vertexes; n++)
    {
      if (newlevel->vertexes[n].x < newlevel->map_minX)
        newlevel->map_minX = newlevel->vertexes[n].x;
      if (newlevel->vertexes[n].x > newlevel->map_maxX)
        newlevel->map_maxX = newlevel->vertexes[n].x;
      if (newlevel->vertexes[n].y < newlevel->map_minY)
        newlevel->map_minY = newlevel->vertexes[n].y;
      if (newlevel->vertexes[n].y > newlevel->map_maxY)
        newlevel->map_maxY = newlevel->vertexes[n].y;
    }
  return newlevel;
}


/*
   Cut several objects from one level and put them into a new level structure.
*/

LevelPtr LevelCut(LevelPtr level, int objtype, SelPtr *objsp)
{
  LevelPtr newlevel;

  /* set cut_things to TRUE (tested in LevelCopy) - this is a hack... */
  cut_things = TRUE;
  newlevel = LevelCopy(level, objtype, *objsp);
  /* back to normal */
  cut_things = FALSE;
  DeleteObjects(level, objtype, objsp);
  return newlevel;
}


/*
   Add the contents of one level to another level (paste the objects).
*/

void LevelPaste(LevelPtr dest, LevelPtr src)
{
  UInt16 tag_inc, n;

  /* find the highest tag number in the dest level */
  tag_inc = 0;
  for (n = 0; n < dest->num_linedefs; n++)
    if (dest->linedefs[n].tag > tag_inc)
      tag_inc = dest->linedefs[n].tag;
  for (n = 0; n < dest->num_sectors; n++)
    if (dest->sectors[n].tag > tag_inc)
      tag_inc = dest->sectors[n].tag;

  /* paste the Things */
  if (src->num_things > 0)
    {
      if (dest->num_things > 0)
        dest->things = (TPtr) ResizeFarMemory(dest->things, (UInt32) (dest->num_things + src->num_things) * sizeof(struct Thing));
      else
        dest->things = (TPtr) GetFarMemory((UInt32) src->num_things * sizeof(struct Thing));
      memcpy(dest->things + dest->num_things, src->things, src->num_things * sizeof(struct Thing));
      dest->num_things += src->num_things;
    }

  /* paste the LineDefs */
  if (src->num_linedefs > 0)
    {
      LDPtr cur;

      if (dest->num_linedefs > 0)
        dest->linedefs = (LDPtr) ResizeFarMemory(dest->linedefs, (UInt32) (dest->num_linedefs + src->num_linedefs) * sizeof(struct LineDef));
      else
        dest->linedefs = (LDPtr) GetFarMemory((UInt32) src->num_linedefs * sizeof(struct LineDef));
      memcpy(dest->linedefs + dest->num_linedefs, src->linedefs, src->num_linedefs * sizeof(struct LineDef));
      cur = dest->linedefs + dest->num_linedefs;
      for (n = src->num_linedefs; n; n--)
        {
          cur->start += dest->num_vertexes;
          cur->end += dest->num_vertexes;
          if (cur->tag > 0)
            cur->tag += tag_inc;
          cur->sidedef1 += dest->num_sidedefs;
          cur->sidedef2 += dest->num_sidedefs;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
      dest->num_linedefs += src->num_linedefs;
    }

  /* paste the SideDefs */
  if (src->num_sidedefs > 0)
    {
      SDPtr cur;

      if (dest->num_sidedefs > 0)
        dest->sidedefs = (SDPtr) ResizeFarMemory(dest->sidedefs, (UInt32) (dest->num_sidedefs + src->num_sidedefs) * sizeof(struct SideDef));
      else
        dest->sidedefs = (SDPtr) GetFarMemory((UInt32) src->num_sidedefs * sizeof(struct SideDef));
      memcpy(dest->sidedefs + dest->num_sidedefs, src->sidedefs, src->num_sidedefs * sizeof(struct SideDef));
      cur = dest->sidedefs + dest->num_sidedefs;
      for (n = src->num_sidedefs; n; n--)
        {
          cur->sector += dest->num_sectors;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
      dest->num_sidedefs += src->num_sidedefs;
    }

  /* paste the Sectors */
  if (src->num_sectors > 0)
    {
      SPtr cur;

      if (dest->num_sectors > 0)
        dest->sectors = (SPtr) ResizeFarMemory(dest->sectors, (UInt32) (dest->num_sectors + src->num_sectors) * sizeof(struct Sector));
      else
        dest->sectors = (SPtr) GetFarMemory((UInt32) src->num_sectors * sizeof(struct Sector));
      memcpy(dest->sectors + dest->num_sectors, src->sectors, src->num_sectors * sizeof(struct Sector));
      cur = dest->sectors + dest->num_sectors;
      for (n = src->num_sectors; n; n--)
        {
          if (cur->tag > 0 && cur->tag != 666 && cur->tag != 667)
            cur->tag += tag_inc;
#ifdef BUGGY_TURBOC_3
          cur = cur + 1;
#else
          cur++;
#endif
        }
      dest->num_sectors += src->num_sectors;
    }

  /* paste the Vertices */
  if (src->num_vertexes > 0)
    {
      if (dest->num_vertexes > 0)
        dest->vertexes = (VPtr) ResizeFarMemory(dest->vertexes, (UInt32) (dest->num_vertexes + src->num_vertexes) * sizeof(struct Vertex));
      else
        dest->vertexes = (VPtr) GetFarMemory((UInt32) src->num_vertexes * sizeof(struct Vertex));
      memcpy(dest->vertexes + dest->num_vertexes, src->vertexes, src->num_vertexes * sizeof(struct Vertex));
      dest->num_vertexes += src->num_vertexes;
      dest->made_map_changes = TRUE;
    }
  dest->made_changes = TRUE;
}


/*
   Move all objects on a level by some offset (used before pasting).
*/

void LevelMove(LevelPtr level, Int16 dx, Int16 dy)
{
  Int16 n;
  TPtr  curt;
  VPtr  curv;

  /* move the Things */
  curt = level->things;
  for (n = level->num_things; n; n--)
    {
      curt->xpos += dx;
      curt->ypos += dy;
#ifdef BUGGY_TURBOC_3
      curt = curt + 1;
#else
      curt++;
#endif
    }

  /* move the Vertices */
  curv = level->vertexes;
  for (n = level->num_vertexes; n; n--)
    {
      curv->x += dx;
      curv->y += dy;
#ifdef BUGGY_TURBOC_3
      curv = curv + 1;
#else
      curv++;
#endif
    }

  /* adjust the boundaries */
  level->map_minX += dx;
  level->map_maxX += dx;
  level->map_minY += dy;
  level->map_maxY += dy;

  if (dx != 0 || dy != 0)
    {
      if (level->num_vertexes > 0)
        level->made_map_changes = TRUE;
      level->made_changes = TRUE;
    }
}


/*
   Keep a copy of the current level in its Undo buffer.
*/

void CheckPointUndo(LevelPtr level, char *operation)
{
  if (Config.undo == FALSE)
    return;
  if (level->undo_buffer != NULL)
    ForgetLevelData(level->undo_buffer);
  level->undo_buffer = LevelDuplicate(level);
  level->undo_operation = operation;
}


/*
   Undo or Redo (swap the current level with an old copy of it).
*/

Bool UndoRedo(LevelPtr *levelp)
{
  if (Config.undo == FALSE)
    return FALSE;
  if ((*levelp)->undo_buffer != NULL)
    {
      (*levelp)->undo_buffer->undo_buffer = *levelp;
      *levelp = (*levelp)->undo_buffer;
      (*levelp)->undo_buffer->undo_buffer = NULL;
    }
  else
    return FALSE;
  return TRUE;
}

/* end of file */
