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

// CampaignEditorDlg.cpp : implementation file
//

#include <setjmp.h>
#include "stdafx.h"
#include "FRED.h"
#include "CampaignEditorDlg.h"
#include "CampaignTreeView.h"
#include "CampaignTreeWnd.h"
#include "Management.h"
#include "cfile/cfile.h"
#include "FREDDoc.h"
#include "parse/parselo.h"
#include "mission/missiongoals.h"

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

int Cur_campaign_mission = -1;
int Cur_campaign_link = -1;

/////////////////////////////////////////////////////////////////////////////
// campaign_editor dialog

IMPLEMENT_DYNCREATE(campaign_editor, CFormView)

campaign_editor *Campaign_tree_formp;

campaign_editor::campaign_editor()
	: CFormView(campaign_editor::IDD)
{
	//{{AFX_DATA_INIT(campaign_editor)
	m_name = _T("");
	m_type = -1;
	m_num_players = _T("");
	m_desc = _T("");
	m_branch_desc = _T("");
	m_branch_brief_anim = _T("");
	m_branch_brief_sound = _T("");
	m_custom_tech_db = FALSE;
	//}}AFX_DATA_INIT

	m_tree.m_mode = MODE_CAMPAIGN;
	m_num_links = 0;
	m_tree.link_modified(&Campaign_modified);
	m_last_mission = -1;

	m_current_campaign_path = _T("");
}

campaign_editor::~campaign_editor()
{
}

void campaign_editor::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(campaign_editor)
	DDX_Control(pDX, IDC_SEXP_TREE, m_tree);
	DDX_Control(pDX, IDC_FILELIST, m_filelist);
	DDX_Text(pDX, IDC_NAME, m_name);
	DDX_CBIndex(pDX, IDC_CAMPAIGN_TYPE, m_type);
	DDX_Text(pDX, IDC_NUM_PLAYERS, m_num_players);
	DDX_Text(pDX, IDC_DESC2, m_desc);
	//DDV_MaxChars(pDX, m_desc, MISSION_DESC_LENGTH - 1);
	DDX_Text(pDX, IDC_MISSION_LOOP_DESC, m_branch_desc);
	DDV_MaxChars(pDX, m_branch_desc, MISSION_DESC_LENGTH - 1);
	DDX_Text(pDX, IDC_LOOP_BRIEF_ANIM, m_branch_brief_anim);
	DDX_Text(pDX, IDC_LOOP_BRIEF_SOUND, m_branch_brief_sound);
	DDX_Check(pDX, IDC_CUSTOM_TECH_DB, m_custom_tech_db);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(campaign_editor, CFormView)
	//{{AFX_MSG_MAP(campaign_editor)
	ON_BN_CLICKED(ID_LOAD, OnLoad)
	ON_BN_CLICKED(IDC_ALIGN, OnAlign)
	ON_BN_CLICKED(ID_CPGN_CLOSE, OnCpgnClose)
	ON_NOTIFY(NM_RCLICK, IDC_SEXP_TREE, OnRclickTree)
	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_SEXP_TREE, OnBeginlabeleditSexpTree)
	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_SEXP_TREE, OnEndlabeleditSexpTree)
	ON_NOTIFY(TVN_SELCHANGED, IDC_SEXP_TREE, OnSelchangedSexpTree)
	ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
	ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
	ON_COMMAND(ID_END_EDIT, OnEndEdit)
	ON_EN_CHANGE(IDC_BRIEFING_CUTSCENE, OnChangeBriefingCutscene)
	ON_CBN_SELCHANGE(IDC_CAMPAIGN_TYPE, OnSelchangeType)
	ON_BN_CLICKED(IDC_TOGGLE_LOOP, OnToggleLoop)
	ON_BN_CLICKED(IDC_LOOP_BRIEF_BROWSE, OnBrowseLoopAni)
	ON_BN_CLICKED(IDC_LOOP_BRIEF_SOUND_BROWSE, OnBrowseLoopSound)
	ON_EN_CHANGE(IDC_MAIN_HALL, OnChangeMainHall)
	ON_EN_CHANGE(IDC_SUBSTITUTE_MAIN_HALL, OnChangeSubstituteMainHall)
	ON_EN_CHANGE(IDC_DEBRIEFING_PERSONA, OnChangeDebriefingPersona)
	ON_BN_CLICKED(IDC_CUSTOM_TECH_DB, OnCustomTechDB)
	ON_BN_CLICKED(IDC_OPEN_CUSTOM_DATA, OnCustomData)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// campaign_editor diagnostics

