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

// PathPadDialog.cpp : implementation file
//

#include "stdafx.h"
#include "editor.h"
#include "PathPadDialog.h"
#include "EditLineDialog.h"
#include "epath.h"
#include "object.h"
#include "room.h"
#include "FindIntersection.h"
#include "ebnode.h"
#include "boa.h"
#include "object.h"

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

/////////////////////////////////////////////////////////////////////////////
// CPathPadDialog dialog

CPathPadDialog::CPathPadDialog(CWnd *pParent /*=NULL*/) : CKeypadDialog(CPathPadDialog::IDD, pParent) {
  //{{AFX_DATA_INIT(CPathPadDialog)
  // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
}

void CPathPadDialog::DoDataExchange(CDataExchange *pDX) {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CPathPadDialog)
  // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPathPadDialog, CDialog)
//{{AFX_MSG_MAP(CPathPadDialog)
ON_EN_KILLFOCUS(IDC_CURRENT_NODE_EDIT, OnKillfocusCurrentNodeEdit)
ON_BN_CLICKED(IDC_PATHPAD_ADD_PATH, OnPathpadAddPath)
ON_BN_CLICKED(IDC_PATHPAD_INSERT_NODE, OnPathpadInsertNode)
ON_BN_CLICKED(IDC_PATHPAD_DELETE_NODE, OnPathpadDeleteNode)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_FORWARD, OnPathpadMoveForward)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_BACKWARD, OnPathpadMoveBackward)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_LEFT, OnPathpadMoveLeft)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_RIGHT, OnPathpadMoveRight)
ON_CBN_SELENDOK(IDC_PATHPAD_PULLDOWN, OnSelendokPathpadPulldown)
ON_BN_CLICKED(IDC_PATHPAD_CENTER_VIEW, OnPathpadCenterView)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_DOWN, OnPathpadMoveDown)
ON_BN_CLICKED(IDC_PATHPAD_MOVE_UP, OnPathpadMoveUp)
ON_BN_CLICKED(IDC_PATHPAD_PREV_NODE, OnPathpadPrevNode)
ON_BN_CLICKED(IDC_PATHPAD_NEXT_NODE, OnPathpadNextNode)
ON_BN_CLICKED(IDC_DELETE_PATH, OnDeletePath)
ON_BN_CLICKED(IDC_SHOW_NODES_CHECK, OnShowNodesCheck)
ON_WM_HELPINFO()
ON_BN_CLICKED(IDC_PATH_MOVE_OBJECT_BUTTON, OnPathMoveObjectButton)
ON_BN_CLICKED(IDC_PATH_REORIENT_VIEWER_BUTTON, OnPathReorientViewerButton)
ON_EN_KILLFOCUS(IDC_PATH_INC_TEXT, OnKillfocusPathIncText)
ON_WM_PAINT()
ON_BN_CLICKED(IDC_PATH_MOVE_NODE_TO_CURRENT_OBJECT, OnPathMoveNodeToCurrentObject)
ON_BN_CLICKED(IDC_PATH_MOVE_OBJ_NO_ORIENT, OnPathMoveObjNoOrient)
ON_BN_CLICKED(IDC_PATH_VERIFY_BUTTON, OnPathVerifyButton)
ON_BN_CLICKED(IDC_AIN_AUTO_EDGE_NODE, OnAinAutoEdgeNode)
ON_BN_CLICKED(IDC_AIN_BACK, OnAinBack)
ON_BN_CLICKED(IDC_AIN_CREATE_NODE_AT_VIEWER, OnAinCreateNodeAtViewer)
ON_BN_CLICKED(IDC_AIN_CREATE_NODES, OnAinCreateNodes)
ON_CBN_SELENDOK(IDC_AIN_CURRENT_NODE, OnSelendokAinCurrentNode)
ON_BN_CLICKED(IDC_AIN_DELETE_EDGE, OnAinDeleteEdge)
ON_BN_CLICKED(IDC_AIN_DELETE_NODE, OnAinDeleteNode)
ON_BN_CLICKED(IDC_AIN_DOWN, OnAinDown)
ON_BN_CLICKED(IDC_AIN_FORWARD, OnAinForward)
ON_BN_CLICKED(IDC_AIN_INSERT_NODE_ON_EDGE, OnAinInsertNodeOnEdge)
ON_BN_CLICKED(IDC_AIN_LEFT, OnAinLeft)
ON_BN_CLICKED(IDC_AIN_MAKE_EDGE, OnAinMakeEdge)
ON_EN_KILLFOCUS(IDC_AIN_MOVE_AMOUNT, OnKillfocusAinMoveAmount)
ON_BN_CLICKED(IDC_AIN_MOVE_NODE_TO_VIEWER, OnAinMoveNodeToViewer)
ON_BN_CLICKED(IDC_AIN_MPNS, OnAinMpns)
ON_CBN_SELENDOK(IDC_AIN_OTHER_NODE, OnSelendokAinOtherNode)
ON_BN_CLICKED(IDC_AIN_RESET_ROOM_NODES, OnAinResetRoomNodes)
ON_BN_CLICKED(IDC_AIN_RIGHT, OnAinRight)
ON_BN_CLICKED(IDC_AIN_SEL_NODE_EDGES, OnAinSelNodeEdges)
ON_BN_CLICKED(IDC_AIN_SEL_ROOM, OnAinSelRoom)
ON_BN_CLICKED(IDC_AIN_UP, OnAinUp)
ON_BN_CLICKED(IDC_AIN_SEL_ROOM_AND_NEXT_ROOMS, OnAinSelRoomAndNextRooms)
ON_BN_CLICKED(IDC_AIN_VERIFY_LEVEL, OnAinVerifyLevel)
ON_BN_CLICKED(IDC_AIN_VERIFY_ROOM, OnAinVerifyRoom)
ON_BN_CLICKED(IDC_AIN_VIEW_ALL_NODES, OnAinViewAllNodes)
ON_BN_CLICKED(IDC_AIN_VIEW_NONE, OnAinViewNone)
ON_BN_CLICKED(IDC_AIN_VIEW_ROOM, OnAinViewRoom)
ON_BN_CLICKED(IDC_AIN_VIEW_ROOM_AND_NEXT_ROOMS, OnAinViewRoomAndNextRooms)
ON_CBN_SETFOCUS(IDC_AIN_CURRENT_NODE, OnSetfocusAinCurrentNode)
ON_CBN_SETFOCUS(IDC_AIN_OTHER_NODE, OnSetfocusAinOtherNode)
ON_BN_CLICKED(IDC_IENAV_BUTTON, OnIenavButton)
ON_BN_CLICKED(IDC_MVTN_BUTTON, OnMvtnButton)
ON_BN_CLICKED(IDC_AER_BUTTON, OnAerButton)
ON_BN_CLICKED(IDC_ONTVGP_BUTTON, OnOntvgpButton)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPathPadDialog message handlers

