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

// ModifyVariableDlg.cpp : implementation file
//

#include "stdafx.h"
#include "FRED.h"
#include "ModifyVariableDlg.h"
#include "parse/sexp.h"

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

#define	NO_RESET_FOCUS	0
#define	RESET_FOCUS		1


/////////////////////////////////////////////////////////////////////////////
// CModifyVariableDlg dialog

CModifyVariableDlg::CModifyVariableDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CModifyVariableDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CModifyVariableDlg)
	m_default_value = _T("");
	m_cur_variable_name = _T("");
	//}}AFX_DATA_INIT
}


void CModifyVariableDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CModifyVariableDlg)
	DDX_Text(pDX, IDC_MODIFY_DEFAULT_VALUE, m_default_value);
	DDV_MaxChars(pDX, m_default_value, TOKEN_LENGTH - 1);
	DDX_CBString(pDX, IDC_MODIFY_VARIABLE_NAME, m_cur_variable_name);
	DDV_MaxChars(pDX, m_cur_variable_name, TOKEN_LENGTH - 1);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CModifyVariableDlg, CDialog)
	//{{AFX_MSG_MAP(CModifyVariableDlg)
	ON_BN_CLICKED(ID_DELETE_VARIABLE, OnDeleteVariable)
	ON_BN_CLICKED(IDC_TYPE_STRING, OnTypeString)
	ON_BN_CLICKED(IDC_TYPE_NUMBER, OnTypeNumber)
	ON_BN_CLICKED(IDC_TYPE_PLAYER_PERSISTENT, OnTypeMissionClose)
	ON_BN_CLICKED(IDC_TYPE_CAMPAIGN_PERSISTENT, OnTypeMissionProgress)
	ON_BN_CLICKED(IDC_TYPE_NETWORK_VARIABLE, OnTypeNetworkVariable)
	ON_BN_CLICKED(IDC_TYPE_ETERNAL, OnTypeEternal)
	ON_CBN_SELCHANGE(IDC_MODIFY_VARIABLE_NAME, OnSelchangeModifyVariableName)
	ON_CBN_EDITCHANGE(IDC_MODIFY_VARIABLE_NAME, OnEditchangeModifyVariableName)
	ON_EN_KILLFOCUS(IDC_MODIFY_DEFAULT_VALUE, OnKillfocusModifyDefaultValue)
	ON_CBN_DROPDOWN(IDC_MODIFY_VARIABLE_NAME, OnDropdownModifyVariableName)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CModifyVariableDlg message handlers

// Maybe delete variable
void CModifyVariableDlg::OnDeleteVariable() 
{
	CString temp_name;
	int rval;

	// Check for name change
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);
	cbox->GetWindowText(temp_name);

	// Can't delete. Name has been changed
	if ( stricmp(Sexp_variables[get_sexp_var_index()].variable_name, temp_name) ) {
		MessageBox("Can not delete variable.  Name has been changed.");
		return;
	}
	
	char message[128] = "Can not delete variable.";
	if (!IsChangeSafe(message))	{
		return;
	}

	// maybe delete variable
	rval = MessageBox("This will permanantly delete the variable.  Do you want to continue?", NULL, MB_OKCANCEL);

	if (rval == IDOK) {
		// delete variable and exit
		m_deleted = true;
		// next statement does UpdataData(TRUE);
		CDialog::OnOK();
	}
}

// Karajorma - Checks whether it is safe to delete or modify this string
bool CModifyVariableDlg::IsChangeSafe(char *message)
{
	// Can't as there are SEXPs using it. 
	int num_counts = m_p_sexp_tree->get_variable_count(Sexp_variables[get_sexp_var_index()].variable_name);
	if (num_counts > 0) {
		char buffer[256];
		sprintf(buffer, "%s Used in %d SEXP(s).", message, num_counts);
		MessageBox(buffer);
		return false;
	}
	
	// Can't as it is used in the team loadout
	num_counts = m_p_sexp_tree->get_loadout_variable_count(get_sexp_var_index());
	if (num_counts > 0) {
		char buffer[256];
		sprintf(buffer, "%s Used in %d location(s) in loadout.", message, num_counts);
		MessageBox(buffer);
		return false;
	}

	return true;
}

// Set type to string
void CModifyVariableDlg::OnTypeString() 
{
	// check if type actually modified
	if (m_type_number == true) {

		// Don't allow type change if in use.		
		char message[128] = "Can not modify variable type.";
		if (!IsChangeSafe(message))	{
			m_type_number = true;
			m_modified_type = false;
			set_variable_type();
			return;
		}

		// keep track if type is really changed
		if (m_modified_type == true) {
			m_modified_type = false;
		} else {
			m_modified_type = true;
		}
	}
	m_type_number = false;
	set_variable_type();
}