#ifdef _DEBUG
void campaign_editor::AssertValid() const
{
	CFormView::AssertValid();
}

void campaign_editor::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// campaign_editor message handlers

void campaign_editor::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	UpdateData(FALSE);
}

void campaign_editor::OnLoad() 
{
	if (Cur_campaign_mission < 0){
		return;
	}

	if (Campaign_modified){
		if (Campaign_wnd->save_modified()){
			return;
		}
	}

	// try to open the file from the same folder as the campaign
	if (!m_current_campaign_path.IsEmpty()) {
		auto full_mission_path = GetPathWithoutFile();
		full_mission_path.Append(Campaign.missions[Cur_campaign_mission].name);

		auto res = cf_find_file_location((LPCTSTR)full_mission_path, CF_TYPE_MISSIONS, false);
		if (res.found) {
			FREDDoc_ptr->SetPathName(res.full_name.c_str());
		}
	}

	Campaign_wnd->DestroyWindow();
}

BOOL campaign_editor::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	int i;
	BOOL r;
	CComboBox *box;

	r = CFormView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
	if (r) {
		box = (CComboBox *) GetDlgItem(IDC_CAMPAIGN_TYPE);
		box->ResetContent();
		for (i=0; i<MAX_CAMPAIGN_TYPES; i++){
			box->AddString(campaign_types[i]);
		}
	}

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

	CWnd* pWnd = GetDlgItem(IDC_SUBSTITUTE_MAIN_HALL);
	m_SubstituteMainHallToolTip->AddTool(pWnd, "Main hall to be used if optional main hall pack is present");
	m_SubstituteMainHallToolTip->Activate(TRUE);

	return r;
}

BOOL campaign_editor::PreTranslateMessage(MSG* pMsg)
{
	m_SubstituteMainHallToolTip->RelayEvent(pMsg);
	return CFormView::PreTranslateMessage(pMsg);
}

void campaign_editor::load_campaign(const char *filename, const char *full_path)
{
	Cur_campaign_mission = Cur_campaign_link = -1;
	load_tree(0);

	if (!filename || !strlen(filename))
		filename = Default_campaign_file_name;

	if (full_path && strlen(full_path))
		m_current_campaign_path = full_path;
	else
		m_current_campaign_path = _T("");

	auto result = mission_campaign_load(filename, full_path, nullptr, 0);
	if (result != 0) {
		if (result == CAMPAIGN_ERROR_CORRUPT)
			MessageBox("Requested campaign file is corrupt.", "Could not load campaign file");
		else if (result == CAMPAIGN_ERROR_SEXP_EXHAUSTED)
			MessageBox("Requested campaign requires too many SEXPs.", "Could not load campaign file");
		else if (result == CAMPAIGN_ERROR_MISSING)
			MessageBox("Requested campaign file could not be found.", "Could not load campaign file");
		else if (result == CAMPAIGN_ERROR_SAVEFILE)
			MessageBox("The pilot savefile for this campaign is invalid for the current mod.", "Could not load campaign file");
		else if (result == CAMPAIGN_ERROR_IGNORED)
			MessageBox("This campaign file is ignored by the current mod.", "Could not load campaign file");
		else
			MessageBox("Couldn't open Campaign file!", "Error");
		Campaign_wnd->OnCpgnFileNew();
		return;
	}

	Campaign_modified = 0;
	Campaign_tree_viewp->construct_tree();
	initialize(true, false);
}

void campaign_editor::OnAlign() 
{
	Campaign_tree_viewp->sort_elements();
	Campaign_tree_viewp->realign_tree();
	Campaign_tree_viewp->Invalidate();
}

