#include "ShipEditorDialog.h"

#include "ui_ShipEditorDialog.h"

#include "iff_defs/iff_defs.h"
#include "mission/missionmessage.h"
#include "missioneditor/common.h"
#include "mission/object.h"

#include <globalincs/linklist.h>
#include <ship/ship.h>
#include <ui/util/SignalBlockers.h>

#include <QCloseEvent>
#include <ui/dialogs/General/CheckBoxListDialog.h>

namespace fso::fred::dialogs {

ShipEditorDialog::ShipEditorDialog(FredView* parent, EditorViewport* viewport)
	: QDialog(parent), ui(new Ui::ShipEditorDialog()), _model(new ShipEditorDialogModel(this, viewport)),
	  _viewport(viewport)
{
	this->setFocus();
	ui->setupUi(this);

	ui->HelpTitle->setVisible(viewport->Show_sexp_help_ship_editor);
	ui->helpText->setVisible(viewport->Show_sexp_help_ship_editor);

	ui->shipNameEdit->setMaxLength(NAME_LENGTH - 1);
	ui->shipDisplayNameEdit->setMaxLength(NAME_LENGTH - 1);
	ui->altNameCombo->lineEdit()->setMaxLength(NAME_LENGTH - 1);
	ui->callsignCombo->lineEdit()->setMaxLength(CALLSIGN_LEN);
	ui->cargoTitleEdit->setMaxLength(NAME_LENGTH - 1);

	connect(_model.get(), &AbstractDialogModel::modelChanged, this, [this] { updateUi(false); });
	connect(viewport->editor, &Editor::currentObjectChanged, this, &ShipEditorDialog::update);
	connect(viewport->editor, &Editor::objectMarkingChanged, this, &ShipEditorDialog::update);

	// Column One

	connect(ui->cargoCombo->lineEdit(), (&QLineEdit::editingFinished), this, &ShipEditorDialog::cargoChanged);
	connect(ui->cargoTitleEdit, (&QLineEdit::editingFinished), this, &ShipEditorDialog::cargoTitleChanged);
	connect(ui->altNameCombo->lineEdit(), (&QLineEdit::textEdited), this, &ShipEditorDialog::altNameChanged);
	connect(ui->callsignCombo->lineEdit(), (&QLineEdit::textEdited), this, &ShipEditorDialog::callsignChanged);

	// ui->cargoCombo->installEventFilter(this);

	updateUi(true);

	// Resize the dialog to the minimum size
	resize(QDialog::sizeHint());
}

ShipEditorDialog::~ShipEditorDialog() = default;

int ShipEditorDialog::getShipClass() const
{
	return _model->getShipClass();
}

int ShipEditorDialog::getSingleShip() const
{
	return _model->getSingleShip();
}

bool ShipEditorDialog::getIfMultipleShips() const
{
	return _model->getIfMultipleShips();
}

void ShipEditorDialog::closeEvent(QCloseEvent* e)
{
	QDialog::closeEvent(e);
}

void ShipEditorDialog::hideEvent(QHideEvent* e)
{
	QDialog::hideEvent(e);
}
void ShipEditorDialog::showEvent(QShowEvent* e)
{
	_model->initializeData();
	QDialog::showEvent(e);
}

void ShipEditorDialog::on_miscButton_clicked()
{
	auto dialog = new dialogs::ShipFlagsDialog(this, _viewport);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

void ShipEditorDialog::on_initialStatusButton_clicked()
{
	auto dialog = new dialogs::ShipInitialStatusDialog(this, _viewport, getIfMultipleShips());
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

void ShipEditorDialog::on_initialOrdersButton_clicked()
{
	auto dialog =
		new dialogs::ShipGoalsDialog(this, _viewport, getIfMultipleShips(), Ships[getSingleShip()].objnum, -1);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

void ShipEditorDialog::on_tblInfoButton_clicked()
{
	auto dialog = new TableViewerDialog(this, _viewport, "Ship TBL Data",
	                                     "ships.tbl", "*-shp.tbm", Ship_info[getShipClass()].name);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

void ShipEditorDialog::update()
{
	if (this->isVisible()) {
		_model->initializeData();
		updateUi(true);
	}
}

void ShipEditorDialog::updateUi(bool overwrite)
{
	util::SignalBlockers blockers(this);
	enableDisable();
	updateColumnOne(overwrite);
	updateColumnTwo(overwrite);
	updateArrival(overwrite);
	updateDeparture(overwrite);
}
void ShipEditorDialog::updateColumnOne(bool overwrite)
{
	util::SignalBlockers blockers(this);
	int idx;
	if (overwrite) {
		ui->shipNameEdit->setText(_model->getShipName().c_str());
		ui->shipDisplayNameEdit->setText(_model->getShipDisplayName().c_str());
		idx = _model->getShipClass();
		ui->shipClassCombo->clear();
		for (size_t i = 0; i < Ship_info.size(); i++) {
			ui->shipClassCombo->addItem(Ship_info[i].name, QVariant(static_cast<int>(i)));
		}
		ui->shipClassCombo->setCurrentIndex(ui->shipClassCombo->findData(idx));

		auto ai = _model->getAIClass();
		ui->AIClassCombo->clear();
		for (auto j = 0; j < Num_ai_classes; j++) {
			ui->AIClassCombo->addItem(Ai_class_names[j], QVariant(j));
		}
		ui->AIClassCombo->setCurrentIndex(ui->AIClassCombo->findData(ai));
	}
	if (_model->getNumSelectedPlayers()) {
		if (_model->getTeam() != -1) {
			ui->teamCombo->setEnabled(true);
		} else {
			ui->teamCombo->setEnabled(false);
		}
		if (overwrite) {
			ui->teamCombo->clear();
			for (auto i = 0; i < MAX_TVT_TEAMS; i++) {
				ui->teamCombo->addItem(Iff_info[i].iff_name, QVariant(static_cast<int>(i)));
			}
		}
	} else {
		ui->teamCombo->setEnabled(_model->getUIEnable());
		if (overwrite) {
			idx = _model->getTeam();
			ui->teamCombo->clear();
			for (size_t i = 0; i < Iff_info.size(); i++) {
				ui->teamCombo->addItem(Iff_info[i].iff_name, QVariant(static_cast<int>(i)));
			}
			ui->teamCombo->setCurrentIndex(ui->teamCombo->findData(idx));
		}
	}
	if (overwrite) {
		auto cargo = _model->getCargo();
		ui->cargoCombo->clear();
		int j;
		for (j = 0; j < Num_cargo; j++) {
			ui->cargoCombo->addItem(Cargo_names[j]);
		}
		if (ui->cargoCombo->findText(QString(cargo.c_str()))) {
			ui->cargoCombo->setCurrentIndex(ui->cargoCombo->findText(QString(cargo.c_str())));
		} else {
			ui->cargoCombo->addItem(cargo.c_str());

			ui->cargoCombo->setCurrentIndex(ui->cargoCombo->findText(QString(cargo.c_str())));
		}
		ui->cargoTitleEdit->setText(_model->getCargoTitle().c_str());
	}
	if (_model->getNumSelectedObjects()) {
		if (_model->getIfMultipleShips()) {
			ui->altNameCombo->setEnabled(false);
		} else {
			auto altname = _model->getAltName();
			ui->altNameCombo->setEnabled(true);
			if (overwrite) {
				ui->altNameCombo->clear();
				ui->altNameCombo->addItem("<none>");
				for (auto j = 0; j < Mission_alt_type_count; j++) {
					ui->altNameCombo->addItem(Mission_alt_types[j]);
				}
				int altNameIdx = ui->altNameCombo->findText(QString(altname.c_str()));
				if (altNameIdx >= 0) {
					ui->altNameCombo->setCurrentIndex(altNameIdx);
				} else {
					ui->altNameCombo->setEditText("<none>");
				}
			}
		}
	}
	if (_model->getNumSelectedObjects()) {
		if (_model->getIfMultipleShips()) {
			ui->callsignCombo->setEnabled(false);
		} else {
			auto callsign = _model->getCallsign();
			ui->callsignCombo->setEnabled(true);
			if (overwrite) {
				ui->callsignCombo->clear();
				ui->callsignCombo->addItem("<none>");
				for (auto j = 0; j < Mission_callsign_count; j++) {
					ui->callsignCombo->addItem(Mission_callsigns[j], QVariant(Mission_callsigns[j]));
				}
				int callsignIdx = ui->callsignCombo->findText(QString(callsign.c_str()));
				if (callsignIdx >= 0) {
					ui->callsignCombo->setCurrentIndex(callsignIdx);
				} else {
					ui->callsignCombo->setEditText("<none>");
				}
			}
		}
	}

	// Layer combo — always rebuild so it reflects current mission layers
	ui->layerCombo->clear();
	for (const auto& name : _viewport->getLayerNames()) {
		ui->layerCombo->addItem(QString::fromStdString(name), QString::fromStdString(name));
	}
	ui->layerCombo->setCurrentIndex(ui->layerCombo->findData(QString::fromStdString(_model->getLayer())));
	ui->layerCombo->setEnabled(_model->getNumSelectedObjects() > 0);
}
void ShipEditorDialog::updateColumnTwo(bool overwrite)
{
	util::SignalBlockers blockers(this);
	if (overwrite) {
		ui->wing->setText(_model->getWing().c_str());

		auto idx = _model->getPersona();
		ui->personaCombo->setCurrentIndex(ui->personaCombo->findData(idx));

		ui->killScoreEdit->setValue(_model->getScore());

		ui->assistEdit->setValue(_model->getAssist());

		ui->playerShipCheckBox->setChecked(_model->getPlayer());
		ui->respawnSpinBox->setValue(_model->getRespawn());
		ui->hotkeyCombo->setCurrentIndex(_model->getHotkey());
	}
}
void ShipEditorDialog::updateArrival(bool overwrite)
{
	util::SignalBlockers blockers(this);
	if (overwrite) {
		auto idx = _model->getArrivalLocationIndex();
		int i;
		ui->arrivalLocationCombo->clear();
		for (i = 0; i < MAX_ARRIVAL_NAMES; i++) {
			ui->arrivalLocationCombo->addItem(Arrival_location_names[i], QVariant(i));
		}
		ui->arrivalLocationCombo->setCurrentIndex(ui->arrivalLocationCombo->findData(idx));
	}
	object* objp;
	int restrict_to_players;
	ui->arrivalTargetCombo->clear();
	if (_model->getArrivalLocation() != ArrivalLocation::FROM_DOCK_BAY) {
		// Add Special Arrivals
		for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++) {
			for (size_t j = 0; j < Iff_info.size(); j++) {
				char tmp[NAME_LENGTH + 15];
				stuff_special_arrival_anchor_name(tmp, static_cast<int>(j), restrict_to_players, false);

				ui->arrivalTargetCombo->addItem(tmp, QVariant(get_special_anchor(tmp).value()));
			}
		}
		// Add All Ships
		for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
			if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
				!(objp->flags[Object::Object_Flags::Marked])) {
				auto ship = get_ship_from_obj(objp);
				ui->arrivalTargetCombo->addItem(Ships[ship].ship_name, QVariant(ship));
			}
		}
	} else {
		for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
			if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
				!(objp->flags[Object::Object_Flags::Marked])) {
				polymodel* pm;

				// determine if this ship has a docking bay
				pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
				Assert(pm);
				if (pm->ship_bay && (pm->ship_bay->num_paths > 0)) {
					auto ship = get_ship_from_obj(objp);
					ui->arrivalTargetCombo->addItem(Ships[ship].ship_name, QVariant(ship));
				}
			}
		}
	}
	ui->arrivalTargetCombo->setCurrentIndex(ui->arrivalTargetCombo->findData(_model->getArrivalTarget()));
	if (overwrite) {
		ui->arrivalDistanceEdit->clear();
		ui->arrivalDistanceEdit->setValue(_model->getArrivalDistance());
		ui->arrivalDelaySpinBox->setValue(_model->getArrivalDelay());

		ui->updateArrivalCueCheckBox->setChecked(_model->getArrivalCue());

		ui->arrivalTree->initializeEditor(_viewport->editor, this);
		if (_model->getNumSelectedShips()) {

			if (_model->getIfMultipleShips()) {
				ui->arrivalTree->clear_tree("");
			}
			if (_model->getUseCue()) {
				ui->arrivalTree->load_tree(_model->getArrivalFormula());
			} else {
				ui->arrivalTree->clear_tree("");
			}
			if (!_model->getIfMultipleShips()) {
				int j = ui->arrivalTree->select_sexp_node;
				if (j != -1) {
					ui->arrivalTree->hilite_item(j);
				}
			}
		} else {
			ui->arrivalTree->clear_tree("");
		}

		ui->noArrivalWarpCheckBox->setCheckState(Qt::CheckState(_model->getNoArrivalWarp()));
	}
}
void ShipEditorDialog::updateDeparture(bool overwrite)
{
	util::SignalBlockers blockers(this);
	if (overwrite) {
		auto idx = _model->getDepartureLocationIndex();
		int i;
		ui->departureLocationCombo->clear();
		for (i = 0; i < MAX_DEPARTURE_NAMES; i++) {
			ui->departureLocationCombo->addItem(Departure_location_names[i], QVariant(i));
		}
		ui->departureLocationCombo->setCurrentIndex(ui->departureLocationCombo->findData(idx));
	}
	object* objp;

	ui->departureTargetCombo->clear();
	for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
		if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags[Object::Object_Flags::Marked])) {
			polymodel* pm;

			// determine if this ship has a docking bay
			pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
			Assert(pm);
			if (pm->ship_bay && (pm->ship_bay->num_paths > 0)) {
				auto ship = get_ship_from_obj(objp);
				ui->departureTargetCombo->addItem(Ships[ship].ship_name, QVariant(ship));
			}
		}
	}
	ui->departureTargetCombo->setCurrentIndex(ui->departureTargetCombo->findData(_model->getDepartureTarget()));
	if (overwrite) {
		ui->departureDelaySpinBox->setValue(_model->getDepartureDelay());

		ui->departureTree->initializeEditor(_viewport->editor, this);
		if (_model->getNumSelectedShips()) {

			if (_model->getIfMultipleShips()) {
				ui->departureTree->clear_tree("");
			}
			if (_model->getUseCue()) {
				ui->departureTree->load_tree(_model->getDepartureFormula(), "false");
			} else {
				ui->departureTree->clear_tree("");
			}
			if (!_model->getIfMultipleShips()) {
				auto i = ui->arrivalTree->select_sexp_node;
				if (i != -1) {
					i = ui->departureTree->select_sexp_node;
					ui->departureTree->hilite_item(i);
				}
			}
		} else {
			ui->departureTree->clear_tree("");
		}

		ui->noDepartureWarpCheckBox->setCheckState(Qt::CheckState(_model->getNoDepartureWarp()));

		ui->updateDepartureCueCheckBox->setChecked(_model->getDepartureCue());
	}
}
// Enables disbales controls based on what is selected
void ShipEditorDialog::enableDisable()
{
	if (!_model->getUseCue()) {
		ui->arrivalLocationCombo->setEnabled(false);
		ui->arrivalDelaySpinBox->setEnabled(false);
		ui->arrivalDistanceEdit->setEnabled(false);
		ui->arrivalTargetCombo->setEnabled(false);
		ui->arrivalTree->setEnabled(false);

		ui->departureLocationCombo->setEnabled(false);
		ui->departureDelaySpinBox->setEnabled(false);
		ui->departureTargetCombo->setEnabled(false);
		ui->departureTree->setEnabled(false);

		ui->noArrivalWarpCheckBox->setEnabled(false);
		ui->noDepartureWarpCheckBox->setEnabled(false);

		ui->restrictArrivalPathsButton->setEnabled(false);
		ui->restrictDeparturePathsButton->setEnabled(false);
	} else {
		ui->arrivalLocationCombo->setEnabled(_model->getUIEnable());
		ui->arrivalDistanceEdit->setEnabled(_model->getUIEnable() && _model->arrivalNeedsDistance());
		ui->arrivalTargetCombo->setEnabled(_model->getUIEnable() && _model->arrivalNeedsTarget());
		if (_model->getArrivalLocation() == ArrivalLocation::FROM_DOCK_BAY) {
			if (_model->getArrivalTarget() >= 0) {
				ui->restrictArrivalPathsButton->setEnabled(_model->getUIEnable());
			} else {
				ui->restrictArrivalPathsButton->setEnabled(false);
			}
			ui->customWarpinButton->setEnabled(false);
		} else {
			ui->restrictArrivalPathsButton->setEnabled(false);
			ui->customWarpinButton->setEnabled(_model->getUIEnable());
		}

		ui->departureLocationCombo->setEnabled(_model->getUIEnable());
		if (_model->getDepartureLocationIndex()) {
			ui->departureTargetCombo->setEnabled(_model->getUIEnable());
		} else {
			ui->departureTargetCombo->setEnabled(false);
		}
		if (_model->getDepartureLocation() == DepartureLocation::TO_DOCK_BAY) {
			if (_model->getDepartureTarget() >= 0) {
				ui->restrictDeparturePathsButton->setEnabled(_model->getUIEnable());
			} else {
				ui->restrictDeparturePathsButton->setEnabled(false);
			}
			ui->customWarpoutButton->setEnabled(false);
		} else {
			ui->restrictDeparturePathsButton->setEnabled(false);
			ui->customWarpoutButton->setEnabled(_model->getUIEnable());
		}

		ui->arrivalDelaySpinBox->setEnabled(_model->getUIEnable());
		ui->arrivalTree->setEnabled(_model->getUIEnable());
		ui->departureDelaySpinBox->setEnabled(_model->getUIEnable());
		ui->departureTree->setEnabled(_model->getUIEnable());
		ui->noArrivalWarpCheckBox->setEnabled(_model->getUIEnable());
		ui->noDepartureWarpCheckBox->setEnabled(_model->getUIEnable());
	}

	if (_model->getNumSelectedObjects()) {
		ui->shipNameEdit->setEnabled(!_model->getIfMultipleShips());
		ui->shipDisplayNameEdit->setEnabled(!_model->getIfMultipleShips());
		ui->shipClassCombo->setEnabled(true);
		ui->altNameCombo->setEnabled(true);
		ui->initialStatusButton->setEnabled(true);
		ui->weaponsButton->setEnabled(_model->getShipClass() >= 0);
		ui->miscButton->setEnabled(true);
		ui->textureReplacementButton->setEnabled(true);
		ui->altShipClassButton->setEnabled(true);
		ui->specialStatsButton->setEnabled(true);
	} else {
		ui->shipNameEdit->setEnabled(false);
		ui->shipDisplayNameEdit->setEnabled(false);
		ui->shipClassCombo->setEnabled(false);
		ui->altNameCombo->setEnabled(false);
		ui->initialStatusButton->setEnabled(false);
		ui->weaponsButton->setEnabled(false);
		ui->miscButton->setEnabled(false);
		ui->textureReplacementButton->setEnabled(false);
		ui->altShipClassButton->setEnabled(false);
		ui->specialStatsButton->setEnabled(false);
	}

	// disable textures unless exactly one ship/player is selected
	ui->textureReplacementButton->setEnabled(_model->getNumSelectedObjects() == 1);

	ui->AIClassCombo->setEnabled(_model->getUIEnable());
	ui->cargoCombo->setEnabled(_model->getUIEnable());
	ui->cargoTitleEdit->setEnabled(_model->getUIEnable());
	ui->hotkeyCombo->setEnabled(_model->getUIEnable());
	if ((_model->getShipClass() >= 0) && !(Ship_info[_model->getShipClass()].flags[Ship::Info_Flags::Cargo]) &&
		!(Ship_info[_model->getShipClass()].flags[Ship::Info_Flags::No_ship_type]))
		ui->initialOrdersButton->setEnabled(_model->getUIEnable());
	else if (_model->getIfMultipleShips())
		ui->initialOrdersButton->setEnabled(_model->getUIEnable());
	else
		ui->initialOrdersButton->setEnabled(false);

	// !pship_count used because if allowed to clear, we would have no player starts
	// mission_type 0 = multi, 1 = single
	if (!(The_mission.game_type & MISSION_TYPE_MULTI) || !_model->getNumUnmarkedPlayers() ||
		(_model->getNumUnmarkedPlayers() + _model->getNumSelectedObjects() > MAX_PLAYERS) ||
		(_model->getNumValidPlayers() < _model->getNumSelectedObjects()))
		ui->playerShipCheckBox->setEnabled(false);
	else
		ui->playerShipCheckBox->setEnabled(true);
	if (The_mission.game_type & MISSION_TYPE_MULTI) {
		ui->respawnSpinBox->setEnabled(_model->getUIEnable());
	} else {
		ui->respawnSpinBox->setEnabled(false);
	}

	// show the "set player" button only if single player
	if (!(The_mission.game_type & MISSION_TYPE_MULTI))
		ui->playerShipButton->setVisible(true);
	else
		ui->playerShipButton->setVisible(false);

	// enable the "set player" button only if single player, single edit, and ship is in player wing
	{
		int marked_ship = (_model->getIfPlayerShip() >= 0) ? _model->getIfPlayerShip() : _model->getSingleShip();
		const bool isPlayerWing = _model->wingIsPlayerWing(Ships[marked_ship].wingnum);
		if (!(The_mission.game_type & MISSION_TYPE_MULTI) && (_model->getNumSelectedObjects() > 0) &&
			(_model->getIfMultipleShips() != true) && (isPlayerWing == true))
			ui->playerShipButton->setEnabled(true);
		else
			ui->playerShipButton->setEnabled(false);
	}

	const bool noPlayerSelected = (_model->getNumSelectedPlayers() == 0);
	ui->deleteButton->setEnabled(_model->getUIEnable() && noPlayerSelected);
	ui->resetButton->setEnabled(_model->getUIEnable() && noPlayerSelected);
	ui->killScoreEdit->setEnabled(_model->getUIEnable());
	ui->assistEdit->setEnabled(_model->getUIEnable());

	ui->tblInfoButton->setEnabled(_model->getShipClass() >= 0);

	if (_model->getUseCue() > 1) {
		ui->updateArrivalCueCheckBox->setVisible(true);
		ui->updateDepartureCueCheckBox->setVisible(true);
	} else {
		ui->updateArrivalCueCheckBox->setVisible(false);
		ui->updateDepartureCueCheckBox->setVisible(false);
	}

	if (_model->getNumSelectedPlayers() > 0) {
		// player ships don't take orders from the player
		ui->playerOrdersButton->setEnabled(false);
	} else if (_model->getIfMultipleShips() || (_model->getNumSelectedObjects() > 1)) {
		// we will allow the ignore orders dialog to be multi edit if all selected
		// ships are the same type.  the ship_type variable holds the ship types
		// for all ships.  Determine how may bits set and enable/diable window
		// as appropriate
		if (_model->getShipOrders().find(std::numeric_limits<size_t>::max()) != _model->getShipOrders().end()) {
			ui->playerOrdersButton->setEnabled(false);
		} else {
			ui->playerOrdersButton->setEnabled(true);
		}
	} else {
		// always enabled when one ship is selected
		ui->playerOrdersButton->setEnabled(_model->getUIEnable());
	}

	// always enabled if >= 1 ship selected
	ui->personaCombo->setEnabled(_model->getUIEnable());

	if (_model->getIfMultipleShips()) {
		this->setWindowTitle("Edit Marked Ships");
	} else if (_model->getNumSelectedPlayers()) {
		this->setWindowTitle("Edit Player Ship");
	} else {
		this->setWindowTitle("Edit Ship");
	}
}
void ShipEditorDialog::cargoChanged()
{
	const QString entry = ui->cargoCombo->lineEdit()->text();
	if (!entry.isEmpty() && entry != _model->getCargo().c_str()) {
		const auto textBytes = entry.toUtf8();
		const SCP_string NewCargo = textBytes.toStdString();
		_model->setCargo(NewCargo);
	}
}
void ShipEditorDialog::cargoTitleChanged()
{
	const QString entry = ui->cargoTitleEdit->text();
	if (!entry.isEmpty() && entry != _model->getCargoTitle().c_str()) {
		const SCP_string NewCargoTitle = entry.toUtf8().constData();
		_model->setCargoTitle(NewCargoTitle);
	}
}
void ShipEditorDialog::altNameChanged()
{
	const QString entry = ui->altNameCombo->lineEdit()->text();
	if (!entry.isEmpty() && entry != _model->getAltName().c_str()) {
		const auto textBytes = entry.toUtf8();
		const SCP_string NewAltname = textBytes.toStdString();
		_model->setAltName(NewAltname);
	}
}
void ShipEditorDialog::callsignChanged()
{
	const QString entry = ui->callsignCombo->lineEdit()->text();
	if (!entry.isEmpty() && entry != _model->getCallsign().c_str()) {
		const auto textBytes = entry.toUtf8();
		const SCP_string newCallsign = textBytes.toStdString();
		_model->setCallsign(newCallsign);
	}
}

