/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/



#include "stdafx.h"
#include "FRED.h"
#include "BriefingEditorDlg.h"
#include "FREDDoc.h"
#include "mission/missionbriefcommon.h"
#include "mission/missionparse.h"
#include "FredRender.h"
#include "Management.h"
#include "globalincs/alphacolors.h"
#include "globalincs/linklist.h"
#include "MainFrm.h"
#include "bmpman/bmpman.h"
#include "gamesnd/eventmusic.h"
#include "starfield/starfield.h"
#include "jumpnode/jumpnode.h"
#include "cfile/cfile.h"
#include "object/objectdock.h"
#include "iff_defs/iff_defs.h"
#include "sound/audiostr.h"
#include "localization/localize.h"
#include "mod_table/mod_table.h"

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

#define ID_MENU		9000

static int Max_icons_for_lines;

/////////////////////////////////////////////////////////////////////////////
// briefing_editor_dlg dialog

briefing_editor_dlg::briefing_editor_dlg(CWnd* pParent /*=NULL*/)
	: CDialog(briefing_editor_dlg::IDD, pParent)
{
	int i, z;

	// figure out max icons we can have with lines to each other less than max allowed lines.
	// Basically, # lines = (# icons - 1) factorial
	Max_icons_for_lines = 0;
	do {
		i = ++Max_icons_for_lines + 1;
		z = 0;
		while (--i)
			z += i;

	} while (z < MAX_BRIEF_STAGE_LINES);

	//{{AFX_DATA_INIT(briefing_editor_dlg)
	m_hilight = FALSE;
	m_icon_image = -1;
	m_icon_label = _T("");
	m_icon_closeup_label = _T("");
	m_icon_scale = -1;
	m_stage_title = _T("");
	m_text = _T("");
	m_time = _T("");
	m_voice = _T("");
	m_icon_team = -1;
	m_ship_type = -1;
	m_change_local = FALSE;
	m_id = 0;
	m_briefing_music = -1;
	m_substitute_briefing_music = _T("");
	m_cut_next = FALSE;
	m_cut_prev = FALSE;
	m_no_grid = FALSE;
	m_current_briefing = -1;
	m_flipicon = FALSE;
	m_use_wing = FALSE;
	m_use_cargo = FALSE;
	//}}AFX_DATA_INIT
	m_play_icon = nullptr;
	m_voice_id = -1;
	m_cur_stage = 0;
	m_last_stage = m_cur_icon = m_last_icon = -1;
	m_tree.link_modified(&modified);  // provide way to indicate trees are modified in dialog

	// copy view initialization
	m_copy_view_set = 0;
}

void briefing_editor_dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(briefing_editor_dlg)
	DDX_Control(pDX, IDC_TREE, m_tree);
	DDX_Control(pDX, IDC_LINES, m_lines);
	DDX_Check(pDX, IDC_HILIGHT, m_hilight);
	DDX_CBIndex(pDX, IDC_ICON_IMAGE, m_icon_image);
	DDX_Text(pDX, IDC_ICON_LABEL, m_icon_label);
	DDV_MaxChars(pDX, m_icon_label, MAX_LABEL_LEN - 1);
	DDX_Text(pDX, IDC_ICON_CLOSEUP_LABEL, m_icon_closeup_label);
	DDV_MaxChars(pDX, m_icon_closeup_label, MAX_LABEL_LEN - 1);
	DDX_Text(pDX, IDC_ICON_SCALE, m_icon_scale);
	DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title);
	DDX_Text(pDX, IDC_TEXT, m_text);
	DDX_Text(pDX, IDC_TIME, m_time);
	DDX_Text(pDX, IDC_VOICE, m_voice);
	DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1);
	DDX_CBIndex(pDX, IDC_TEAM, m_icon_team);
	DDX_CBIndex(pDX, IDC_SHIP_TYPE, m_ship_type);
	DDX_Check(pDX, IDC_LOCAL, m_change_local);
	DDX_Text(pDX, IDC_ID, m_id);
	DDX_CBIndex(pDX, IDC_BRIEFING_MUSIC, m_briefing_music);
	DDX_Text(pDX, IDC_SUBSTITUTE_BRIEFING_MUSIC, m_substitute_briefing_music);
	DDX_Check(pDX, IDC_CUT_NEXT, m_cut_next);
	DDX_Check(pDX, IDC_CUT_PREV, m_cut_prev);
	DDX_Check(pDX, IDC_NO_GRID, m_no_grid);
	DDX_Check(pDX, IDC_FLIP_ICON, m_flipicon);
	DDX_Check(pDX, IDC_USE_WING_ICON, m_use_wing);
	DDX_Check(pDX, IDC_USE_CARGO_ICON, m_use_cargo);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(briefing_editor_dlg, CDialog)
	//{{AFX_MSG_MAP(briefing_editor_dlg)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_NEXT, OnNext)
	ON_BN_CLICKED(IDC_PREV, OnPrev)
	ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
	ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage)
	ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage)
	ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage)
	ON_BN_CLICKED(IDC_MAKE_ICON, OnMakeIcon)
	ON_BN_CLICKED(IDC_DELETE_ICON, OnDeleteIcon)
	ON_BN_CLICKED(IDC_GOTO_VIEW, OnGotoView)
	ON_BN_CLICKED(IDC_SAVE_VIEW, OnSaveView)
	ON_CBN_SELCHANGE(IDC_ICON_IMAGE, OnSelchangeIconImage)
	ON_CBN_SELCHANGE(IDC_TEAM, OnSelchangeTeam)
	ON_CBN_SELCHANGE(IDC_SHIP_TYPE, OnSelchangeShipType)
	ON_BN_CLICKED(IDC_PROPAGATE_ICONS, OnPropagateIcons)
	ON_WM_INITMENU()
	ON_BN_CLICKED(IDC_LINES, OnLines)
	ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
	ON_BN_CLICKED(IDC_PLAY, OnPlay)
	ON_BN_CLICKED(IDC_COPY_VIEW, OnCopyView)
	ON_BN_CLICKED(IDC_PASTE_VIEW, OnPasteView)
	ON_BN_CLICKED(IDC_FLIP_ICON, OnFlipIcon)
	ON_BN_CLICKED(IDC_USE_WING_ICON, OnWingIcon)
	ON_BN_CLICKED(IDC_USE_CARGO_ICON, OnCargoIcon)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// briefing_editor_dlg message handlers

