/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.

--- HISTORICAL COMMENTS FOLLOW ---

 * $Logfile: /DescentIII/Main/editor/WireframeGrWnd.cpp $
 * $Revision: 1.1.1.1 $
 * $Date: 2003-08-26 03:57:39 $
 * $Author: kevinb $
 *
 * Wireframe view of mine
 *
 * $Log: not supported by cvs2svn $
 *
 * 29    4/03/99 5:22p Matt
 * Don't render wireframe when editor rendering disabled
 *
 * 28    2/05/99 3:49p Jason
 * added more types to color coded topographical window
 *
 * 27    2/05/99 3:37p Jason
 * added/fixed features for the designers
 *
 * 26    2/03/99 1:11p Matt
 * Changed the paletted room current faces to be stored in a seperate
 * array, instead of in the room structure.
 *
 * 25    1/20/99 10:50a Jason
 * added new terrain
 *
 * 24    1/15/99 7:52p Chris
 * Updated ObjSetPos() to include a f_update_attach_children flag
 *
 * 23    10/04/98 1:25p Matt
 *
 * 22    9/23/98 12:30p Jason
 * fixed memory overwrite bug
 *
 * 21    5/20/98 3:48p Matt
 * Added suport for variable-depth face selection in wireframe view.
 *
 * 20    5/01/98 3:51p Jason
 * sped up terrain rendering by precaching all megacell lod bitmaps
 *
 * 19    4/23/98 6:53p Jason
 * changed wireframe map to work with new bitmap changes
 *
 * 18    4/23/98 6:38p Jason
 * made bitmaps use 1555 format
 *
 * 17    2/05/98 3:18p Matt
 * Renamed GetSegmentFromPoint() to GetCellFromPoint()
 *
 * 16    2/05/98 2:57p Matt
 * Changed code to use ObjSetPos()
 *
 * 15    2/04/98 6:23p Matt
 * Changed object room number to indicate a terrain cell via a flag.  Got
 * rid of the object flag which used to indicate terrain.
 *
 * 14    12/03/97 6:21p Jason
 * made selections undoable
 *
 * 13    10/15/97 5:20p Jason
 * did a HUGE overhaul of the bitmap system
 *
 * 12    10/03/97 3:39p Matt
 * Added seperate sets of wireframe view variables for mine & room views
 *
 * 11    9/17/97 11:34a Matt
 * Ripped out segment code
 *
 * 10    9/08/97 4:13p Jason
 * added colored lighting for the terrain
 *
 * 9     9/06/97 3:21p Matt
 * Reset cur portal when room changes
 *
 * 8     9/04/97 4:39p Matt
 * Added includes needed as a result of removing includes from d3edit.h
 *
 * 7     8/29/97 5:14p Matt
 * Switched segment selection function to do rooms instead
 *
 * 6     8/21/97 3:45p Matt
 * Reset curedge & curvert when changing curroom & curface
 *
 * 5     8/06/97 11:43a Jason
 * made lightmaps work correctly in the terrain
 *
 * 4     7/29/97 12:18p Matt
 * Made wireframe search work for rooms & faces
 *
 * 3     7/22/97 7:08p Matt
 * Cleaned up D3EditState, moving some vars in and some out, and renaming
 * and changing a few others
 *
 * 32    6/19/97 11:32a Jason
 * fixed bug where you could draw into the topo map even if you weren't
 * viewing terrain
 *
 * 31    6/12/97 12:43p Jason
 * fixed simple bug where bottom-right edges were being included in
 * terrain selection
 *
 * 30    6/11/97 2:11p Jason
 * made magnification actually zoom into the texture you're looking at
 *
 * 29    5/22/97 5:16p Jason
 * added "drawing" on the topo map
 *
 * 28    5/21/97 3:39p Jason
 * added terrain editing stuff
 *
 * 27    5/19/97 5:10p Jason
 * changes for our new abstracted renderer code
 *
 * 26    5/15/97 4:37p Jason
 * fixed some terrain bugs
 *
 * 25    5/15/97 3:27a Jason
 * implemented terrain magnification
 *
 * 24    4/11/97 4:20p Jason
 * now draws the player on the wireframe terrain window
 *
 * 23    4/10/97 4:39p Jason
 * Made selection manager stuff work with terrain topographical map
 *
 * 22    3/31/97 5:58p Matt
 * Revamped mine update flags
 *
 * 21    3/20/97 5:54p Matt
 * Made box selection code ignore very small boxes so normal mouse clicks
 * would work
 *
 * 20    3/20/97 5:01p Matt
 * Added new select-from-box code
 *
 * 19    3/20/97 3:02p Samir
 * Implemented Selection Manager in Wireframe window.
 *
 * 18    3/20/97 11:55a Jason
 * changes for terrain editing/drawing
 *
 * 17    3/02/97 4:25p Matt
 * Added shift-click to select/deselect segments in the wireframe window
 *
 * 16    2/28/97 2:21p Samir
 * You can drag a box within the wireframe window.
 *
 * 15    2/27/97 4:26p Matt
 * Fixed curseg selection status messages
 *
 * 14    2/19/97 5:37p Matt
 * Added code to set curseg from clicking in wireframe window
 *
 * 13    1/30/97 6:26p Samir
 * Minor change to drawing caption text
 *
 * 12    1/23/97 4:56p Samir
 * Handles saving screen positions of windows and tileability.
 *
 * 11    1/21/97 12:43p Samir
 * Floatable-resizable view
 *
 * $NoKeywords: $
 */