// Set type to number
void CModifyVariableDlg::OnTypeNumber() 
{
	// check if type actually modified
	if (m_type_number == false) {
		
		// Don't allow type change if in use.			
		char message[128] = "Can not modify variable type.";
		if (!IsChangeSafe(message))	{
			m_type_number = false;
			m_modified_type = false;
			set_variable_type();
			return;
		}

		// keep track if type is really changed
		if (m_modified_type == true) {
			m_modified_type = false;
		} else {
			m_modified_type = true;
		}
	}
	m_type_number = true;
	set_variable_type();
}

// set player persistence
void CModifyVariableDlg::OnTypeMissionClose()
{
	m_type_on_mission_close = ((CButton *) GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT))->GetCheck() ? true : false;

	// keep track if type is really changed (ugh - just force it to be always changed)
	m_modified_persistence = true;

	if (m_type_on_mission_close)
		m_type_on_mission_progress = false;

	// if the variable isn't persistent, it can't be eternal
	if (!m_type_on_mission_close && !m_type_on_mission_progress) {
		m_type_eternal = false;
	}

	set_variable_type();
}

// set campaign persistence
void CModifyVariableDlg::OnTypeMissionProgress()
{
	m_type_on_mission_progress = ((CButton *) GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT))->GetCheck() ? true : false;

	// keep track if type is really changed (ugh - just force it to be always changed)
	m_modified_persistence = true;

	if (m_type_on_mission_progress)
		m_type_on_mission_close = false;
	
	// if the variable isn't persistent, it can't be eternal
	if (!m_type_on_mission_close && !m_type_on_mission_progress) {
		m_type_eternal = false;
	}

	set_variable_type();
}

// set eternal persistence
void CModifyVariableDlg::OnTypeEternal()
{
	m_type_eternal = ((CButton *)GetDlgItem(IDC_TYPE_ETERNAL))->GetCheck() ? true : false;

	m_modified_persistence = true;

	// if the variable isn't persistent, it can't be eternal
	if (!m_type_on_mission_close && !m_type_on_mission_progress) {
		m_type_eternal = false;
		MessageBox("Eternal variables must have a different persistence type set first!");
	}

	set_variable_type();
}

void CModifyVariableDlg::OnTypeNetworkVariable()
{
	m_type_network_variable = ((CButton *) GetDlgItem(IDC_TYPE_NETWORK_VARIABLE))->GetCheck() ? true : false;

	m_modified_type = true; 

	set_variable_type();
}

void CModifyVariableDlg::OnSelchangeModifyVariableName()
{
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);

	// get index of current selection
	int index = cbox->GetCurSel();

	// check an item was actually selected, and not outside the box
	if (index == CB_ERR) {
		return;
	}

	// check if another has been modified
	if (m_modified_type || m_modified_persistence || m_modified_name || m_modified_value) {
		
		// Don't send message if changing to self
		if (index != m_combo_last_modified_index) {
			MessageBox("Can only modify one variable.");
		}

		//reset focus to current
		cbox->SetCurSel(m_combo_last_modified_index);
		return;
	}

	m_combo_last_modified_index = index;

	// Get index into sexp_variables
	int sexp_variable_index = get_sexp_var_index();
	
	// Set new type for selection
	m_type_number = ((Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER) != 0) ? true : false;
	m_type_on_mission_progress = ((Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS) != 0) ? true : false;
	m_type_on_mission_close = ((Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SAVE_ON_MISSION_CLOSE) != 0) ? true : false;
	m_type_network_variable = ((Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NETWORK) != 0) ? true : false;
	m_type_eternal = ((Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) != 0) ? true : false;
	set_variable_type();

	// Set new default value for selection
	if (sexp_variable_index > -1) {
		CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE);
		edit->SetWindowText(Sexp_variables[sexp_variable_index].text);
	}
}