BOOL briefing_editor_dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	//create tool tip controls
	m_CloseupLabelToolTip = new CToolTipCtrl();
	m_CloseupLabelToolTip->Create(this);

	CWnd* pWnd = GetDlgItem(IDC_ICON_CLOSEUP_LABEL);
	m_CloseupLabelToolTip->AddTool(pWnd, "If this is blank, the ship class will be used");
	m_CloseupLabelToolTip->Activate(TRUE);

	return TRUE;  // return TRUE unless you set the focus to a control
				  // EXCEPTION: OCX Property Pages should return FALSE
}

BOOL briefing_editor_dlg::PreTranslateMessage(MSG* pMsg)
{
	m_CloseupLabelToolTip->RelayEvent(pMsg);

	return CDialog::PreTranslateMessage(pMsg);
}

void briefing_editor_dlg::OnInitMenu(CMenu* pMenu)
{
	int i;
	CMenu *m;

	// disable any items we should disable
	m = pMenu->GetSubMenu(0);

	// uncheck all menu items
	for (i=0; i<Num_teams; i++)
		m->CheckMenuItem(i, MF_BYPOSITION | MF_UNCHECKED);

	for (i=Num_teams; i<MAX_TVT_TEAMS; i++)
		m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED);

	// put a check next to the currently selected item
	m->CheckMenuItem(m_current_briefing, MF_BYPOSITION | MF_CHECKED );

	// Karajorma - it might be nice to autobalance the briefings and debriefings but I'm not doing anything till I
	// understand how the how system works better. disabling the option for now. 
	m = pMenu->GetSubMenu(1); 
	m->EnableMenuItem(ID_AUTOBALANCE, MF_GRAYED); 

	CDialog::OnInitMenu(pMenu);
}

void briefing_editor_dlg::create()
{
	int i;
	CComboBox *box;

	CDialog::Create(IDD);
	theApp.init_window(&Briefing_wnd_data, this);
	box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE);
	for (i=0; i<MIN_BRIEF_ICONS; i++)
		box->AddString(Icon_names[i]);

	box = (CComboBox *) GetDlgItem(IDC_TEAM);
	for (i=0; i< (int)Iff_info.size(); i++)
		box->AddString(Iff_info[i].iff_name);

	box = (CComboBox *) GetDlgItem(IDC_SHIP_TYPE);
	for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
		box->AddString(it->name);

	box = (CComboBox *) GetDlgItem(IDC_BRIEFING_MUSIC);
	box->AddString("None");
	for (auto &sm: Spooled_music)
		box->AddString(sm.name);

	box = (CComboBox *) GetDlgItem(IDC_SUBSTITUTE_BRIEFING_MUSIC);
	box->AddString("None");
	for (auto &sm: Spooled_music)
		box->AddString(sm.name);

	m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
	((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);

	m_current_briefing = 0;
	Briefing = &Briefings[m_current_briefing];
	m_briefing_music = Mission_music[SCORE_BRIEFING] + 1;
	m_substitute_briefing_music = The_mission.substitute_briefing_music_name;

	UpdateData(FALSE);
	update_data();
	OnGotoView();
	update_map_window();
}

void briefing_editor_dlg::focus_sexp(int select_sexp_node)
{
	int i, t, n;

	n = m_tree.select_sexp_node = select_sexp_node;
	if (n == -1)
		return;

	for (t = 0; t < Num_teams; t++) {
		for (i = 0; i < Briefings[t].num_stages; i++) {
			if (query_node_in_sexp(n, Briefings[t].stages[i].formula)) {
				m_current_briefing = t;
				Briefing = &Briefings[t];
				m_cur_stage = i;
				update_data();
				GetDlgItem(IDC_TREE)->SetFocus();
				m_tree.hilite_item(m_tree.select_sexp_node);
				return;
			}
		}
	}
}

void briefing_editor_dlg::OnOK()
{
}

void briefing_editor_dlg::OnCancel()
{
	OnClose();
}

void briefing_editor_dlg::OnClose() 
{
	int bs, i, j, s, t, dup = 0;
	briefing_editor_dlg *ptr;
	brief_stage *sp;

	m_cur_stage = -1;
	update_data(1);

	audiostream_close_file(m_voice_id, 0);

	for ( bs = 0; bs < Num_teams; bs++ ) {
		for (s=0; s<Briefing[bs].num_stages; s++) {
			sp = &Briefing[bs].stages[s];
			t = sp->num_icons;
			for (i=0; i<t-1; i++)
				for (j=i+1; j<t; j++) {
					if ((sp->icons[i].id >= 0) && (sp->icons[i].id == sp->icons[j].id))
						dup = 1;
				}
		}
	}

	if (dup)
		MessageBox("You have duplicate icon names.  You should resolve these.", "Warning");

	theApp.record_window_data(&Briefing_wnd_data, this);
	ptr = Briefing_dialog;	// this juggling prevents a crash in certain situations
	Briefing_dialog = nullptr;
	delete ptr;

	FREDDoc_ptr->autosave("briefing editor");
}

void briefing_editor_dlg::reset_editor()
{
	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;

	m_cur_stage = -1;
	update_data(0);
}

// some kind of hackish code to get around the problem if saving (which implicitly loads,
// which implicitly clears all mission info) not affecting the editor's state at save.
void briefing_editor_dlg::save_editor_state()
{
	stage_saved = m_cur_stage;
	icon_saved = m_cur_icon;
}

void briefing_editor_dlg::restore_editor_state()
{
	m_cur_stage = stage_saved;
	m_cur_icon = icon_saved;
}