void campaign_editor::initialize( bool init_files, bool clear_path )
{
	Cur_campaign_mission = Cur_campaign_link = -1;
	m_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
	load_tree(0);
	Campaign_tree_viewp->initialize();

	if (clear_path)
		m_current_campaign_path = _T("");

	// only initialize the file dialog box when the parameter says to.  This should
	// only happen when a campaign type changes
	if ( init_files ){
		m_filelist.initialize(GetPathWithoutFile());
	}

	m_name = Campaign.name;
	m_type = Campaign.type;
	m_num_players.Format("%d", Campaign.num_players);

	if (!Campaign.description.empty()) {
		convert_multiline_string(m_desc, Campaign.description);
	} else {
		m_desc = _T("");
	}

	m_branch_desc = _T("");

	m_branch_brief_anim = _T("");	
	m_branch_brief_sound = _T("");	

	// single player should hide the two dialog items about number of players allowed
	if ( m_type == CAMPAIGN_TYPE_SINGLE ) {
		GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_HIDE );
		GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_HIDE );
	} else {
		GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_SHOW );
		GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_SHOW );
	}

	// set up the tech db checkbox
	m_custom_tech_db = (BOOL)(Campaign.flags & CF_CUSTOM_TECH_DATABASE);

	UpdateData(FALSE);
}

void campaign_editor::mission_selected(int num)
{
	CEdit *bc_dialog, *bc_hall, *bc_substitute_hall, *bc_persona;
	char personatext[10];

	bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE);
	bc_hall = (CEdit *) GetDlgItem(IDC_MAIN_HALL);
	bc_substitute_hall = (CEdit *) GetDlgItem(IDC_SUBSTITUTE_MAIN_HALL);
	bc_persona = (CEdit *) GetDlgItem(IDC_DEBRIEFING_PERSONA);

	// clear out the text for the briefing cutscene, and put in new text if specified
	bc_dialog->SetWindowText("");
	if ( strlen(Campaign.missions[num].briefing_cutscene) )
		bc_dialog->SetWindowText(Campaign.missions[num].briefing_cutscene);

	// new main hall stuff
	bc_hall->SetWindowText(Campaign.missions[num].main_hall.c_str());
	bc_substitute_hall->SetWindowText(Campaign.missions[num].substitute_main_hall.c_str());

	// new debriefing persona stuff
	sprintf(personatext, "%d", Campaign.missions[num].debrief_persona_index);
	bc_persona->SetWindowText(CString(personatext));
}

void campaign_editor::update()
{
	// get data from dlog box
	UpdateData(TRUE);

	// handle special characters
	lcl_fred_replace_stuff(m_name);
	lcl_fred_replace_stuff(m_desc);

	// update campaign name
	string_copy(Campaign.name, m_name, NAME_LENGTH - 1);
	Campaign.type = m_type;

	// update campaign desc
	SCP_string desc_buf;
	deconvert_multiline_string(desc_buf, m_desc);

	Campaign.description.clear();
	Campaign.description = desc_buf;

	// update flags
	Campaign.flags = CF_DEFAULT_VALUE;
	if (m_custom_tech_db)
		Campaign.flags |= CF_CUSTOM_TECH_DATABASE;

	// maybe update mission loop text
	save_loop_desc_window();

	// set the number of players in a multiplayer mission equal to the number of players in the first mission
	if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
		if ( Campaign.num_missions == 0 ) {
			Campaign.num_players = 0;
		} else {
			mission a_mission;

			get_mission_info(Campaign.missions[0].name, &a_mission);
			Campaign.num_players = a_mission.num_players;
		}
	}
}

void campaign_editor::OnCpgnClose() 
{
	Campaign_wnd->OnClose();
}