// Check if variable name has changed from Sexp_variables[].varaible name
void CModifyVariableDlg::OnEditchangeModifyVariableName() 
{
	// Do string compare to check for change
	CString temp_name;

	// Get current variable name
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);
	cbox->GetWindowText(temp_name);

	// Check if variable name is modified
	if ( strcmp(Sexp_variables[get_sexp_var_index()].variable_name, temp_name) ) {
		m_modified_name = true;
	} else {
		m_modified_name = false;
	}
}

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

	int i, box_index;
	// Init combo box and translation table from combo box to sexp_varaibles
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);
	cbox->ResetContent();

	for (i=0; i<MAX_SEXP_VARIABLES; i++) {
		m_translate_combo_to_sexp[i] = -1;
	}

	// initialize list -- Box is set to *not* sort
	for (i=0; i<MAX_SEXP_VARIABLES; i++) {
		if ((Sexp_variables[i].type & SEXP_VARIABLE_SET) && !(Sexp_variables[i].type & SEXP_VARIABLE_BLOCK)){
			box_index = cbox->AddString(Sexp_variables[i].variable_name);
			
			// check no error
			if ( !((box_index == CB_ERR) || (box_index == CB_ERRSPACE)) ) {
				m_translate_combo_to_sexp[box_index] = i;
			}
		}
	}
	
	// Exit gracefully if nothing added to combo box
	if (cbox->GetCount() == 0) {
		Int3();	// this should not happen
		OnCancel();
	}

	int last_modified = 0;
	// Set current variable
	if (m_start_index > -1) {
		for (i=0; i<MAX_SEXP_VARIABLES; i++) {
			if (m_translate_combo_to_sexp[i] == m_start_index) {
				last_modified = i;
				break;
			}
		}
	}

	m_combo_last_modified_index = last_modified;
	cbox->SetCurSel(last_modified);

	// Set the default value
	if (m_translate_combo_to_sexp[last_modified] > -1) {
		CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE);
		edit->SetWindowText(Sexp_variables[m_translate_combo_to_sexp[last_modified]].text);
	}

	// Set old variable name
	m_old_var_name = Sexp_variables[m_translate_combo_to_sexp[last_modified]].variable_name;
	
	// Set type
	m_type_number = ((Sexp_variables[last_modified].type & SEXP_VARIABLE_NUMBER) != 0) ? true : false;
	m_type_on_mission_progress = ((Sexp_variables[last_modified].type & SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS) != 0) ? true : false;
	m_type_on_mission_close = ((Sexp_variables[last_modified].type & SEXP_VARIABLE_SAVE_ON_MISSION_CLOSE) != 0) ? true : false;
	m_type_network_variable = ((Sexp_variables[last_modified].type & SEXP_VARIABLE_NETWORK) != 0) ? true : false;
	m_type_eternal = ((Sexp_variables[last_modified].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) != 0) ? true : false;
	set_variable_type();

	// keep track of changes
	m_modified_name  = false;
	m_modified_value = false;
	m_modified_type  = false;
	m_modified_persistence = false;
	m_deleted		  = false;
	m_do_modify		  = false;

	m_data_validated		= false;
	m_var_name_validated = false;
	
	//create tool tip controls
	m_ProgressToolTip = new CToolTipCtrl();
	m_ProgressToolTip->Create(this);
	m_CloseToolTip = new CToolTipCtrl();
	m_CloseToolTip->Create(this);
	m_EternalToolTip = new CToolTipCtrl();
	m_EternalToolTip->Create(this);

	CWnd* pWnd = GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT);
	m_ProgressToolTip->AddTool(pWnd, "This type of variable will save when the player clicks Accept to go to the next mission");
	m_ProgressToolTip->Activate(TRUE);

	pWnd = GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT);
	m_CloseToolTip->AddTool(pWnd, "This type of variable will save when the player leaves the mission");
	m_CloseToolTip->Activate(TRUE);

	pWnd = GetDlgItem(IDC_TYPE_ETERNAL);
	m_EternalToolTip->AddTool(pWnd, "This type of variable is saved to the player file. So it can be referred to by other campaigns");
	m_EternalToolTip->Activate(TRUE);
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

BOOL CModifyVariableDlg::PreTranslateMessage(MSG* pMsg) {

	m_ProgressToolTip->RelayEvent(pMsg);
	m_CloseToolTip->RelayEvent(pMsg);
	m_EternalToolTip->RelayEvent(pMsg);

	return CDialog::PreTranslateMessage(pMsg);
}

void CModifyVariableDlg::set_variable_type()
{
	// get buttons
	CButton *button_string = (CButton *) GetDlgItem(IDC_TYPE_STRING);
	CButton *button_number = (CButton *) GetDlgItem(IDC_TYPE_NUMBER);

	// assign state
	button_number->SetCheck( m_type_number);
	button_string->SetCheck(!m_type_number);

	((CButton *) GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT))->SetCheck(m_type_on_mission_progress);
	((CButton *) GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT))->SetCheck(m_type_on_mission_close);
	((CButton *) GetDlgItem(IDC_TYPE_NETWORK_VARIABLE))->SetCheck(m_type_network_variable);
	((CButton *) GetDlgItem(IDC_TYPE_ETERNAL))->SetCheck(m_type_eternal);
}