void briefing_editor_dlg::update_data(int update)
{
	char buf[MAX_LABEL_LEN];
	SCP_string buf3;
	int i, j, l, lines, count, enable = TRUE, valid = 0, invalid = 0;
	object *objp;
	brief_stage *ptr = NULL;

	if (update)
		UpdateData(TRUE);

	// save off current data before we update over it with new briefing stage/team stuff
	Briefing = save_briefing;

	Mission_music[SCORE_BRIEFING] = m_briefing_music - 1;
	strcpy_s(The_mission.substitute_briefing_music_name, m_substitute_briefing_music);
	if (m_last_stage >= 0) {
		ptr = &Briefing->stages[m_last_stage];
		deconvert_multiline_string(buf3, m_text);
		lcl_fred_replace_stuff(buf3);
		if (ptr->text != buf3)
			set_modified();

		ptr->text = buf3;
		MODIFY(ptr->camera_time, atoi(m_time));
		string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN - 1, 1);
		i = ptr->flags;
		if (m_cut_prev)
			i |= BS_BACKWARD_CUT;
		else
			i &= ~BS_BACKWARD_CUT;

		if (m_cut_next)
			i |= BS_FORWARD_CUT;
		else
			i &= ~BS_FORWARD_CUT;

		if (m_no_grid)
			ptr->draw_grid = false;
		else
			ptr->draw_grid = true;

		MODIFY(ptr->flags, i);
		ptr->formula = m_tree.save_tree();
		switch (m_lines.GetCheck()) {
			case 1:
				// add lines between every pair of 2 marked icons if there isn't one already.
				for (i=0; i<ptr->num_icons - 1; i++)
					for (j=i+1; j<ptr->num_icons; j++) {
						if ( icon_marked[i] && icon_marked[j] ) {
							for (l=0; l<ptr->num_lines; l++)
								if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) )
									break;

							if ((l == ptr->num_lines) && (l < MAX_BRIEF_STAGE_LINES)) {
								ptr->lines[l].start_icon = i;
								ptr->lines[l].end_icon = j;
								ptr->num_lines++;
							}
						}
					}

				break;

			case 0:
				// remove all existing lines between any 2 marked icons
				i = ptr->num_lines;
				while (i--)
					if ( icon_marked[ptr->lines[i].start_icon] && icon_marked[ptr->lines[i].end_icon] ) {
						ptr->num_lines--;
						for (l=i; l<ptr->num_lines; l++)
							ptr->lines[l] = ptr->lines[l + 1];
					}

				break;
		}

		//WMC - Safeguard against broken lines
		i = ptr->num_lines;
		while(i--)
		{
			if(ptr->lines[i].start_icon < 0 || ptr->lines[i].end_icon < 0 || ptr->lines[i].start_icon >= ptr->num_icons || ptr->lines[i].end_icon >= ptr->num_icons)
			{
				ptr->num_lines--;
				for(l = i; l < ptr->num_lines; l++)
					ptr->lines[l] = ptr->lines[l+1];
			}
		}

		if (m_last_icon >= 0) {
			valid = (m_id != ptr->icons[m_last_icon].id);
			if (m_id >= 0) {
				if (valid && !m_change_local) {
					for (i=m_last_stage+1; i<Briefing->num_stages; i++) {
						if (find_icon(m_id, i) >= 0) {
							char msg[1024];

							valid = 0;
							sprintf(msg, "Icon ID #%d is already used in a later stage.  You can only\n"
								"change to that ID locally.  Icon ID has been reset back to %d", m_id, ptr->icons[m_last_icon].id);

							m_id = ptr->icons[m_last_icon].id;
							MessageBox(msg);
							break;
						}
					}
				}

				for (i=0; i<ptr->num_icons; i++)
					if ((i != m_last_icon) && (ptr->icons[i].id == m_id)) {
						char msg[1024];

						sprintf(msg, "Icon ID #%d is already used in this stage.  Icon ID has been reset back to %d",
							m_id, ptr->icons[m_last_icon].id);

						m_id = ptr->icons[m_last_icon].id;
						MessageBox(msg);
						break;
					}

				if (valid && !m_change_local) {
					set_modified();
					reset_icon_loop(m_last_stage);
					while (get_next_icon(ptr->icons[m_last_icon].id))
						iconp->id = m_id;
				}
			}

			ptr->icons[m_last_icon].id = m_id;

			string_copy(buf, m_icon_label, MAX_LABEL_LEN - 1);
			lcl_fred_replace_stuff(buf, MAX_LABEL_LEN - 1);
			if (stricmp(ptr->icons[m_last_icon].label, buf) && !m_change_local) {
				set_modified();
				reset_icon_loop(m_last_stage);
				while (get_next_icon(m_id))
					strcpy_s(iconp->label, buf);
			}
			strcpy_s(ptr->icons[m_last_icon].label, buf);

			string_copy(buf, m_icon_closeup_label, MAX_LABEL_LEN - 1);
			lcl_fred_replace_stuff(buf, MAX_LABEL_LEN - 1);
			if (stricmp(ptr->icons[m_last_icon].closeup_label, buf) && !m_change_local) {
				set_modified();
				reset_icon_loop(m_last_stage);
				while (get_next_icon(m_id))
					strcpy_s(iconp->closeup_label, buf);
			}
			strcpy_s(ptr->icons[m_last_icon].closeup_label, buf);

			if (m_icon_scale > 0)
				ptr->icons[m_last_icon].scale_factor = m_icon_scale / 100.0f;

			if ( m_hilight )
				ptr->icons[m_last_icon].flags |= BI_HIGHLIGHT;
			else
				ptr->icons[m_last_icon].flags &= ~BI_HIGHLIGHT;

			if (m_flipicon)
				ptr->icons[m_last_icon].flags |= BI_MIRROR_ICON;
			else
				ptr->icons[m_last_icon].flags &= ~BI_MIRROR_ICON;

			if (m_use_wing)
				ptr->icons[m_last_icon].flags |= BI_USE_WING_ICON;
			else
				ptr->icons[m_last_icon].flags &= ~BI_USE_WING_ICON;

			if (m_use_cargo)
				ptr->icons[m_last_icon].flags |= BI_USE_CARGO_ICON;
			else
				ptr->icons[m_last_icon].flags &= ~BI_USE_CARGO_ICON;

			if ((ptr->icons[m_last_icon].type != m_icon_image) && !m_change_local) {
				set_modified();
				reset_icon_loop(m_last_stage);
				while (get_next_icon(m_id))
					iconp->type = m_icon_image;
			}
			ptr->icons[m_last_icon].type = m_icon_image;

			if ((ptr->icons[m_last_icon].team != m_icon_team) && !m_change_local) {
				set_modified();
				reset_icon_loop(m_last_stage);
				while (get_next_icon(m_id))
					iconp->team = m_icon_team;
			}
			ptr->icons[m_last_icon].team = m_icon_team;

			if ((ptr->icons[m_last_icon].ship_class != m_ship_type) && !m_change_local) {
				set_modified();
				reset_icon_loop(m_last_stage);
				while (get_next_icon(m_id))
					iconp->ship_class = m_ship_type;
			}
			MODIFY(ptr->icons[m_last_icon].ship_class, m_ship_type);
		}
	}

	if (!::IsWindow(m_hWnd))
		return;

	// set briefing pointer to correct team
	Briefing = &Briefings[m_current_briefing];

	if ((m_cur_stage >= 0) && (m_cur_stage < Briefing->num_stages)) {
		ptr = &Briefing->stages[m_cur_stage];
		m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Briefing->num_stages);
		convert_multiline_string(m_text, ptr->text);
		m_time.Format("%d", ptr->camera_time);
		m_voice = ptr->voice;
		m_cut_prev = (ptr->flags & BS_BACKWARD_CUT) ? 1 : 0;
		m_cut_next = (ptr->flags & BS_FORWARD_CUT) ? 1 : 0;
		m_no_grid = (ptr->draw_grid) ? 0 : 1;
		m_tree.load_tree(ptr->formula);

	} else {
		m_stage_title = _T("No stages");
		m_text = _T("");
		m_time = _T("");
		m_voice = _T("");
		m_cut_prev = m_cut_next = 0;
		m_no_grid = 0;
		m_tree.clear_tree();
		enable = FALSE;
		m_cur_stage = -1;
	}

	if (m_cur_stage == Briefing->num_stages - 1)
		GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE);
	else
		GetDlgItem(IDC_NEXT) -> EnableWindow(enable);

	if (m_cur_stage)
		GetDlgItem(IDC_PREV) -> EnableWindow(enable);
	else
		GetDlgItem(IDC_PREV) -> EnableWindow(FALSE);

	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE);
	else
		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE);

	if (Briefing->num_stages) {
		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable);
		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable);
	} else {
		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE);
		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE);
	}

	GetDlgItem(IDC_TIME) -> EnableWindow(enable);
	GetDlgItem(IDC_VOICE) -> EnableWindow(enable);
	GetDlgItem(IDC_BROWSE) -> EnableWindow(enable);
	GetDlgItem(IDC_TEXT) -> EnableWindow(enable);
	GetDlgItem(IDC_SAVE_VIEW) -> EnableWindow(enable);
	GetDlgItem(IDC_GOTO_VIEW) -> EnableWindow(enable);
	GetDlgItem(IDC_CUT_PREV) -> EnableWindow(enable);
	GetDlgItem(IDC_CUT_NEXT) -> EnableWindow(enable);
	GetDlgItem(IDC_NO_GRID)->EnableWindow(enable);
	GetDlgItem(IDC_TREE) -> EnableWindow(enable);
	GetDlgItem(IDC_PLAY) -> EnableWindow(enable);

	if ((m_cur_stage >= 0) && (m_cur_icon >= 0) && (m_cur_icon < ptr->num_icons)) {
		m_hilight = (ptr->icons[m_cur_icon].flags & BI_HIGHLIGHT)?1:0;
		m_flipicon = (ptr->icons[m_cur_icon].flags & BI_MIRROR_ICON)?1:0;
		m_use_wing = (ptr->icons[m_cur_icon].flags & BI_USE_WING_ICON)?1:0;
		m_use_cargo = (ptr->icons[m_cur_icon].flags & BI_USE_CARGO_ICON)?1:0;
		m_icon_image = ptr->icons[m_cur_icon].type;
		m_icon_team = ptr->icons[m_cur_icon].team;
		m_icon_label = ptr->icons[m_cur_icon].label;
		m_icon_closeup_label = ptr->icons[m_cur_icon].closeup_label;
		m_icon_scale = static_cast<int>(ptr->icons[m_cur_icon].scale_factor * 100.0f);
		m_ship_type = ptr->icons[m_cur_icon].ship_class;
		m_id = ptr->icons[m_cur_icon].id;
		enable = TRUE;

	} else {
		m_flipicon = FALSE;
		m_hilight = FALSE;
		m_use_wing = FALSE;
		m_use_cargo = FALSE;
		m_icon_image = -1;
		m_icon_team = -1;
		m_ship_type = -1;
		m_icon_label = _T("");
		m_icon_closeup_label = _T("");
		m_icon_scale = 100;
		m_cur_icon = -1;
		m_id = 0;
		enable = FALSE;
	}

	// see if icon is overridden by ships.tbl
	int sip_bii_ship = (m_ship_type >= 0) ? Ship_info[m_ship_type].bii_index_ship : -1;
	int sip_bii_wing = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_wing : -1;
	int sip_bii_cargo = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_ship_with_cargo : -1;

	GetDlgItem(IDC_USE_WING_ICON) -> ShowWindow(sip_bii_wing >= 0);
	GetDlgItem(IDC_USE_CARGO_ICON) -> ShowWindow(sip_bii_cargo >= 0);

	GetDlgItem(IDC_ICON_CLOSEUP_LABEL) -> EnableWindow(enable);
	GetDlgItem(IDC_ICON_LABEL) -> EnableWindow(enable);
	GetDlgItem(IDC_ICON_IMAGE) -> EnableWindow(enable);
	GetDlgItem(IDC_SHIP_TYPE) -> EnableWindow(enable);
	GetDlgItem(IDC_HILIGHT) -> EnableWindow(enable);
	GetDlgItem(IDC_FLIP_ICON) -> EnableWindow(enable);
	GetDlgItem(IDC_USE_WING_ICON) -> EnableWindow(enable);
	GetDlgItem(IDC_USE_CARGO_ICON) -> EnableWindow(enable);
	GetDlgItem(IDC_LOCAL) -> EnableWindow(enable);
	GetDlgItem(IDC_TEAM) -> EnableWindow(enable);
	GetDlgItem(IDC_ID) -> EnableWindow(enable);
	GetDlgItem(IDC_DELETE_ICON) -> EnableWindow(enable);

	valid = invalid = 0;
	objp = GET_FIRST(&obj_used_list);
	while (objp != END_OF_LIST(&obj_used_list)) {
		if (objp->flags[Object::Object_Flags::Marked]) {
			if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START) || (objp->type == OBJ_WAYPOINT) || (objp->type == OBJ_JUMP_NODE))
				valid = 1;
			else
				invalid = 1;
		}

		objp = GET_NEXT(objp);
	}

	if (m_cur_stage >= 0)
		ptr = &Briefing->stages[m_cur_stage];

	if (valid && !invalid && (m_cur_stage >= 0) && (ptr->num_icons < MAX_STAGE_ICONS))
		GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(TRUE);
	else
		GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);

	if (m_cur_stage >= 0)
		for (i=0; i<ptr->num_icons; i++)
			icon_marked[i] = 0;

	valid = invalid = 0;
	objp = GET_FIRST(&obj_used_list);
	while (objp != END_OF_LIST(&obj_used_list)) {
		if (objp->flags[Object::Object_Flags::Marked]) {
			if (objp->type == OBJ_POINT) {
				valid++;
				icon_marked[objp->instance] = 1;

			} else
				invalid++;
		}

		objp = GET_NEXT(objp);
	}

	if (valid && !invalid && (m_cur_stage >= 0))
		GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
	else
		GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(FALSE);

	count = 0;
	lines = 1;  // default lines checkbox to checked
	
	if (m_cur_stage >= 0) {
		for (i=0; i<ptr->num_lines; i++)
			line_marked[i] = 0;

		// go through and locate all lines between marked icons
		for (i=0; i<ptr->num_icons - 1; i++)
			for (j=i+1; j<ptr->num_icons; j++) {
				if ( icon_marked[i] && icon_marked[j] ) {
					for (l=0; l<ptr->num_lines; l++)
						if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) ) {
							line_marked[l] = 1;
							count++;  // track number of marked lines (lines between 2 icons that are both marked)
							break;
						}

					// at least 1 line missing between 2 marked icons, so use mixed state
					if (l == ptr->num_lines)
						lines = 2;
				}
			}
	}

	// not even 1 line between any 2 marked icons?  Set checkbox to unchecked.
	if (!count)
		lines = 0;

	i = 0;
	if (m_cur_stage >= 0){
		i = calc_num_lines_for_icons(valid) + ptr->num_lines - count;
	}

	if ((valid > 1) && !invalid && (m_cur_stage >= 0) && (i <= MAX_BRIEF_STAGE_LINES))
		GetDlgItem(IDC_LINES) -> EnableWindow(TRUE);
	else
		GetDlgItem(IDC_LINES) -> EnableWindow(FALSE);

	m_lines.SetCheck(lines);

	UpdateData(FALSE);
	if ((m_last_stage != m_cur_stage) || (Briefing != save_briefing)) {
		if (m_last_stage >= 0) {
			for (i=0; i<save_briefing->stages[m_last_stage].num_icons; i++) {
				// save positions of all icons, in case they have moved
				save_briefing->stages[m_last_stage].icons[i].pos = Objects[icon_obj[i]].pos;
				// release objects being used by last stage
				obj_delete(icon_obj[i]);
			}
		}

		if (m_cur_stage >= 0) {
            flagset<Object::Object_Flags> default_flags;
			for (i=0; i<ptr->num_icons; i++) {
				// create an object for each icon for display/manipulation purposes
				icon_obj[i] = obj_create(OBJ_POINT, -1, i, NULL, &ptr->icons[i].pos, 0.0f, default_flags + Object::Object_Flags::Renders);
			}

			obj_merge_created_list();
		}

		m_last_stage = m_cur_stage;
	}

	m_last_icon = m_cur_icon;
	Update_window = 1;
	save_briefing = Briefing;
}