void campaign_editor::load_tree(int save_first)
{
	char text[80];
	int i;
	HTREEITEM h;

	if (save_first)
		save_tree();

	m_tree.clear_tree();
	m_tree.DeleteAllItems();
	m_num_links = 0;
	UpdateData(TRUE);
	update_loop_desc_window();

	m_last_mission = Cur_campaign_mission;
	if (Cur_campaign_mission < 0) {
		GetDlgItem(IDC_SEXP_TREE)->EnableWindow(FALSE);
		GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(FALSE);
		GetDlgItem(IDC_MAIN_HALL)->EnableWindow(FALSE);
		GetDlgItem(IDC_SUBSTITUTE_MAIN_HALL)->EnableWindow(FALSE);
		return;
	}

	GetDlgItem(IDC_SEXP_TREE)->EnableWindow(TRUE);
	GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(TRUE);
	GetDlgItem(IDC_MAIN_HALL)->EnableWindow(TRUE);
	GetDlgItem(IDC_SUBSTITUTE_MAIN_HALL)->EnableWindow(TRUE);

	GetDlgItem(IDC_MISSION_LOOP_DESC)->EnableWindow(FALSE);
	GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(FALSE);
	GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(FALSE);
	GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(FALSE);
	GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(FALSE);

	for (i=0; i<Total_links; i++) {
		if (Links[i].from == Cur_campaign_mission) {
			Links[i].node = m_tree.load_sub_tree(Links[i].sexp, true, "do-nothing");
			m_num_links++;

			if (Links[i].from == Links[i].to) {
				strcpy_s(text, "Repeat mission");
			} else if ( (Links[i].to == -1) && (Links[i].from != -1) ) {
				strcpy_s(text, "End of Campaign");
			} else {
				sprintf(text, "Branch to %s", Campaign.missions[Links[i].to].name);
			}

			// insert item into tree
			int image, sel_image;
			if (Links[i].is_mission_loop || Links[i].is_mission_fork) {
				image = BITMAP_BLUE_DOT;
				sel_image = BITMAP_GREEN_DOT;
			} else {
				image = BITMAP_BLACK_DOT;
				sel_image = BITMAP_RED_DOT;
			}

			h = m_tree.insert(text, image, sel_image);

			m_tree.SetItemData(h, Links[i].node);
			m_tree.add_sub_tree(Links[i].node, h);
		}
	}
}

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

void campaign_editor::OnBeginlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

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

void campaign_editor::OnEndlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

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

int campaign_editor::handler(int code, int node, char *str)
{
	int i;

	switch (code) {
	case ROOT_DELETED:
		for (i=0; i<Total_links; i++){
			if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == node)){
				break;
			}
		}

		Campaign_tree_viewp->delete_link(i);
		m_num_links--;
		return node;

	default:
		Int3();
	}

	return -1;
}

void campaign_editor::save_tree(int clear)
{
	int i;

	if (m_last_mission < 0){
		return;  // nothing to save
	}

	for (i=0; i<Total_links; i++){
		if (Links[i].from == m_last_mission) {
			sexp_unmark_persistent(Links[i].sexp);
			free_sexp2(Links[i].sexp);
			Links[i].sexp = m_tree.save_tree(Links[i].node);
			sexp_mark_persistent(Links[i].sexp);
		}
	}

	if (clear){
		m_last_mission = -1;
	}
}

void campaign_editor::OnSelchangedSexpTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	int i, node;
	HTREEITEM h, h2;

	// get handle of selected item
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	h = pNMTreeView->itemNew.hItem;
	Assert(h);

	// update help on sexp
	m_tree.update_help(h);

	// get handle of parent
	while ((h2 = m_tree.GetParentItem(h))>0){
		h = h2;
	}

	// get identifier of parent
	node = (int)m_tree.GetItemData(h);
	for (i=0; i<Total_links; i++){
		if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == node)){
			break;
		}
	}

	if (i == Total_links) {
		Cur_campaign_link = -1;
		return;
	}

	// update mission loop text
	UpdateData(TRUE);
	save_loop_desc_window();

	Cur_campaign_link = i;
	update_loop_desc_window();
	Campaign_tree_viewp->Invalidate();
	*pResult = 0;
}

void campaign_editor::OnMoveUp() 
{
	int i, last = -1;
	campaign_tree_link temp;
	HTREEITEM h1, h2;

	if (Cur_campaign_link >= 0) {
		save_tree();
		for (i=0; i<Total_links; i++){
			if (Links[i].from == Cur_campaign_mission) {
				if (i == Cur_campaign_link){
					break;
				}

				last = i;
			}
		}

		if ((last != -1) && (i < Total_links)) {
			h1 = m_tree.GetParentItem(m_tree.handle(Links[i].node));
			h2 = m_tree.GetParentItem(m_tree.handle(Links[last].node));
			m_tree.move_root(h1, h2, true);
			m_tree.SelectItem(m_tree.GetParentItem(m_tree.handle(Links[i].node)));

			temp = Links[last];
			Links[last] = Links[i];
			Links[i] = temp;
			Cur_campaign_link = last;
		}
	}

	GetDlgItem(IDC_SEXP_TREE)->SetFocus();
}