void CModifyVariableDlg::OnOK() 
{
	CString temp_data;

	// Validate data
	CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE);
	edit->GetWindowText(temp_data);

	// validate data
	validate_data(temp_data, RESET_FOCUS);
	if (m_data_validated) {
		// Don't get OnKillfocusModifyDefaultValue when ok
		if (!m_modified_value) {
			if ( strcmp(Sexp_variables[get_sexp_var_index()].text, temp_data) ) {
				m_modified_value = true;
			}
		}

		// validate variable name
		validate_var_name(RESET_FOCUS);
		if (m_var_name_validated) {

			// maybe set m_do_modify -- this is needed. compare with OnCancel()
			if (m_modified_name || m_modified_persistence || m_modified_value || m_modified_type) {
				m_do_modify = true;
			}
			CDialog::OnOK();
		}
	}
}

void CModifyVariableDlg::OnKillfocusModifyDefaultValue() 
{
	// Do string compare to check for change
	CString temp_data;

	CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE);
	edit->GetWindowText(temp_data);

	if ( strcmp(Sexp_variables[get_sexp_var_index()].text, temp_data) ) {
		m_modified_value = true;
	} else {
		m_modified_value = false;
	}
}

// validate data
// check (1)  zero length (2) invalid chars (3) value if numberf
void CModifyVariableDlg::validate_data(CString &temp_data, int set_focus)
{
	// display invalid data message
	bool message = false;
	char message_text[256];

	// check length > 0
	auto length  = strlen(temp_data);
	if (length == 0) {
		strcpy_s(message_text, "Invalid  Default Value");
		message = true;

	} else if (m_type_number) {
		// check if string and str(atoi(stri)) are same
		int temp_num = atoi(temp_data);
		char buf[TOKEN_LENGTH];
		sprintf(buf, "%d", temp_num);

		if ( stricmp(buf, temp_data) ) {
			message = true;
			strcpy_s(message_text, "Invalid  Default Value");
		} else {
			message = false;
		}
	}

	// check for invalid characters
	auto rval = strcspn(temp_data, "@()");
	if (rval != length) {
		message = true;
		sprintf(message_text, "Invalid char '%c' in Default Value", temp_data[(int)rval]);
	}

	// display message
	if ( message ) {
		m_data_validated = false;
		MessageBox(message_text);

		// reset focus
		if (set_focus == RESET_FOCUS) {
			CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE);
			edit->SetFocus();
			edit->SetSel(0, -1);
		}
	}

	// string always ok, numbers if no message
	m_data_validated = !message;
}

// validate variable name
// check (1) zero length (2) invalid chars (3) already in use
void CModifyVariableDlg::validate_var_name(int set_focus)
{
	CString temp_name;
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);
	cbox->GetWindowText(temp_name);

	int cur_sel = cbox->GetCurSel();
	m_old_var_name = Sexp_variables[m_translate_combo_to_sexp[cur_sel]].variable_name;

	// display invalid data message
	bool message = false;
	char message_text[256];

	// check length > 0
	auto length  = strlen(temp_name);
	if (length == 0) {
		strcpy_s(message_text, "Invalid Variable Name");
		message = true;
	} else {

		// check for invalid characters
		auto rval = strcspn(temp_name, "@()");
		if (rval != length) {
			message = true;
			sprintf(message_text, "Invalid char '%c' in Variable Name", temp_name[(int)rval]);
		} else {
			int index = get_index_sexp_variable_name(temp_name);

			// if not a new name and not start name
			if ( (index != -1) && (index != m_translate_combo_to_sexp[m_combo_last_modified_index]) ) {
				message = true;
				strcpy_s(message_text, "Variable Name already in use");
			}
		}
	}

	// display message
	if ( message ) {
		MessageBox(message_text);

		// reset focus
		if (set_focus == RESET_FOCUS) {
			cbox->SetFocus();
		}
	}

	// set var_name_validated
	m_var_name_validated = !message;
}


int CModifyVariableDlg::get_sexp_var_index()
{
	int index = m_translate_combo_to_sexp[m_combo_last_modified_index];
	Assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) );

	return index;
}

// Reset text in drop down list
void CModifyVariableDlg::OnDropdownModifyVariableName() 
{
	CString temp_name;

	// Get current variable name
	CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME);
	cbox->GetWindowText(temp_name);

	// Reset combo box text
	int rval;
	rval = cbox->InsertString(m_combo_last_modified_index, temp_name);
	if ( (rval == CB_ERR) || (rval == CB_ERRSPACE) ) {
		AfxMessageBox("An internal error has occured.");
		OnCancel();
	}
	cbox->DeleteString(m_combo_last_modified_index+1);

	cbox->SetCurSel(m_combo_last_modified_index);
}