// Given a number of icons, figure out how many lines would be required to connect each one
// with all of the others.
//
int briefing_editor_dlg::calc_num_lines_for_icons(int num)
{
	int lines = 0;

	if (num < 2)
		return 0;

	// Basically, this is # lines = (# icons - 1) factorial
	while (--num)
		lines += num;

	return lines;
}

void briefing_editor_dlg::OnNext()
{
	m_cur_stage++;
	m_cur_icon = -1;
	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;
	update_data();
	OnGotoView();
}

void briefing_editor_dlg::OnPrev()
{
	m_cur_stage--;
	m_cur_icon = -1;
	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;
	update_data();
	OnGotoView();
}

void briefing_editor_dlg::OnBrowse() 
{
	int z;
	CString name;

	UpdateData(TRUE);

	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;

	if (The_mission.game_type & MISSION_TYPE_TRAINING)
		z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
	else
		z = cfile_push_chdir(CF_TYPE_VOICE_BRIEFINGS);

	CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
		"Voice Files (*.ogg, *.wav)|*.ogg;*.wav|Ogg Vorbis Files (*.ogg)|*.ogg|Wave Files (*.wav)|*.wav||");

	if (dlg.DoModal() == IDOK) {
		m_voice = dlg.GetFileName();
		UpdateData(FALSE);
	}

	if (!z)
		cfile_pop_dir();
}