void CPathPadDialog::OnKillfocusCurrentNodeEdit() {
  CEdit *ebox;
  char str[20];
  int curpath = D3EditState.current_path;

  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  ebox = (CEdit *)GetDlgItem(IDC_CURRENT_NODE_EDIT);
  ebox->GetWindowText(str, 20);

  // Do the base 1 to base 0 conversion
  D3EditState.current_node = atoi(str) - 1;

  if (D3EditState.current_node >= GamePaths[curpath].num_nodes)
    D3EditState.current_node = GamePaths[curpath].num_nodes - 1;
  else if (D3EditState.current_node < 0)
    D3EditState.current_node = 0;

  UpdateDialog();
}

void CPathPadDialog::OnPathpadAddPath() {

  int curnode;
  char *namestr;
  int pathnum;
  int dup_test;

  pathnum = AllocGamePath();
  if (pathnum < 0) {
    return;
  }

  CEditLineDialog dlg("Enter path name", this);
  int ret = dlg.DoModal();

  if (ret == IDCANCEL) {
    FreeGamePath(pathnum);
    return;
  }

  namestr = (char *)dlg.GetText();
  dup_test = FindGamePathName(namestr);
  if (dup_test != -1) {
    OutrageMessageBox("PATH NOT CREATED: A path named %s already exists.", namestr);
    FreeGamePath(pathnum);
    return;
  }
  strcpy(GamePaths[pathnum].name, namestr);

  curnode = InsertNodeIntoPath(pathnum, -1, 0);
  if (curnode < 0)
    return;

  D3EditState.current_path = pathnum;
  D3EditState.current_node = curnode;
  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadInsertNode() {
  int curpath = D3EditState.current_path;
  int curnode = D3EditState.current_node;

  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  curnode = InsertNodeIntoPath(curpath, curnode, 0);
  if (curnode < 0)
    return;

  D3EditState.current_node = curnode;
  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadDeleteNode() {
  int curpath = D3EditState.current_path;
  int curnode = D3EditState.current_node;

  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  if (GamePaths[curpath].num_nodes == 1) {
    MessageBox("This is the only node in this path.  If you wish to delete this path, click the 'Delete Path' button.");
    return;
  }

  DeleteNodeFromPath(curpath, curnode);

  // If this was the ending node, move it back one
  if (curnode == GamePaths[curpath].num_nodes)
    D3EditState.current_node = curnode - 1;

  State_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveForward() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.fvec * D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveBackward() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.fvec * -D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveLeft() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.rvec * -D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveRight() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.rvec * D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnSelendokPathpadPulldown() {
  int i, cur;
  char name[200];

  cur = SendDlgItemMessage(IDC_PATHPAD_PULLDOWN, CB_GETCURSEL, 0, 0);
  SendDlgItemMessage(IDC_PATHPAD_PULLDOWN, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);

  i = FindGamePathName(name);

  D3EditState.current_path = i;
  D3EditState.current_node = 0;

  State_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadCenterView() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  game_path *gp = &GamePaths[p];

  vector attempted_pos;
  vector subvec = {0.0, 0.0, 0.0}; // changed for now

  attempted_pos = gp->pathnodes[n].pos - subvec;

  fvi_query fq;
  fvi_info hit_info;

  fq.p0 = &GamePaths[p].pathnodes[n].pos;
  fq.startroom = GamePaths[p].pathnodes[n].roomnum;
  fq.p1 = &attempted_pos;
  fq.rad = 0.0f;
  fq.thisobjnum = -1;
  fq.ignore_obj_list = NULL;
  fq.flags = FQ_TRANSPOINT | FQ_IGNORE_RENDER_THROUGH_PORTALS;

  int hit = fvi_FindIntersection(&fq, &hit_info);

  if (hit != HIT_NONE)
    Int3(); // what does it mean that we hit something?

  ObjSetPos(Viewer_object, &hit_info.hit_pnt, hit_info.hit_room, NULL, false);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveDown() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.uvec * -D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadMoveUp() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  vector delta_movement = Viewer_object->orient.uvec * D3EditState.node_movement_inc;

  MovePathNode(p, n, &delta_movement);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadPrevNode() {
  if (Num_game_paths < 1)
    return;

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  n--;

  if (n < 0)
    n = GamePaths[p].num_nodes - 1;

  D3EditState.current_node = n;

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnPathpadNextNode() {
  if (Num_game_paths < 1)
    return;

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  n++;

  n %= GamePaths[p].num_nodes;
  D3EditState.current_node = n;

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::UpdateDialog() {
  int n = D3EditState.current_path;
  char str[50];
  CEdit *ebox;

  SendDlgItemMessage(IDC_PATHPAD_PULLDOWN, CB_RESETCONTENT, 0, 0);

  for (int i = 0; i < MAX_GAME_PATHS; i++) {
    if (GamePaths[i].used)
      SendDlgItemMessage(IDC_PATHPAD_PULLDOWN, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)GamePaths[i].name);
  }
  SendDlgItemMessage(IDC_PATHPAD_PULLDOWN, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)GamePaths[n].name);

  ebox = (CEdit *)GetDlgItem(IDC_CURRENT_NODE_EDIT);
  sprintf(str, "%d", D3EditState.current_node + 1);
  ebox->SetWindowText(str);

  ebox = (CEdit *)GetDlgItem(IDC_PATH_INC_TEXT);
  sprintf(str, "%f", D3EditState.node_movement_inc);
  ebox->SetWindowText(str);

  if (n < Num_game_paths && D3EditState.current_node < GamePaths[n].num_nodes) {
    ebox = (CEdit *)GetDlgItem(IDC_PATHPAD_CUR_NODE_ROOM);
    if (!ROOMNUM_OUTSIDE(GamePaths[n].pathnodes[D3EditState.current_node].roomnum))
      sprintf(str, "Current Mine Room: %d", GamePaths[n].pathnodes[D3EditState.current_node].roomnum);
    else
      sprintf(str, "Current Terrain Cell: %d", (0x7FFFFFFF & GamePaths[n].pathnodes[D3EditState.current_node].roomnum));
    ebox->SetWindowText(str);
  }

  ebox = (CEdit *)GetDlgItem(IDC_PATHPAD_NUM_NODES);
  sprintf(str, "Number of nodes: %d", GamePaths[n].num_nodes);
  ebox->SetWindowText(str);

  CheckDlgButton(IDC_SHOW_NODES_CHECK, Show_paths ? 1 : 0);
}

void CPathPadDialog::OnDeletePath() {
  int ret;
  int n = D3EditState.current_path;

  if (Num_game_paths < 1)
    return;

  ret = MessageBox("Are you sure you want to delete this path?", GamePaths[n].name, MB_YESNO);
  if (ret == IDNO)
    return;

  FreeGamePath(n);

  D3EditState.current_path = GetNextPath(n);
  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnShowNodesCheck() {
  int c = IsDlgButtonChecked(IDC_SHOW_NODES_CHECK);

  if (c)
    Show_paths = 1;
  else
    Show_paths = 0;

  TV_changed = 1;
}

BOOL CPathPadDialog::OnHelpInfo(HELPINFO *pHelpInfo) {
  WinHelp(HID_PATHSTAB, HELP_CONTEXT);
  return TRUE;
  // return CDialog::OnHelpInfo(pHelpInfo);
}

void CPathPadDialog::OnPathMoveObjectButton() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  if (Cur_object_index != -1) //	we have a selected object
  {

    int p = D3EditState.current_path;
    int n = D3EditState.current_node;

    game_path *gp = &GamePaths[p];

    ObjSetPos(&Objects[Cur_object_index], &gp->pathnodes[n].pos, gp->pathnodes[n].roomnum, NULL, false);

    matrix node_orient;

    vm_VectorToMatrix(&node_orient, &gp->pathnodes[n].fvec, &gp->pathnodes[n].uvec, NULL);
    ObjSetOrient(&Objects[Cur_object_index], &node_orient);

    World_changed = 1;
  }
}

void CPathPadDialog::OnPathReorientViewerButton() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  int p = D3EditState.current_path;
  int n = D3EditState.current_node;

  game_path *gp = &GamePaths[p];

  matrix node_orient;

  vm_VectorToMatrix(&node_orient, &gp->pathnodes[n].fvec, &gp->pathnodes[n].uvec, NULL);
  ObjSetOrient(Viewer_object, &node_orient);

  World_changed = 1;
  UpdateDialog();
}

void CPathPadDialog::OnKillfocusPathIncText() {
  CEdit *ebox;
  char str[20];

  ebox = (CEdit *)GetDlgItem(IDC_PATH_INC_TEXT);
  ebox->GetWindowText(str, 20);

  // Do the base 1 to base 0 conversion
  sscanf(str, "%f", &D3EditState.node_movement_inc);

  if (D3EditState.node_movement_inc <= 0.0f || D3EditState.node_movement_inc > 32000.0f)
    D3EditState.node_movement_inc = 1.0f;

  UpdateDialog();
}

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

  UpdateDialog();
}

void CPathPadDialog::OnPathMoveNodeToCurrentObject() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  if (Cur_object_index != -1) //	we have a selected object
  {

    int p = D3EditState.current_path;
    int n = D3EditState.current_node;

    game_path *gp = &GamePaths[p];
    MovePathNodeToPos(p, n, &Objects[Cur_object_index].pos);

    World_changed = 1;
    UpdateDialog();
  }
}

void CPathPadDialog::OnPathMoveObjNoOrient() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  if (Cur_object_index != -1) //	we have a selected object
  {

    int p = D3EditState.current_path;
    int n = D3EditState.current_node;

    game_path *gp = &GamePaths[p];

    ObjSetPos(&Objects[Cur_object_index], &gp->pathnodes[n].pos, gp->pathnodes[n].roomnum, NULL, false);

    // matrix node_orient;

    World_changed = 1;
    UpdateDialog();
  }
}