void campaign_editor::OnMoveDown() 
{
	int i, j;
	campaign_tree_link temp;
	HTREEITEM h1, h2;

	if (Cur_campaign_link >= 0) {
		save_tree();
		for (i=0; i<Total_links; i++)
			if (Links[i].from == Cur_campaign_mission)
				if (i == Cur_campaign_link)
					break;

		for (j=i+1; j<Total_links; j++)
			if (Links[j].from == Cur_campaign_mission)
				break;

		if (j < Total_links) {
			h1 = m_tree.GetParentItem(m_tree.handle(Links[i].node));
			h2 = m_tree.GetParentItem(m_tree.handle(Links[j].node));
			m_tree.move_root(h1, h2, false);
			m_tree.SelectItem(m_tree.GetParentItem(m_tree.handle(Links[i].node)));

			temp = Links[j];
			Links[j] = Links[i];
			Links[i] = temp;
			Cur_campaign_link = j;
		}
	}

	GetDlgItem(IDC_SEXP_TREE)->SetFocus();
}

void campaign_editor::move_handler(int node1, int node2, bool insert_before)
{
	int index1, index2;
	campaign_tree_link temp;

	for (index1=0; index1<Total_links; index1++){
		if ((Links[index1].from == Cur_campaign_mission) && (Links[index1].node == node1)){
			break;
		}
	}
	Assert(index1 < Total_links);

	for (index2=0; index2<Total_links; index2++){
		if ((Links[index2].from == Cur_campaign_mission) && (Links[index2].node == node2)){
			break;
		}
	}
	Assert(index2 < Total_links);

	temp = Links[index1];

	int offset = insert_before ? -1 : 0;

	while (index1 < index2 + offset) {
		Links[index1] = Links[index1 + 1];
		index1++;
	}
	while (index1 > index2 + offset + 1) {
		Links[index1] = Links[index1 - 1];
		index1--;
	}

	Links[index1] = temp;

	// update Cur_campaign_link
	Cur_campaign_link = index2;
}

void campaign_editor::insert_handler(int old, int node)
{
	int i;

	for (i=0; i<Total_links; i++){
		if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == old)){
			break;
		}
	}

	Assert(i < Total_links);
	Links[i].node = node;
	return;
}

void campaign_editor::OnEndEdit() 
{
	HWND h;
	CWnd *w;

	w = GetFocus();
	if (w) {
		h = w->m_hWnd;
		GetDlgItem(IDC_SEXP_TREE)->SetFocus();
		::SetFocus(h);
	}
}

void campaign_editor::OnChangeBriefingCutscene() 
{
	CEdit *bc_dialog;

	bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE);

	// maybe save off the current cutscene names.
	if ( Cur_campaign_mission != -1 ) {

		// see if the contents of the edit box have changed.  Luckily, this returns 0 when the edit box is
		// cleared.
		if ( bc_dialog->GetModify() ) {
			bc_dialog->GetWindowText( Campaign.missions[Cur_campaign_mission].briefing_cutscene, NAME_LENGTH );
			Campaign_modified = 1;
		}
	}

}

// what to do when changing campaign type
void campaign_editor::OnSelchangeType() 
{
	// if campaign type is single player, then disable the multiplayer items
	update();
	initialize(true, false);
	Campaign_modified = 1;
}