void briefing_editor_dlg::OnAddStage() 
{
	int i;

	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
		return;

	m_cur_stage = i = Briefing->num_stages++;
	copy_stage(i - 1, i);
	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;
	update_data(1);
}

void briefing_editor_dlg::OnDeleteStage() 
{
	int i, z;

	if (m_cur_stage < 0)
		return;

	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;

	Assert(Briefing->num_stages);
	z = m_cur_stage;
	m_cur_stage = -1;
	update_data(1);
	for (i=z+1; i<Briefing->num_stages; i++)	{
		copy_stage(i, i-1);
	}

	Briefing->num_stages--;
	m_cur_stage = z;
	if (m_cur_stage >= Briefing->num_stages)
		m_cur_stage = Briefing->num_stages - 1;

	update_data(0);
}

void briefing_editor_dlg::draw_icon(object *objp)
{
	if (m_cur_stage < 0)
		return;

	// average the w and h scale factors
	float scale_factor = 0.5f * ((float)True_rw / Briefing_window_resolution[0] + (float)True_rh / Briefing_window_resolution[1]);

	brief_render_icon(m_cur_stage, objp->instance, 1.0f/30.0f, objp->flags[Object::Object_Flags::Marked], scale_factor);
}

void briefing_editor_dlg::batch_render()
{
	int i, num_lines;


	if (m_cur_stage < 0)
		return;

	num_lines = Briefing->stages[m_cur_stage].num_lines;
	for (i=0; i<num_lines; i++)
		brief_render_icon_line(m_cur_stage, i);
}