void CPathPadDialog::OnPathVerifyButton() {}

int BNode_current_id = 0;
int BNode_current_room = 0;
int BNode_other_id = 0;
int BNode_other_room = 0;

float BNode_move_amount = 1.0f;

void CPathPadDialog::OnAinAutoEdgeNode() {
  EBNode_AutoEdgeNode(BNode_current_id, BNode_current_room);
  EBNode_VerifyGraph();
  World_changed = 1;
}

void CPathPadDialog::OnAinBack() {
  vector diff = -Viewer_object->orient.fvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinCreateNodeAtViewer() {
  int node = EBNode_AddNode(Viewer_object->roomnum, &Viewer_object->pos, true, true);

  if (node != -1) {
    World_changed = 1;
  }

  //	EBNode_VerifyGraph();
}

void CPathPadDialog::OnAinCreateNodes() {
  MakeBOA();
  EBNode_MakeFirstPass();
  World_changed = 1;
}

void CPathPadDialog::OnSelendokAinCurrentNode() {
  int cur;
  char name[200];

  cur = SendDlgItemMessage(IDC_AIN_CURRENT_NODE, CB_GETCURSEL, 0, 0);
  SendDlgItemMessage(IDC_AIN_CURRENT_NODE, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);

  if (sscanf(name, "Room %d, Node %d", &BNode_current_room, &BNode_current_id) != 2) {
    sscanf(name, "Region %d, Node %d", &BNode_current_room, &BNode_current_id);
    BNode_current_room += Highest_room_index + 1;
  }

  BNode_current_id--;
}

void CPathPadDialog::OnAinDeleteEdge() {
  EBNode_RemoveEdge(BNode_current_id, BNode_current_room, BNode_other_id, BNode_other_room);
  World_changed = 1;

  EBNode_VerifyGraph();
}

void CPathPadDialog::OnAinDeleteNode() {
  EBNode_RemoveNode(BNode_current_room, BNode_current_id);
  World_changed = 1;

  EBNode_VerifyGraph();
}

void CPathPadDialog::OnAinDown() {
  vector diff = -Viewer_object->orient.uvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinForward() {
  vector diff = Viewer_object->orient.fvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinInsertNodeOnEdge() {
  EBNode_InsertNodeOnEdge(BNode_current_id, BNode_current_room, BNode_other_id, BNode_other_room);
  EBNode_VerifyGraph();
  World_changed = 1;
}

void CPathPadDialog::OnAinLeft() {
  vector diff = -Viewer_object->orient.rvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinMakeEdge() {
  EBNode_AddEdge(BNode_current_id, BNode_current_room, BNode_other_id, BNode_other_room);
  EBNode_VerifyGraph();
  World_changed = 1;
}

void CPathPadDialog::OnKillfocusAinMoveAmount() {
  CEdit *ebox;
  char str[20];

  ebox = (CEdit *)GetDlgItem(IDC_AIN_MOVE_AMOUNT);
  ebox->GetWindowText(str, 20);
  sscanf(str, "%f", &BNode_move_amount);

  UpdateDialog();
}

void CPathPadDialog::OnAinMoveNodeToViewer() {
  vector diff = Viewer_object->pos;

  EBNode_Move(false, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinMpns() {
  // TODO: Add your control notification handler code here
}

void CPathPadDialog::OnSelendokAinOtherNode() {
  int cur;
  char name[200];

  cur = SendDlgItemMessage(IDC_AIN_OTHER_NODE, CB_GETCURSEL, 0, 0);
  SendDlgItemMessage(IDC_AIN_OTHER_NODE, CB_GETLBTEXT, cur, (LPARAM)(LPCTSTR)name);

  if (sscanf(name, "Room %d, Node %d", &BNode_other_room, &BNode_other_id) != 2) {
    sscanf(name, "Region %d, Node %d", &BNode_other_room, &BNode_other_id);
    BNode_other_room += Highest_room_index + 1;
  }

  BNode_other_id--;
}

void CPathPadDialog::OnAinResetRoomNodes() {
  // TODO: Add your control notification handler code here
  EBNode_VerifyGraph();
}

void CPathPadDialog::OnAinRight() {
  vector diff = Viewer_object->orient.rvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

#define EBVIEW_ROOM 1
#define EBVIEW_ROOM_AND_NEXT_ROOMS 2
#define EBVIEW_CUR_EDGES 3

char ENode_view_type = EBVIEW_ROOM;

void CPathPadDialog::OnAinSelNodeEdges() {
  ENode_view_type = EBVIEW_CUR_EDGES;
  ResetNodesForComboBox(IDC_AIN_CURRENT_NODE, ENode_view_type);
  ResetNodesForComboBox(IDC_AIN_OTHER_NODE, ENode_view_type);
}

void CPathPadDialog::OnAinSelRoom() {
  ENode_view_type = EBVIEW_ROOM;
  ResetNodesForComboBox(IDC_AIN_CURRENT_NODE, ENode_view_type);
  ResetNodesForComboBox(IDC_AIN_OTHER_NODE, ENode_view_type);
}

void CPathPadDialog::OnAinSelRoomAndNextRooms() {
  ENode_view_type = EBVIEW_ROOM_AND_NEXT_ROOMS;
  ResetNodesForComboBox(IDC_AIN_CURRENT_NODE, ENode_view_type);
  ResetNodesForComboBox(IDC_AIN_OTHER_NODE, ENode_view_type);
}

void CPathPadDialog::OnAinUp() {
  vector diff = Viewer_object->orient.uvec * BNode_move_amount;

  EBNode_Move(true, BNode_current_room, BNode_current_id, &diff);
  World_changed = 1;
}

void CPathPadDialog::OnAinVerifyLevel() { EBNode_VerifyGraph(); }

void CPathPadDialog::OnAinVerifyRoom() {
  EBNode_ClearLevel();
  World_changed = 1;
}

void CPathPadDialog::OnAinViewAllNodes() {
  EBN_draw_type = EBDRAW_LEVEL;
  World_changed = 1;
}

void CPathPadDialog::OnAinViewNone() {
  EBN_draw_type = EBDRAW_NONE;
  World_changed = 1;
}

void CPathPadDialog::OnAinViewRoom() {
  EBN_draw_type = EBDRAW_ROOM;
  World_changed = 1;
}

void CPathPadDialog::OnAinViewRoomAndNextRooms() {
  EBN_draw_type = EBDRAW_ROOM_AND_NEXT_ROOMS;
  World_changed = 1;
}

void CPathPadDialog::ResetNodesForComboBox(int handle, char selection_type) {
  char message[25];
  SendDlgItemMessage(handle, CB_RESETCONTENT, 0, 0);
  bool f_add = false;

  int num_next_rooms = 0;
  int next_rooms[1000];

  if (Viewer_object->roomnum >= 0 && Viewer_object->roomnum <= Highest_room_index &&
      Rooms[Viewer_object->roomnum].used) {
    if (selection_type == EBVIEW_ROOM || selection_type == EBVIEW_ROOM_AND_NEXT_ROOMS) {
      for (int i = 0; i < Rooms[Viewer_object->roomnum].bn_info.num_nodes; i++) {
        f_add = true;

        sprintf(message, "Room %d, Node %d", Viewer_object->roomnum, i + 1);
        SendDlgItemMessage(handle, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)message);
      }

      if (selection_type == EBVIEW_ROOM_AND_NEXT_ROOMS) {
        int i;
        int j;
        int k;

        for (i = 0; i < Rooms[Viewer_object->roomnum].num_portals; i++) {
          bool f_found = false;

          for (j = 0; j < num_next_rooms; j++) {
            if (next_rooms[j] == Rooms[Viewer_object->roomnum].portals[i].croom) {
              f_found = true;
              break;
            }
          }

          if (!f_found && Rooms[Viewer_object->roomnum].portals[i].croom >= 0 &&
              !(Rooms[Rooms[Viewer_object->roomnum].portals[i].croom].flags & RF_EXTERNAL)) {
            for (k = 0; k < Rooms[Rooms[Viewer_object->roomnum].portals[i].croom].bn_info.num_nodes; k++) {
              sprintf(message, "Room %d, Node %d", Rooms[Viewer_object->roomnum].portals[i].croom, k + 1);
              SendDlgItemMessage(handle, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)message);
            }
          }
        }
      }

      if (f_add) {
        sprintf(message, "Room %d, Node %d", Viewer_object->roomnum, 0 + 1);
        SendDlgItemMessage(handle, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)message);
      } else {
        strcpy(message, "No Nodes in room");
        SendDlgItemMessage(handle, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)message);
      }
    }
  } else if (ROOMNUM_OUTSIDE(Viewer_object->roomnum)) {
    if (selection_type == EBVIEW_ROOM || selection_type == EBVIEW_ROOM_AND_NEXT_ROOMS) {
      int room = BOA_INDEX(Viewer_object->roomnum);
      int tr = room - Highest_room_index - 1;
      bn_list *bnlist = BNode_GetBNListPtr(Viewer_object->roomnum);

      for (int i = 0; i < bnlist->num_nodes; i++) {
        f_add = true;

        sprintf(message, "Region %d, Node %d", tr, i + 1);
        SendDlgItemMessage(handle, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)message);
      }

      if (selection_type == EBVIEW_ROOM_AND_NEXT_ROOMS) {
        int i;
        int j;
        int k;

        for (i = 0; i < BOA_num_connect[tr]; i++) {
          bool f_found = false;

          for (j = 0; j < num_next_rooms; j++) {
            if (next_rooms[j] == BOA_connect[tr][i].roomnum) {
              f_found = true;
              break;
            }
          }

          if (!f_found && BOA_connect[tr][i].roomnum >= 0 && !(Rooms[BOA_connect[tr][i].roomnum].flags & RF_EXTERNAL)) {
            for (k = 0; k < Rooms[BOA_connect[tr][i].roomnum].bn_info.num_nodes; k++) {
              sprintf(message, "Room %d, Node %d", BOA_connect[tr][i].roomnum, k + 1);
              SendDlgItemMessage(handle, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)message);
            }
          }
        }
      }

      if (f_add) {
        sprintf(message, "Region %d, Node %d", tr, 0 + 1);
        SendDlgItemMessage(handle, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)message);
      } else {
        strcpy(message, "No Nodes in region");
        SendDlgItemMessage(handle, CB_SELECTSTRING, 0, (LPARAM)(LPCTSTR)message);
      }
    }
  }
}