// update the loop mission text
void campaign_editor::save_loop_desc_window()
{
	char buffer[MISSION_DESC_LENGTH];

	// update only if active link and there is a mission has mission loop or fork
	if ( (Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) ) {
		// handle special characters
		lcl_fred_replace_stuff(m_branch_desc);

		deconvert_multiline_string(buffer, m_branch_desc, MISSION_DESC_LENGTH - 1);
		if (Links[Cur_campaign_link].mission_branch_txt) {
			free(Links[Cur_campaign_link].mission_branch_txt);
		}
		if (Links[Cur_campaign_link].mission_branch_brief_anim) {
			free(Links[Cur_campaign_link].mission_branch_brief_anim);
		}
		if (Links[Cur_campaign_link].mission_branch_brief_sound) {
			free(Links[Cur_campaign_link].mission_branch_brief_sound);
		}

		if (strlen(buffer)) {
			Links[Cur_campaign_link].mission_branch_txt = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_txt = NULL;
		}

		deconvert_multiline_string(buffer, m_branch_brief_anim, MAX_FILENAME_LEN - 1);
		if(strlen(buffer)){
			Links[Cur_campaign_link].mission_branch_brief_anim = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_brief_anim = NULL;
		}

		deconvert_multiline_string(buffer, m_branch_brief_sound, MAX_FILENAME_LEN - 1);
		if(strlen(buffer)){
			Links[Cur_campaign_link].mission_branch_brief_sound = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_brief_sound = NULL;
		}
	}
}

void campaign_editor::update_loop_desc_window()
{
	bool enable_branch_desc_window;

	enable_branch_desc_window = false;
	if ((Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork)) {
		enable_branch_desc_window = true;
	}

	// maybe enable description window
	GetDlgItem(IDC_MISSION_LOOP_DESC)->EnableWindow(enable_branch_desc_window);
	GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(enable_branch_desc_window);
	GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(enable_branch_desc_window);
	GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(enable_branch_desc_window);
	GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(enable_branch_desc_window);

	// set new text
	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_txt && enable_branch_desc_window) {
		convert_multiline_string(m_branch_desc, Links[Cur_campaign_link].mission_branch_txt);		
	} else {
		m_branch_desc = _T("");
	}

	// set new text
	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_brief_anim && enable_branch_desc_window) {
		convert_multiline_string(m_branch_brief_anim, Links[Cur_campaign_link].mission_branch_brief_anim);		
	} else {
		m_branch_brief_anim = _T("");
	}

	// set new text
	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_brief_sound && enable_branch_desc_window) {
		convert_multiline_string(m_branch_brief_sound, Links[Cur_campaign_link].mission_branch_brief_sound);
	} else {
		m_branch_brief_sound = _T("");
	}

	// reset text
	UpdateData(FALSE);
}

void campaign_editor::OnToggleLoop() 
{
	// check if branch selected
	if (Cur_campaign_link == -1) {
		return;
	}

	// store mission loop text
	UpdateData(TRUE);

	if ( (Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) ) {
		if (Links[Cur_campaign_link].mission_branch_txt) {
			free(Links[Cur_campaign_link].mission_branch_txt);
		}

		if (Links[Cur_campaign_link].mission_branch_brief_anim) {
			free(Links[Cur_campaign_link].mission_branch_brief_anim);
		}

		if (Links[Cur_campaign_link].mission_branch_brief_sound) {
			free(Links[Cur_campaign_link].mission_branch_brief_sound);
		}

		char buffer[MISSION_DESC_LENGTH];
		
		deconvert_multiline_string(buffer, m_branch_desc, MISSION_DESC_LENGTH - 1);
		if (m_branch_desc && strlen(buffer)) {
			Links[Cur_campaign_link].mission_branch_txt = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_txt = NULL;
		}

		deconvert_multiline_string(buffer, m_branch_brief_anim, MISSION_DESC_LENGTH - 1);
		if (m_branch_brief_anim && strlen(buffer)) {
			Links[Cur_campaign_link].mission_branch_brief_anim = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_brief_anim = NULL;
		}

		deconvert_multiline_string(buffer, m_branch_brief_sound, MISSION_DESC_LENGTH - 1);
		if (m_branch_brief_sound && strlen(buffer)) {
			Links[Cur_campaign_link].mission_branch_brief_sound = strdup(buffer);
		} else {
			Links[Cur_campaign_link].mission_branch_brief_sound = NULL;
		}
	}

	// toggle to mission_loop setting
	Links[Cur_campaign_link].is_mission_loop = !Links[Cur_campaign_link].is_mission_loop;
	Links[Cur_campaign_link].is_mission_fork = false;

	// reset loop desc window (gray if inactive)
	update_loop_desc_window();

	// set root icon
	int bitmap1, bitmap2;
	if (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) {
		bitmap2 = BITMAP_GREEN_DOT;
		bitmap1 = BITMAP_BLUE_DOT;
	} else {
		bitmap1 = BITMAP_BLACK_DOT;
		bitmap2 = BITMAP_RED_DOT;
	}

	// Search for item and update bitmap
	HTREEITEM h = m_tree.GetRootItem();
	while (h) {
		if ((int) m_tree.GetItemData(h) == Links[Cur_campaign_link].node) {
			m_tree.SetItemImage(h, bitmap1, bitmap2);
			break;
		}

		h = m_tree.GetNextSiblingItem(h);
	}

	// set to redraw
	Campaign_tree_viewp->Invalidate();
}