void briefing_editor_dlg::icon_select(int num)
{
	m_cur_icon = num;
	update_data(1);
}

void briefing_editor_dlg::OnInsertStage() 
{
	int i, z;

	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
		return;

	if (!Briefing->num_stages) {
		OnAddStage();
		return;
	}

	audiostream_close_file(m_voice_id, 0);
	m_voice_id = -1;

	z = m_cur_stage;
	m_cur_stage = -1;
	update_data(1);
	for (i=Briefing->num_stages; i>z; i--)	{
		copy_stage(i-1, i);
	}

	Briefing->num_stages++;
	copy_stage(z, z + 1);
	m_cur_stage = z;
	update_data(0);
}

void briefing_editor_dlg::copy_stage(int from, int to)
{
	if ((from < 0) || (from >= Briefing->num_stages)) {
		Briefing->stages[to].text = "<Text here>";
		strcpy_s(Briefing->stages[to].voice, "none.wav");
		Briefing->stages[to].camera_pos = view_pos;
		Briefing->stages[to].camera_orient = view_orient;
		Briefing->stages[to].camera_time = 500;
		Briefing->stages[to].num_icons = 0;
		Briefing->stages[to].formula = Locked_sexp_true;
		Briefing->stages[to].grid_color = Color_briefing_grid;
		return;
	}

	// Copy all the data in the stage structure.
	Briefing->stages[to].text = Briefing->stages[from].text;
	strcpy_s( Briefing->stages[to].voice, Briefing->stages[from].voice );
	Briefing->stages[to].camera_pos = Briefing->stages[from].camera_pos;
	Briefing->stages[to].camera_orient = Briefing->stages[from].camera_orient;
	Briefing->stages[to].camera_time = Briefing->stages[from].camera_time;
	Briefing->stages[to].flags = Briefing->stages[from].flags;
	Briefing->stages[to].num_icons = Briefing->stages[from].num_icons;
	Briefing->stages[to].num_lines = Briefing->stages[from].num_lines;
	Briefing->stages[to].formula = Briefing->stages[from].formula;
	// For now let's just always set this back to default. Eventually when we have a UI color picker in qtFRED, we can copy from stage to stage
	Briefing->stages[to].grid_color = Color_briefing_grid;

	memmove( Briefing->stages[to].icons, Briefing->stages[from].icons, sizeof(brief_icon)*MAX_STAGE_ICONS );
	memmove( Briefing->stages[to].lines, Briefing->stages[from].lines, sizeof(brief_line)*MAX_BRIEF_STAGE_LINES );
}

void briefing_editor_dlg::update_positions()
{
	int i, s, z;
	vec3d v1, v2;

	if (m_cur_stage < 0)
		return;

	for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
		v1 = Briefing->stages[m_cur_stage].icons[i].pos;
		v2 = Objects[icon_obj[i]].pos;
		if ((v1.xyz.x != v2.xyz.x) || (v1.xyz.y != v2.xyz.y) || (v1.xyz.z != v2.xyz.z)) {
			Briefing->stages[m_cur_stage].icons[i].pos = Objects[icon_obj[i]].pos;
			if (!m_change_local)  // propagate changes through rest of stages..
				for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
					z = find_icon(Briefing->stages[m_cur_stage].icons[i].id, s);
					if (z >= 0)
						Briefing->stages[s].icons[z].pos = Objects[icon_obj[i]].pos;
				}
		}
	}
}

void briefing_editor_dlg::OnMakeIcon() 
{
	const char *name;
	int team, ship, waypoint, count = -1;
	int cargo = 0, cargo_count = 0, freighter_count = 0;
	object *ptr;
	vec3d min, max, pos;
	brief_icon *biconp;
	CJumpNode *jnp = nullptr;

	if (Briefing->stages[m_cur_stage].num_icons >= MAX_STAGE_ICONS)
		return;

	m_cur_icon = Briefing->stages[m_cur_stage].num_icons++;
	biconp = &Briefing->stages[m_cur_stage].icons[m_cur_icon];
	ship = waypoint = -1;
	team = 0;

	vm_vec_make(&min, 9e19f, 9e19f, 9e19f);
	vm_vec_make(&max, -9e19f, -9e19f, -9e19f);
	ptr = GET_FIRST(&obj_used_list);
	while (ptr != END_OF_LIST(&obj_used_list)) {
		if (ptr->flags[Object::Object_Flags::Marked]) {
			if (ptr->pos.xyz.x < min.xyz.x)
				min.xyz.x = ptr->pos.xyz.x;
			if (ptr->pos.xyz.x > max.xyz.x)
				max.xyz.x = ptr->pos.xyz.x;
			if (ptr->pos.xyz.y < min.xyz.y)
				min.xyz.y = ptr->pos.xyz.y;
			if (ptr->pos.xyz.y > max.xyz.y)
				max.xyz.y = ptr->pos.xyz.y;
			if (ptr->pos.xyz.z < min.xyz.z)
				min.xyz.z = ptr->pos.xyz.z;
			if (ptr->pos.xyz.z > max.xyz.z)
				max.xyz.z = ptr->pos.xyz.z;
			
			switch (ptr->type) {
				case OBJ_SHIP:
				case OBJ_START:
					ship = ptr->instance;
					break;

				case OBJ_WAYPOINT:
					waypoint = ptr->instance;
					break;
				case OBJ_JUMP_NODE:
				    jnp = jumpnode_get_by_objnum(OBJ_INDEX(ptr)); 
					break;

				default:
					Int3();
			}

			if (ship >= 0) {
				team = Ships[ship].team;

				if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Cargo])
					cargo_count++;

				if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Freighter])
				{
					// direct docked with any marked cargo?
					for (dock_instance *dock_ptr = ptr->dock_list; dock_ptr != NULL; dock_ptr = dock_ptr->next)
					{
						if (dock_ptr->docked_objp->flags[Object::Object_Flags::Marked])
						{
							if (Ship_info[Ships[dock_ptr->docked_objp->instance].ship_info_index].flags[Ship::Info_Flags::Cargo])
								freighter_count++;
						}
					}
				}
			}

			count++;
		}

		ptr = GET_NEXT(ptr);
	}

	if (cargo_count && cargo_count == freighter_count)
		cargo = 1;

	vm_vec_avg(&pos, &min, &max);
	if (ship >= 0)
		name = Ships[ship].ship_name;
	else if (waypoint >= 0)
	{
		waypoint_list *wp_list = find_waypoint_list_with_instance(waypoint);
		Assert(wp_list != NULL);
		name = wp_list->get_name();
	}
	else if (jnp != nullptr)
		name = jnp->GetName();
	// Return if we didn't find anything somehow
	else
		return;

	auto len = strlen(name);
	if (len >= MAX_LABEL_LEN - 1)
		len = MAX_LABEL_LEN - 1;

	strncpy(biconp->label, name, len);
	biconp->label[len] = 0;