void CPathPadDialog::OnSetfocusAinCurrentNode() { ResetNodesForComboBox(IDC_AIN_CURRENT_NODE, ENode_view_type); }

void CPathPadDialog::OnSetfocusAinOtherNode() { ResetNodesForComboBox(IDC_AIN_OTHER_NODE, ENode_view_type); }

void CPathPadDialog::OnIenavButton() {
  int node = EBNode_InsertNodeOnEdge(BNode_current_id, BNode_current_room, BNode_other_id, BNode_other_room);
  if (node >= 0) {
    vector diff = Viewer_object->pos;

    EBNode_Move(false, BNode_current_room, node, &diff);
    World_changed = 1;
  }
}

void CPathPadDialog::OnMvtnButton() {
  vector pos;
  int roomnum;

  bn_list *bnlist = BNode_GetBNListPtr(BNode_current_room);
  if (!bnlist)
    return;

  pos = bnlist->nodes[BNode_current_id].pos;

  if (BNode_current_room >= 0 && BNode_current_room <= Highest_room_index) {
    roomnum = BNode_current_room;
  } else {
    roomnum = GetTerrainRoomFromPos(&pos);
  }

  BNode_current_id,

      ObjSetPos(Viewer_object, &pos, roomnum, NULL, false);
  World_changed = 1;
}

void CPathPadDialog::OnAerButton() {
  bn_list *bnlist = BNode_GetBNListPtr(BNode_current_room);
  if (!bnlist)
    return;

  for (int i = 0; i < bnlist->num_nodes; i++) {
    EBNode_AutoEdgeNode(i, BNode_current_room);
  }

  EBNode_VerifyGraph();
  World_changed = 1;
}

void CPathPadDialog::OnOntvgpButton() {
  if (Num_game_paths < 1) {
    MessageBox("There are no paths to operate on.");
    return;
  }

  if (Cur_object_index != -1) //	we have a selected object
  {

    int p = D3EditState.current_path;
    int n = D3EditState.current_node;

    game_path *gp = &GamePaths[p];

    gp->pathnodes[n].fvec = Viewer_object->orient.fvec;
    gp->pathnodes[n].uvec = Viewer_object->orient.uvec;

    World_changed = 1;
    UpdateDialog();
  }
}