#include "stdafx.h"
#include "editor.h"
#include "WireframeGrWnd.h"
#include "3d.h"
#include "gametexture.h"
#include "drawworld.h"
#include "moveworld.h"
#include "selectedroom.h"
#include "terrain.h"
#include "bitmap.h"
#include "erooms.h"

#include "gr.h"
#include "mono.h"
#include "ddio.h"
#include "SelManager.h"
#include "room.h"
#include "lighting.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWireframeGrWnd

CWireframeGrWnd::CWireframeGrWnd() : CGrWnd("WireframeScreen") {
  m_Movable = FALSE;
  m_x1 = m_y1 = 0;
  m_y2 = TERRAIN_DEPTH;
  m_x2 = TERRAIN_WIDTH;
}

CWireframeGrWnd::~CWireframeGrWnd() {}

void DrawIntoTopoMap(int x, int y);

void CWireframeGrWnd::GetHighlightedRect(int *left, int *top, int *right, int *bottom) {
  *left = m_HighlightedRect.left;
  *top = m_HighlightedRect.top;
  *right = m_HighlightedRect.right;
  *bottom = m_HighlightedRect.bottom;
}

BEGIN_MESSAGE_MAP(CWireframeGrWnd, CWnd)
//{{AFX_MSG_MAP(CWireframeGrWnd)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_SIZE()
ON_WM_PAINT()
ON_WM_MOVE()
ON_WM_SYSCOMMAND()
ON_WM_NCACTIVATE()
ON_WM_NCPAINT()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_RBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//??extern void RenderWireFrame (grViewport *my_viewport);
//??extern void RenderTexFrame (grViewport *);

extern bool Disable_editor_rendering;

void CWireframeGrWnd::Render() {
  //	Don't do a thing if these objects aren't created yet
  if (!m_grScreen)
    return;
  if (!m_grViewport)
    return;

  if (Disable_editor_rendering)
    return;

  if (Editor_view_mode == VM_TERRAIN)
    DrawTerrainWorld(m_grViewport, &Wireframe_view->target, &Wireframe_view->orient, Wireframe_view->dist);
  else
    DrawWorld(m_grViewport, &Wireframe_view->target, &Wireframe_view->orient, Wireframe_view->dist, 0,
              Wireframe_view->rad);
}

/////////////////////////////////////////////////////////////////////////////
// CWireframeGrWnd message handlers