//	iconp->text[0] = 0;
	biconp->type = 0;
	biconp->team = team;
	biconp->pos = pos;
	biconp->flags = 0;
	biconp->id = Cur_brief_id++;
	biconp->scale_factor = 1.0f;

	biconp->modelnum = -1;
	biconp->model_instance_num = -1;

	if (ship >= 0) {
		biconp->ship_class = Ships[ship].ship_info_index;
        ship_info* sip = &Ship_info[Ships[ship].ship_info_index];

        if (sip->flags[Ship::Info_Flags::Knossos_device])
            biconp->type = ICON_KNOSSOS_DEVICE;
        else if (sip->flags[Ship::Info_Flags::Corvette])
            biconp->type = ICON_CORVETTE;
        else if (sip->flags[Ship::Info_Flags::Gas_miner])
            biconp->type = ICON_GAS_MINER;
        else if (sip->flags[Ship::Info_Flags::Supercap])
            biconp->type = ICON_SUPERCAP;
        else if (sip->flags[Ship::Info_Flags::Sentrygun])
            biconp->type = ICON_SENTRYGUN;
        else if (sip->flags[Ship::Info_Flags::Awacs])
            biconp->type = ICON_AWACS;
        else if (sip->flags[Ship::Info_Flags::Cargo])
            if (cargo)
                biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
            else
                biconp->type = count ? ICON_CARGO_WING : ICON_CARGO;
        else if (sip->flags[Ship::Info_Flags::Support])
            biconp->type = ICON_SUPPORT_SHIP;
        else if (sip->flags[Ship::Info_Flags::Fighter])
            biconp->type = count ? ICON_FIGHTER_WING : ICON_FIGHTER;
        else if (sip->flags[Ship::Info_Flags::Bomber])
            biconp->type = count ? ICON_BOMBER_WING : ICON_BOMBER;
        else if (sip->flags[Ship::Info_Flags::Freighter])
            if (cargo)
                biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
            else
                biconp->type = count ? ICON_FREIGHTER_WING_NO_CARGO : ICON_FREIGHTER_NO_CARGO;
        else if (sip->flags[Ship::Info_Flags::Cruiser])
            biconp->type = count ? ICON_CRUISER_WING : ICON_CRUISER;
        else if (sip->flags[Ship::Info_Flags::Transport])
            biconp->type = count ? ICON_TRANSPORT_WING : ICON_TRANSPORT;
        else if (sip->flags[Ship::Info_Flags::Capital] || sip->flags[Ship::Info_Flags::Drydock])
            biconp->type = ICON_CAPITAL;
        else if (sip->flags[Ship::Info_Flags::Navbuoy])
            biconp->type = ICON_WAYPOINT;
        else
            biconp->type = ICON_ASTEROID_FIELD;
	}
	// jumpnodes -- have to use Navbuoy if clicked on during briefing because jump nodes are not in ship_info
	else if (jnp != nullptr) {
		// find the first navbuoy, by iterating through the ship classes
		biconp->ship_class = -1;
		
		for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
		{
			if (it->flags[Ship::Info_Flags::Navbuoy])
			{
                biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
                break;
            }
        }
		
		biconp->type = ICON_JUMP_NODE;
	} 
	// everything else -- have to use Navbuoy if clicked on during briefing because waypoints etc. are not in ship_info
	else {
		// find the first navbuoy
		biconp->ship_class = -1;

		for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) 
		{
            if (it->flags[Ship::Info_Flags::Navbuoy])
            {
				biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
                break;
            }
        }
		biconp->type = ICON_WAYPOINT;
	}

	if (!m_change_local){
		propagate_icon(m_cur_icon);
	}

    flagset<Object::Object_Flags> default_flags;
    default_flags.set(Object::Object_Flags::Renders);
	icon_obj[m_cur_icon] = obj_create(OBJ_POINT, -1, m_cur_icon, NULL, &pos, 0.0f, default_flags);
	Assert(icon_obj[m_cur_icon] >= 0);
	obj_merge_created_list();
	unmark_all();
	set_cur_object_index(icon_obj[m_cur_icon]);
	GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);
	GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
	update_data(1);
}

void briefing_editor_dlg::OnDeleteIcon()
{
	delete_icon(m_cur_icon);
}

void briefing_editor_dlg::delete_icon(int num)
{
	int i, z;

	if (num < 0)
		num = m_cur_icon;

	if (num < 0)
		return;

	Assert(m_cur_stage >= 0);
	Assert(Briefing->stages[m_cur_stage].num_icons);
	z = m_cur_icon;
	if (z == num)
		z = -1;
	if (z > num)
		z--;

	m_cur_icon = -1;
	update_data(1);
	obj_delete(icon_obj[num]);
	for (i=num+1; i<Briefing->stages[m_cur_stage].num_icons; i++) {
		Briefing->stages[m_cur_stage].icons[i-1] = Briefing->stages[m_cur_stage].icons[i];
		icon_obj[i-1] = icon_obj[i];
		Objects[icon_obj[i-1]].instance = i - 1;
	}

	Briefing->stages[m_cur_stage].num_icons--;
	if (z >= 0) {
		m_cur_icon = z;
		update_data(0);
	}
}