void campaign_editor::OnBrowseLoopAni()
{
	int pushed_dir;
	
	UpdateData(TRUE);

	// switch directories
	pushed_dir = cfile_push_chdir(CF_TYPE_INTERFACE);
	CFileDialog dlg(TRUE, "ani", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
		"Ani Files (*.ani)|*.ani|Eff File (*.eff)|*.eff|APNG Files (*.png)|*.png|All Anims (*.ani, *.eff, *.png)|*.ani;*.eff;*.png");

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

	// move back to the proper directory
	if (!pushed_dir){
		cfile_pop_dir();	
	}
}

void campaign_editor::OnBrowseLoopSound()
{
	int pushed_dir;
	
	UpdateData(TRUE);

	// switch directories
	pushed_dir = cfile_push_chdir(CF_TYPE_VOICE_CMD_BRIEF);
	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_branch_brief_sound = dlg.GetFileName();
		UpdateData(FALSE);
	}

	// move back to the proper directory
	if (!pushed_dir){
		cfile_pop_dir();	
	}
}

void campaign_editor::OnChangeMainHall()
{
	CString str;
	UpdateData(TRUE);

	if (Cur_campaign_mission >= 0)
	{
		GetDlgItem(IDC_MAIN_HALL)->GetWindowText(str);
		Campaign.missions[Cur_campaign_mission].main_hall = SCP_string((LPCTSTR)(str));
	}
}

void campaign_editor::OnChangeSubstituteMainHall()
{
	CString str;
	UpdateData(TRUE);

	if (Cur_campaign_mission >= 0)
	{
		GetDlgItem(IDC_SUBSTITUTE_MAIN_HALL)->GetWindowText(str);
		Campaign.missions[Cur_campaign_mission].substitute_main_hall = SCP_string((LPCTSTR)(str));
	}
}

void campaign_editor::OnChangeDebriefingPersona()
{
	CString str;
	int persona;

	UpdateData(TRUE);

	if (Cur_campaign_mission >= 0)
	{
		GetDlgItem(IDC_DEBRIEFING_PERSONA)->GetWindowText(str);
		persona = atoi(str);

		if (persona < 0 || persona > 0xff)
			persona = 0;

		Campaign.missions[Cur_campaign_mission].debrief_persona_index = (ubyte) persona;
	}
}

void campaign_editor::OnCustomTechDB() 
{
	UpdateData(TRUE);

	if (m_custom_tech_db)
		Campaign.flags |= CF_CUSTOM_TECH_DATABASE;
	else
		Campaign.flags &= ~CF_CUSTOM_TECH_DATABASE;
}

void campaign_editor::OnCustomData()
{
	UpdateData(TRUE);

	CustomDataDlg dlg(&Campaign.custom_data, this);
	dlg.DoModal();

	UpdateData(FALSE);
}

CString campaign_editor::GetPathWithoutFile() const
{
	if (m_current_campaign_path.IsEmpty())
		return m_current_campaign_path;

	CString path = m_current_campaign_path;
	int pos = path.ReverseFind(DIR_SEPARATOR_CHAR);
	if (pos >= 0) {
		path.Truncate(pos + 1);
	}

	return path;
}

void campaign_editor::SetCurrentCampaignPath(const CString& path)
{
	m_current_campaign_path = path;
}

const CString &campaign_editor::GetCurrentCampaignPath() const
{
	return m_current_campaign_path;
}