void ShipEditorDialog::on_textureReplacementButton_clicked()
{
	auto dialog = new dialogs::ShipTextureReplacementDialog(this, _viewport, getIfMultipleShips());
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

void ShipEditorDialog::on_playerShipButton_clicked()
{
	_model->setPlayer(true);
}
void ShipEditorDialog::on_altShipClassButton_clicked()
{
	auto dialog = new dialogs::ShipAltShipClass(this, _viewport);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}
void ShipEditorDialog::on_prevButton_clicked()
{
	_model->onPrevious();
}
void ShipEditorDialog::on_nextButton_clicked()
{
	_model->onNext();
}
void ShipEditorDialog::on_resetButton_clicked()
{
	_model->onShipReset();
}
void ShipEditorDialog::on_deleteButton_clicked()
{
	_model->onDeleteShip();
}
void ShipEditorDialog::on_weaponsButton_clicked()
{
	auto dialog = new dialogs::ShipWeaponsDialog(this, _viewport, getIfMultipleShips());
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}
void ShipEditorDialog::on_playerOrdersButton_clicked()
{
	QVector<std::pair<QString, int>> toWidget;
	for (const auto& p : _model->getPlayerOrders())
		toWidget.append({QString::fromUtf8(p.first.c_str()), p.second});

	dialogs::CheckBoxListDialog dlg(this);
	dlg.setCaption(tr("Player Orders Accepted"));
	dlg.setTristate(true);
	dlg.setOptions(toWidget);

	if (dlg.exec() == QDialog::Accepted) {
		SCP_vector<std::pair<SCP_string, int>> orders;
		for (const auto& [name, state] : dlg.getFlags())
			orders.emplace_back(name.toUtf8().constData(), state);
		_model->applyPlayerOrders(orders);
	}
}
void ShipEditorDialog::on_specialStatsButton_clicked()
{
	auto dialog = new dialogs::ShipSpecialStatsDialog(this, _viewport);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}
void ShipEditorDialog::on_hideCuesButton_clicked()
{
	const auto showHelp = _viewport->Show_sexp_help_ship_editor;

	_cues_hidden = !_cues_hidden;

	ui->arrivalGroupBox->setVisible(!_cues_hidden);
	ui->departureGroupBox->setVisible(!_cues_hidden);
	ui->HelpTitle->setVisible(!_cues_hidden && showHelp);
	ui->helpText->setVisible(!_cues_hidden && showHelp);
	ui->hideCuesButton->setText(_cues_hidden ? "Show Cues" : "Hide Cues");

	QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
	resize(sizeHint());
}
void ShipEditorDialog::on_restrictArrivalPathsButton_clicked()
{
	CheckBoxListDialog dlg(this);
	dlg.setCaption("Restrict Arrival Paths");
	auto arrivalPaths = _model->getArrivalPaths();

	QVector<std::pair<QString, bool>> checkbox_list;
	for (const auto& path : arrivalPaths) {
		checkbox_list.append({path.first.c_str(), path.second});
	}
	dlg.setOptions(checkbox_list); // TODO upgrade checkbox to accept and display item descriptions
	if (dlg.exec() == QDialog::Accepted) {
		auto returned_values = dlg.getCheckedStates();

		SCP_vector<std::pair<SCP_string, bool>> updatedPaths;

		for (int i = 0; i < checkbox_list.size(); ++i) {
			SCP_string name = checkbox_list[i].first.toUtf8().constData();
			updatedPaths.emplace_back(name, returned_values[i]);
		}

		_model->setArrivalPaths(updatedPaths);
	}
}
void ShipEditorDialog::on_customWarpinButton_clicked()
{
	auto dialog = new dialogs::ShipCustomWarpDialog(this, _viewport, false);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}
void ShipEditorDialog::on_restrictDeparturePathsButton_clicked()
{
	CheckBoxListDialog dlg(this);
	dlg.setCaption("Restrict Departure Paths");
	auto departurePaths = _model->getDeparturePaths();

	QVector<std::pair<QString, bool>> checkbox_list;
	for (const auto& path : departurePaths) {
		checkbox_list.append({path.first.c_str(), path.second});
	}
	dlg.setOptions(checkbox_list); // TODO upgrade checkbox to accept and display item descriptions
	if (dlg.exec() == QDialog::Accepted) {
		auto returned_values = dlg.getCheckedStates();

		SCP_vector<std::pair<SCP_string, bool>> updatedPaths;

		for (int i = 0; i < checkbox_list.size(); ++i) {
			SCP_string name = checkbox_list[i].first.toUtf8().constData();
			updatedPaths.emplace_back(name, returned_values[i]);
		}

		_model->setDeparturePaths(updatedPaths);
	}
}
void ShipEditorDialog::on_customWarpoutButton_clicked()
{
	auto dialog = new dialogs::ShipCustomWarpDialog(this, _viewport, true);
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->show();
}

/*---------------------------------------------------------
					WARNING
Do not try to optimise string entries; this convoluted method is necessary to avoid fatal errors caused by QT
-----------------------------------------------------------*/
void ShipEditorDialog::on_shipNameEdit_editingFinished()
{
	const QString entry = ui->shipNameEdit->text();
	if (!entry.isEmpty() && entry != _model->getShipName().c_str()) {
		const auto textBytes = entry.toUtf8();
		const std::string NewShipName = textBytes.toStdString();
		_model->setShipName(NewShipName);
	}

	// automatically determine or reset the display name
	_model->setShipDisplayName(Editor::get_display_name_for_text_box(_model->getShipName()));

	// sync the variable to the edit box
	ui->shipDisplayNameEdit->setText(_model->getShipDisplayName().c_str());
}
void ShipEditorDialog::on_shipDisplayNameEdit_editingFinished()
{
	const QString entry = ui->shipDisplayNameEdit->text();
	if (entry != _model->getShipDisplayName().c_str()) {
		const auto textBytes = entry.toUtf8();
		const std::string NewShipDisplayName = textBytes.toStdString();
		_model->setShipDisplayName(NewShipDisplayName);
	}
}
void ShipEditorDialog::on_shipClassCombo_currentIndexChanged(int index)
{
	auto shipClassIdx = ui->shipClassCombo->itemData(index).toInt();
	_model->setShipClass(shipClassIdx);
}
void ShipEditorDialog::on_AIClassCombo_currentIndexChanged(int index)
{
	auto aiClassIdx = ui->AIClassCombo->itemData(index).toInt();
	_model->setAIClass(aiClassIdx);
}
void ShipEditorDialog::on_teamCombo_currentIndexChanged(int index)
{
	auto teamIdx = ui->teamCombo->itemData(index).toInt();
	_model->setTeam(teamIdx);
}
void ShipEditorDialog::on_layerCombo_currentIndexChanged(int index)
{
	if (index < 0)
		return;
	_model->setLayer(ui->layerCombo->itemData(index).toString().toUtf8().constData());
}
void ShipEditorDialog::on_hotkeyCombo_currentIndexChanged(int index)
{
	_model->setHotkey(index);
}
void ShipEditorDialog::on_personaCombo_currentIndexChanged(int index)
{
	auto personaIdx = ui->personaCombo->itemData(index).toInt();
	_model->setPersona(personaIdx);
}
void ShipEditorDialog::on_killScoreEdit_valueChanged(int value)
{
	_model->setScore(value);
}
void ShipEditorDialog::on_assistEdit_valueChanged(int value)
{
	_model->setAssist(value);
}
void ShipEditorDialog::on_playerShipCheckBox_toggled(bool value)
{
	_model->setPlayer(value);
}
void ShipEditorDialog::on_respawnSpinBox_valueChanged(int value)
{
	_model->setRespawn(value);
}
void ShipEditorDialog::on_arrivalLocationCombo_currentIndexChanged(int index)
{
	auto arrivalLocationIdx = ui->arrivalLocationCombo->itemData(index).toInt();
	_model->setArrivalLocationIndex(arrivalLocationIdx);
}
void ShipEditorDialog::on_arrivalTargetCombo_currentIndexChanged(int index)
{
	auto arrivalLocationIdx = ui->arrivalTargetCombo->itemData(index).toInt();
	_model->setArrivalTarget(arrivalLocationIdx);
}
void ShipEditorDialog::on_arrivalDistanceEdit_valueChanged(int value)
{
	_model->setArrivalDistance(value);
}
void ShipEditorDialog::on_arrivalDelaySpinBox_valueChanged(int value)
{
	_model->setArrivalDelay(value);
}
void ShipEditorDialog::on_updateArrivalCueCheckBox_toggled(bool value)
{
	_model->setArrivalCue(value);
}
void ShipEditorDialog::on_noArrivalWarpCheckBox_stateChanged(int state)
{
	if (state == Qt::PartiallyChecked)
		return;
	_model->setNoArrivalWarp(state);
}
void ShipEditorDialog::on_arrivalTree_rootNodeFormulaChanged(int old, int node)
{
	_model->setArrivalFormula(old, node);
}
void ShipEditorDialog::on_arrivalTree_helpChanged(const QString& help)
{
	ui->helpText->setPlainText(help);
}
void ShipEditorDialog::on_arrivalTree_miniHelpChanged(const QString& help)
{
	ui->HelpTitle->setText(help);
}
void ShipEditorDialog::on_departureLocationCombo_currentIndexChanged(int index)
{
	auto depLocationIdx = ui->departureLocationCombo->itemData(index).toInt();
	_model->setDepartureLocationIndex(depLocationIdx);
}
void ShipEditorDialog::on_departureTargetCombo_currentIndexChanged(int index)
{
	auto depLocationIdx = ui->departureTargetCombo->itemData(index).toInt();
	_model->setDepartureTarget(depLocationIdx);
}
void ShipEditorDialog::on_departureDelaySpinBox_valueChanged(int value)
{
	_model->setDepartureDelay(value);
}
void ShipEditorDialog::on_updateDepartureCueCheckBox_toggled(bool value)
{
	_model->setDepartureCue(value);
}
void ShipEditorDialog::on_departureTree_rootNodeFormulaChanged(int old, int node)
{
	_model->setDepartureFormula(old, node);
}
void ShipEditorDialog::on_departureTree_helpChanged(const QString& help)
{
	ui->helpText->setPlainText(help);
}
void ShipEditorDialog::on_departureTree_miniHelpChanged(const QString& help)
{
	ui->HelpTitle->setText(help);
}
void ShipEditorDialog::on_noDepartureWarpCheckBox_stateChanged(int state)
{
	if (state == Qt::PartiallyChecked)
		return;
	_model->setNoDepartureWarp(state);
}
} // namespace fso::fred::dialogs