void briefing_editor_dlg::OnGotoView()
{
	if (m_cur_stage < 0)
		return;

	view_pos = Briefing->stages[m_cur_stage].camera_pos;
	view_orient = Briefing->stages[m_cur_stage].camera_orient;
	Update_window = 1;
}

void briefing_editor_dlg::OnSaveView()
{
	if (m_cur_stage < 0)
		return;

	Briefing->stages[m_cur_stage].camera_pos = view_pos;
	Briefing->stages[m_cur_stage].camera_orient = view_orient;
}

void briefing_editor_dlg::OnSelchangeIconImage()
{
	update_data(1);
}

void briefing_editor_dlg::OnSelchangeTeam()
{
	update_data(1);
}

void briefing_editor_dlg::OnSelchangeShipType()
{
	update_data(1);
}

int briefing_editor_dlg::check_mouse_hit(int x, int y)
{
	int i;
	brief_icon *ptr;

	if (m_cur_stage < 0)
		return -1;

	for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
		ptr = &Briefing->stages[m_cur_stage].icons[i];
		if ((x > ptr->x) && (x < ptr->x + ptr->w) && (y > ptr->y) && (y < ptr->y + ptr->h))	{
			return icon_obj[i];
		}
	}

	return -1;
}

void briefing_editor_dlg::OnPropagateIcons() 
{
	object *ptr;

	ptr = GET_FIRST(&obj_used_list);
	while (ptr != END_OF_LIST(&obj_used_list)) {
		if ((ptr->type == OBJ_POINT) && (ptr->flags[Object::Object_Flags::Marked])) {
			propagate_icon(ptr->instance);
		}

		ptr = GET_NEXT(ptr);
	}
}

void briefing_editor_dlg::propagate_icon(int num)
{
	int i, s;

	for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
		i = Briefing->stages[s].num_icons;
		if (i >= MAX_STAGE_ICONS)
			continue;

		if (find_icon(Briefing->stages[m_cur_stage].icons[num].id, s) >= 0)
			continue;  // don't change if icon exists here already.

		Briefing->stages[s].icons[i] = Briefing->stages[m_cur_stage].icons[num];
		Briefing->stages[s].num_icons++;
	}
}

int briefing_editor_dlg::find_icon(int id, int stage)
{
	int i;

	if (id >= 0)
		for (i=0; i<Briefing->stages[stage].num_icons; i++)
			if (Briefing->stages[stage].icons[i].id == id)
				return i;

	return -1;
}

void briefing_editor_dlg::reset_icon_loop(int stage)
{
	stage_loop = stage + 1;
	icon_loop = -1;
}

int briefing_editor_dlg::get_next_icon(int id)
{
	while (1) {
		icon_loop++;
		if (icon_loop >= Briefing->stages[stage_loop].num_icons) {
			stage_loop++;
			if (stage_loop > Briefing->num_stages)
				return 0;

			icon_loop = -1;
			continue;
		}

		iconp = &Briefing->stages[stage_loop].icons[icon_loop];
		if ((id >= 0) && (iconp->id == id))
			return 1;
	}
}

BOOL briefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	int id;

	// deal with figuring out menu stuff
	id = LOWORD(wParam);
	if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) {
		m_current_briefing = id - ID_TEAM_1;

		// put user back at first stage for this team (or no current stage is there are none).
		Briefing = &Briefings[m_current_briefing];
		if ( Briefing->num_stages > 0 )
			m_cur_stage = 0;
		else
			m_cur_stage = -1;

		update_data(1);
		OnGotoView();
		return 1;
	}

	return CDialog::OnCommand(wParam, lParam);
}

void briefing_editor_dlg::OnLines() 
{
	if (m_lines.GetCheck() == 1)
		m_lines.SetCheck(0);
	else
		m_lines.SetCheck(1);

	update_data(1);
}

void briefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	m_tree.right_clicked();	
	*pResult = 0;
}

void briefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

	if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
		*pResult = 0;
		modified = 1;

	} else
		*pResult = 1;
}

void briefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

	*pResult = m_tree.end_label_edit(pTVDispInfo->item);
}

BOOL briefing_editor_dlg::DestroyWindow()
{
	Briefing_dialog = nullptr;
	if (m_play_icon) DestroyIcon(m_play_icon);
	audiostream_close_file(m_voice_id, 0);
	return CDialog::DestroyWindow();
}

void briefing_editor_dlg::OnPlay() 
{
	GetDlgItem(IDC_VOICE)->GetWindowText(m_voice);

	if (m_voice_id >= 0) {
		audiostream_close_file(m_voice_id, 0);
		m_voice_id = -1;
		return;
	}

	// we use ASF_EVENTMUSIC here so that it will keep the extension in place
	m_voice_id = audiostream_open((char *)(LPCSTR) m_voice, ASF_EVENTMUSIC);

	if (m_voice_id >= 0) {
		audiostream_play(m_voice_id, 1.0f, 0);
	}
}

void briefing_editor_dlg::OnCopyView() 
{
	// TODO: Add your control notification handler code here

	m_copy_view_set = 1;
	m_copy_view_pos = view_pos;
	m_copy_view_orient = view_orient;
}

void briefing_editor_dlg::OnPasteView() 
{
	// TODO: Add your control notification handler code here
	if (m_cur_stage < 0)
		return;

	if (m_copy_view_set == 0) {
		MessageBox("No view set", "Unable to copy view");
	} else {
		Briefing->stages[m_cur_stage].camera_pos = m_copy_view_pos;
		Briefing->stages[m_cur_stage].camera_orient = m_copy_view_orient;
		
		update_data(1);
		OnGotoView();
	}
}

void briefing_editor_dlg::OnFlipIcon() 
{
	update_data(1);	
}

void briefing_editor_dlg::OnWingIcon()
{
	update_data(1);
}

void briefing_editor_dlg::OnCargoIcon()
{
	update_data(1);
}