BOOL CWireframeGrWnd::Create(RECT &rect, BOOL movable, CWnd *pParent) {
  RECT new_rect;
  DWORD dwstyle;

  dwstyle = (movable ? GRWND_STYLE : GRWND_STATIC_STYLE);

  ::CopyRect(&new_rect, &rect);
  ::AdjustWindowRect(&new_rect, dwstyle, FALSE);

  //	We want this window to start at rect.left, rect.top.
  if (new_rect.left < rect.left) {
    int offx;
    offx = rect.left - new_rect.left;
    new_rect.left += offx;
    new_rect.right += offx;
  }
  if (new_rect.top < rect.top) {
    int offy;
    offy = rect.top - new_rect.top;
    new_rect.top += offy;
    new_rect.bottom += offy;
  }

  ::CopyRect(&rect, &new_rect);

  m_grCreated = TRUE;
  m_Movable = movable;

  lstrcpy(m_Name, "Wireframe");

  return CWnd::Create(NULL, m_Name, dwstyle, rect, pParent, IDC_WIREFRAME_WND);
}

int CWireframeGrWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) {
  if (CGrWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  theApp.wireframe_view = this;
  return 0;
}

void CWireframeGrWnd::OnDestroy() {
  CGrWnd::OnDestroy();
  theApp.wireframe_view = NULL;
}

void CWireframeGrWnd::OnSize(UINT nType, int cx, int cy) {
  CGrWnd::OnSize(nType, cx, cy);

  // TODO: Add your message handler code here
  if (m_Movable) { // Don't change these values if this is a static window
    D3EditState.wirescr_w = cx;
    D3EditState.wirescr_h = cy;
  }
}

void CWireframeGrWnd::OnPaint() {
  CPaintDC dc(this); // device context for painting

  //	Draw what's on the screen back page to the desktop
  m_grScreen->flip();
  draw_tile_caption(&dc);
}

void CWireframeGrWnd::OnMove(int x, int y) {
  CGrWnd::OnMove(x, y);

  //	must do this because actual (left,top) of window should be from title bar, not client
  if (m_Movable) { // Don't change these values if we aren't movable
    RECT rect;
    GetWindowRect(&rect);
    GetParent()->ScreenToClient(&rect);
    D3EditState.wirescr_x = rect.left;
    D3EditState.wirescr_y = rect.top;
  }
}

// Given an x,y mouse point, returns the terrain cell number at that point
int CWireframeGrWnd::GetCellFromPoint(int x, int y) {
  int width = m_grViewport->width();
  int height = m_grViewport->height();

  float xstep = (float)(m_x2 - m_x1) / (float)width;
  float ystep = (float)(abs(m_y2 - m_y1)) / (float)height;

  int destx = xstep * x;
  int desty = ystep * y;

  destx += m_x1;
  desty += m_y1;

  desty = (TERRAIN_DEPTH - 1) - desty; // flip due to origin difference

  return (desty * TERRAIN_WIDTH + destx);
}

void CWireframeGrWnd::OnSysCommand(UINT nID, LPARAM lParam) {
  // TODO: Add your message handler code here and/or call default

  switch (nID) {
  case SC_CLOSE:
    D3EditState.wirescr_visible = 0;
    break;
  }

  CWnd::OnSysCommand(nID, lParam);
}

BOOL CWireframeGrWnd::OnNcActivate(BOOL bActive) { return CGrWnd::OnNcActivate(bActive); }

void CWireframeGrWnd::OnNcPaint() { CGrWnd::OnNcPaint(); }

void CWireframeGrWnd::draw_tile_caption(CDC *dc) {
  if (!m_Movable) {
    dc->SetBkMode(TRANSPARENT);
    dc->SetTextColor(RGB(192, 192, 192));
    if (Editor_view_mode == VM_TERRAIN)
      dc->TextOut(10, 10, "Topographical", lstrlen("Topographical"));
    else
      dc->TextOut(10, 10, m_Name, lstrlen(m_Name));
  }
}

//	These functions handle special mouse operations within the wireframe window
void EndWireframeSel(editorSelectorManager *esm);

void CWireframeGrWnd::OnLButtonDown(UINT nFlags, CPoint point) {
  if (Editor_view_mode == VM_TERRAIN) {
    if (KEY_STATE(KEY_LCTRL)) {
      int cellnum = GetCellFromPoint(point.x, point.y);
      vector pos;

      ComputeTerrainSegmentCenter(&pos, cellnum);
      ObjSetPos(Viewer_object, &pos, MAKE_ROOMNUM(cellnum), NULL, false);

      State_changed = 1;
    } else if (KEY_STATE(KEY_LSHIFT)) {
      int seg = GetCellFromPoint(point.x, point.y);

      Terrain_tex_seg[Terrain_seg[seg].texseg_index].tex_index = D3EditState.texdlg_texture;
      World_changed = 1;

    } else
      SelManager.StartSelection(this, EndWireframeSel, point.x, point.y);

  } else // mine or room mode
  {
    int found_segnum, found_facenum;
    int find_mode = FM_CLOSEST;

    // See if we're clicking on the current face
    found_segnum = ROOMNUM(Curroomp);
    found_facenum = Curface;
    if (WireframeFindRoomFace(m_grViewport, point.x, point.y, &found_segnum, &found_facenum, FM_SPECIFIC)) {
      find_mode = FM_NEXT;
    }

  find_again:;

    if (WireframeFindRoomFace(m_grViewport, point.x, point.y, &found_segnum, &found_facenum, find_mode)) {
      if (KEY_STATE(KEY_LSHIFT) || KEY_STATE(KEY_RSHIFT)) { // ctrl + click means toggle select state
        int newstate;
        newstate = ToggleRoomSelectedState(found_segnum);
        State_changed = 1;
        EditorStatus("Room %d %s", found_segnum, newstate ? "selected" : "deselected");
      } else {
        if (Editor_view_mode == VM_ROOM) {
          ASSERT(found_segnum == D3EditState.current_room);
          Current_faces[D3EditState.current_room - FIRST_PALETTE_ROOM] = found_facenum;
          EditorStatus("Current face set to %d", found_facenum);
        } else {
          Curroomp = &Rooms[found_segnum];
          Curface = found_facenum;
          Curedge = Curvert = 0;
          Curportal = -1;
          EditorStatus("Current room:face set to %d:%d", ROOMNUM(Curroomp), Curface);
        }
        State_changed = 1;
      }
    } else if (find_mode == FM_NEXT) {
      find_mode = FM_CLOSEST;
      goto find_again;
    }

    //	no more dragging, but something is highlighted.
    //	so we will redraw world if mouse is pressed

    SelManager.StartSelection(this, EndWireframeSel, point.x, point.y);
  }

  CWnd::OnLButtonDown(nFlags, point);
}

void CWireframeGrWnd::OnMouseMove(UINT nFlags, CPoint point) {
  if (Editor_view_mode == VM_TERRAIN)
    DrawIntoTopoMap(point.x, point.y);
  CWnd::OnMouseMove(nFlags, point);
}

void CWireframeGrWnd::OnLButtonUp(UINT nFlags, CPoint point) { CWnd::OnLButtonUp(nFlags, point); }

// How big a box needs to be before we recognize it
#define MIN_BOX_SIZE 3

void EndWireframeSel(editorSelectorManager *esm) {
  int left, top, right, bot;
  int n;

  esm->GetSelectedRect(&left, &top, &right, &bot);

  if ((right - left < MIN_BOX_SIZE) || (bot - top < MIN_BOX_SIZE))
    return;

  if (Editor_view_mode == VM_TERRAIN) {
    int i;

    if (!(KEY_STATE(KEY_RALT))) {
      for (i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++)
        TerrainSelected[i] = 0;
      Num_terrain_selected = 0;
    }

    RECT rect;
    int x1, y1, x2, y2;
    esm->m_OwnerWnd->GetWindowRect(&rect);
    esm->m_OwnerWnd->ScreenToClient(&rect);
    theApp.wireframe_view->GetMagCoords(&x1, &y1, &x2, &y2);

    float xstep = (float)(x2 - x1) / (float)rect.right;
    float ystep = (float)(abs(y2 - y1)) / (float)rect.bottom;

    left *= xstep;
    right *= xstep;
    top *= ystep;
    bot *= ystep;

    left += x1;
    right += x1;
    top += y1;
    bot += y1;

    // We switch top and bottom due to the fact that our origin is in the bottom
    // left instead of the top left
    bot = (TERRAIN_DEPTH - 1) - bot;
    top = (TERRAIN_DEPTH - 1) - top;

    int t;
    int state = 1;
    if (KEY_STATE(KEY_RALT))
      state = 0;

    for (i = bot; i <= top; i++) {
      for (t = left; t <= right; t++) {
        if (!state) {
          if (TerrainSelected[i * TERRAIN_WIDTH + t])
            Num_terrain_selected--;
          TerrainSelected[i * TERRAIN_WIDTH + t] = 0;
        } else {
          TerrainSelected[i * TERRAIN_WIDTH + t] = 1;
          Num_terrain_selected++;
        }
      }
    }

    if (KEY_STATE(KEY_LALT)) {

      theApp.wireframe_view->SetMagCoords(left, (TERRAIN_DEPTH - 1) - top, right, (TERRAIN_DEPTH - 1) - bot);
    }

    World_changed = 1;
  } else {
    if (!(KEY_STATE(KEY_LCTRL) || KEY_STATE(KEY_RCTRL))) // ctrl + select means add to list
      ClearRoomSelectedList();

    n = SelectRoomsInBox(NULL, left, top, right, bot);
    EditorStatus("%d rooms selected", n);
  }

  esm->EndSelection();
}

void CWireframeGrWnd::DrawPlayerOnTerrain() {
  static int lastx = -1, lastz = -1;
  static uint16_t last_color;

  int seg = GetTerrainCellFromPos(&Viewer_object->pos);
  if (seg < 0)
    return;

  int x = seg % TERRAIN_WIDTH;
  int z = seg / TERRAIN_WIDTH;

  //	x-=(m_x1);
  //	z-=m_y1;

  z = (TERRAIN_DEPTH - 1) - z;

  RECT rect;

  GetWindowRect(&rect);
  ScreenToClient(&rect);

  float xstep = (float)(m_x2 - m_x1) / (float)(rect.right - rect.left);
  float ystep = (float)(abs(m_y2 - m_y1)) / (float)(rect.bottom - rect.top);

  m_grViewport->lock();

  x = (float)x / xstep;
  z = (float)z / ystep;

  if (x < 0 || x > rect.right)
    goto getout;
  if (z < 0 || z > rect.bottom)
    goto getout;

  if (lastx != -1 && lastx < (rect.right - rect.left) && lastz < (rect.bottom - rect.top))
    m_grViewport->setpixel(last_color, lastx, lastz);

  lastx = x;
  lastz = z;
  last_color = m_grViewport->getpixel(lastx, lastz);

  m_grViewport->setpixel(GR_RGB(255, 255, 255), x, z);

getout:
  m_grViewport->unlock();
  m_grScreen->flip();
}

void DrawPlayerOnWireframe() {
  if (theApp.wireframe_view)
    theApp.wireframe_view->DrawPlayerOnTerrain();
}

void CWireframeGrWnd::GetMagCoords(int *x1, int *y1, int *x2, int *y2) {
  *x1 = m_x1;
  *x2 = m_x2;
  *y1 = m_y1;
  *y2 = m_y2;
}
void CWireframeGrWnd::SetMagCoords(int x1, int y1, int x2, int y2) {
  m_x1 = x1;
  m_x2 = x2;
  m_y1 = y1;
  m_y2 = y2;
}

// Redraws only one terrain cell in the wireframe view
void CWireframeGrWnd::DrawTerrainCell(int seg) {
  grSurface *cur_surf;
  uint16_t *destptr;
  int i, t, x, y;
  int segx = seg % TERRAIN_WIDTH;
  int segz = seg / TERRAIN_WIDTH;

  cur_surf = m_grViewport->lock();
  destptr = (uint16_t *)cur_surf->data();
  int rowsize = cur_surf->rowsize() / 2;
  int x1, y1, x2, y2;

  theApp.wireframe_view->GetMagCoords(&x1, &y1, &x2, &y2);

  fix xstep = IntToFix(x2 - x1) / cur_surf->width();
  fix ystep = IntToFix(abs(y2 - y1)) / cur_surf->height();

  float cellwidth = 1.0 / FixToFloat(xstep);
  float cellheight = 1.0 / FixToFloat(ystep);

  int ystart = cellheight * (((TERRAIN_DEPTH - 1) - segz) - y1);
  int xstart = cellwidth * (segx - x1);

  cellheight = ceil(cellheight);
  cellwidth = ceil(cellwidth);

  x = segx;
  y = segz; // flip due to origin difference
  uint16_t fadepixel = 0;

  int bm_handle = GameTextures[Terrain_tex_seg[Terrain_seg[(y * TERRAIN_WIDTH + x)].texseg_index].tex_index].bm_handle;

  uint16_t pix = *bm_data(bm_handle, 0);

  if (TerrainSelected[y * TERRAIN_WIDTH + x]) {
    int red = ((fadepixel >> 10) & 0x1f) << 3;
    int green = ((fadepixel >> 5) & 0x1f) << 3;
    int blue = (fadepixel & 0x1f) << 3;

    red <<= 2;
    green <<= 2;
    blue <<= 2;

    red = std::min(255, red);
    green = std::min(255, green);
    blue = std::min(255, blue);

    fadepixel = GR_RGB16(red, green, blue);
  }

  for (i = 0; i < (int)cellheight; i++)
    for (t = 0; t < (int)cellwidth; t++) {

      destptr[(i + ystart) * rowsize + t + xstart] = fadepixel;
    }
  m_grViewport->unlock();
}
/*

// draws a topographical representation of our terrain in the wireframe window
void CWireframeGrWnd::DrawTerrainWorld(grViewport *vp,vector *view_target,matrix *view_orient,float view_dist)
{
        grSurface *cur_surf;
        uint16_t *destptr;
        int lightval;
        int i,t;

        cur_surf=vp->lock();
        destptr=(uint16_t*)cur_surf->data();
        int rowsize=cur_surf->rowsize()/2;
        int x1,y1,x2,y2;

        theApp.wireframe_view->GetMagCoords (&x1,&y1,&x2,&y2);

        fix xstep=IntToFix(x2-x1)/cur_surf->width();
        fix ystep=IntToFix(abs(y2-y1))/cur_surf->height();
        fix u=IntToFix (x1);
        fix v=IntToFix (y1);

        for (i=0;i<cur_surf->height();i++,v+=ystep,u=IntToFix(x1))
                for (t=0;t<cur_surf->width();t++,u+=xstep)
                {
                        int x,y;
                        x=FixToInt (u);
                        y=FixToInt (v);

                        y=(TERRAIN_DEPTH-1)-y;	// flip due to origin difference

                        uint16_t fadepixel;

                        int bm_handle=GameTextures[Terrain_seg[y*TERRAIN_WIDTH+x].tex_index].bm_handle;
                        lightval=(Terrain_seg[y*TERRAIN_WIDTH+x].light)*(MAX_TEXTURE_SHADES-1);

                        uint16_t pix;

                        pix=*bm_data(bm_handle,0);
                        fadepixel=(TexShadeTable16[lightval][pix>>8])+TexShadeTable8[lightval][pix & 0xFF];


                        if (TerrainSelected[y*TERRAIN_WIDTH+x])
                        {
                                int red=(fadepixel>>11)<<3;
                                int green=((fadepixel >> 5)&0x3f)<<2;
                                int blue=(fadepixel & 0x1f)<<3;

                                red<<=2;
                                green<<=2;
                                blue<<=2;

                                red=std::min(255,red);
                                green=std::min(255,green);
                                blue=std::min(255,blue);



                                fadepixel=GR_RGB16(red,green,blue);
                        }


                        destptr[i*rowsize+t]=fadepixel;
                }
        vp->unlock();

}*/

// draws a topographical representation of our terrain in the wireframe window
void CWireframeGrWnd::DrawTerrainWorld(grViewport *vp, vector *view_target, matrix *view_orient, float view_dist) {
  grSurface *cur_surf;
  uint16_t *destptr;
  float lightval;
  int i, t;

  cur_surf = vp->lock();
  destptr = (uint16_t *)cur_surf->data();
  int rowsize = cur_surf->rowsize() / 2;
  int x1, y1, x2, y2;

  theApp.wireframe_view->GetMagCoords(&x1, &y1, &x2, &y2);

  fix xstep = IntToFix(x2 - x1) / cur_surf->width();
  fix ystep = IntToFix(abs(y2 - y1)) / cur_surf->height();
  fix u = IntToFix(x1);
  fix v = IntToFix(y1);

  fix subx, suby;

  for (i = 0; i < cur_surf->height(); i++, v += ystep, u = IntToFix(x1))
    for (t = 0; t < cur_surf->width(); t++, u += xstep) {
      int x, y;
      x = FixToInt(u);
      y = FixToInt(v);

      y = (TERRAIN_DEPTH - 1) - y; // flip due to origin difference

      uint16_t fadepixel;

      int bm_handle =
          GameTextures[Terrain_tex_seg[Terrain_seg[y * TERRAIN_WIDTH + x].texseg_index].tex_index].bm_handle;
      lightval = Ubyte_to_float[(Terrain_seg[y * TERRAIN_WIDTH + x].l)];

      uint16_t pix;
      uint16_t *data = bm_data(bm_handle, 0);
      int w = bm_w(bm_handle, 0);

      subx = (u & 0xFFFF) * w;
      suby = (v & 0xFFFF) * bm_h(bm_handle, 0);
      subx >>= 16;
      suby >>= 16;

      pix = data[suby * w + subx];
      ddgr_color pix32 = GR_16_TO_COLOR(pix);

      int pred = (GR_COLOR_RED(pix32) * lightval);
      int pgreen = (GR_COLOR_GREEN(pix32) * lightval);
      int pblue = (GR_COLOR_BLUE(pix32) * lightval);

      fadepixel = GR_RGB16(pred, pgreen, pblue);

      if (TerrainSelected[y * TERRAIN_WIDTH + x]) {
        int red = ((fadepixel >> 10) & 0x1f) << 3;
        int green = ((fadepixel >> 5) & 0x1f) << 3;
        int blue = (fadepixel & 0x1f) << 3;

        red <<= 2;
        green <<= 2;
        blue <<= 2;

        red = std::min(255, red);
        green = std::min(255, green);
        blue = std::min(255, blue);

        fadepixel = GR_RGB16(red, green, blue);
      }

      if (Terrain_seg[y * TERRAIN_WIDTH + x].objects != -1) {
        object *obj = &Objects[Terrain_seg[y * TERRAIN_WIDTH + x].objects];

        if (obj->type == OBJ_ROBOT)
          fadepixel = GR_RGB16(255, 0, 0);
        else if (obj->type == OBJ_POWERUP)
          fadepixel = GR_RGB16(0, 128, 255);
        else if (obj->type == OBJ_PLAYER || obj->type == OBJ_GHOST)
          fadepixel = GR_RGB16(0, 255, 0);
        else if (obj->type == OBJ_CLUTTER)
          fadepixel = GR_RGB16(255, 0, 255);
      }

      destptr[i * rowsize + t] = fadepixel;
    }
  vp->unlock();
}

void CWireframeGrWnd::OnRButtonDown(UINT nFlags, CPoint point) {
  if (Editor_view_mode == VM_TERRAIN) {
    m_x1 = 0;
    m_y1 = 0;
    m_x2 = TERRAIN_WIDTH;
    m_y2 = TERRAIN_DEPTH;

    State_changed = 1;
  }
  CWnd::OnRButtonDown(nFlags, point);
}

// Lets the user draw free hand into the topographical map
void DrawIntoTopoMap(int x, int y) {
  int i, t, segx, segz;

  if (KEY_STATE(KEY_LSHIFT)) {
    int seg = theApp.wireframe_view->GetCellFromPoint(x, y);
    if (seg < 0 || seg >= TERRAIN_WIDTH * TERRAIN_DEPTH)
      Int3(); // get Jason

    for (i = -1; i <= 1; i++) {
      for (t = -1; t <= 1; t++) {
        segx = seg % TERRAIN_WIDTH;
        segz = seg / TERRAIN_WIDTH;

        if ((segz + i) < 0 || segz + i >= TERRAIN_DEPTH)
          continue;
        if ((segx + t) < 0 || segx + t >= TERRAIN_WIDTH)
          continue;

        segz += i;
        segx += t;

        Terrain_tex_seg[Terrain_seg[(segz * TERRAIN_WIDTH) + segx].texseg_index].tex_index = D3EditState.texdlg_texture;
        theApp.wireframe_view->DrawTerrainCell((segz * TERRAIN_WIDTH) + segx);
      }
    }
    theApp.wireframe_view->Invalidate();
  }
}
