#include "ShipInitialStatusDialog.h"

#include "globalincs/alphacolors.h"
#include "globalincs/globals.h"

#include "ui_ShipInitialStatus.h"

#include "mission/util.h"
#include "object/objectdock.h"

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

#include <QCloseEvent>

namespace fso::fred::dialogs {
ShipInitialStatusDialog::ShipInitialStatusDialog(QDialog* parent, EditorViewport* viewport, bool editMultiple)
	: QDialog(parent), ui(new Ui::ShipInitialStatusDialog()),
	  _model(new ShipInitialStatusDialogModel(this, viewport, editMultiple)), _viewport(viewport)
{
	ui->setupUi(this);

	ui->cargoEdit->setMaxLength(NAME_LENGTH - 1);
	ui->cargoTitleEdit->setMaxLength(NAME_LENGTH - 1);

	connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipInitialStatusDialog::updateUi);

	updateUi();

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

ShipInitialStatusDialog::~ShipInitialStatusDialog() = default;

void ShipInitialStatusDialog::accept()
{
	// If apply() returns true, close the dialog
	if (_model->apply()) {
		QDialog::accept();
	}
	// else: validation failed, don't close
}
void ShipInitialStatusDialog::reject()
{
	// Asks the user if they want to save changes, if any
	// If they do, it runs _model->apply() and returns the success value
	// If they don't, it runs _model->reject() and returns true
	if (rejectOrCloseHandler(this, _model.get(), _viewport)) {
		QDialog::reject(); // actually close
	}
	// else: do nothing, don't close
}
void ShipInitialStatusDialog::closeEvent(QCloseEvent* e)
{
	reject();
	e->ignore(); // Don't let the base class close the window
}
void ShipInitialStatusDialog::on_velocitySpinBox_valueChanged(int value)
{
	_model->setVelocity(value);
}
void ShipInitialStatusDialog::on_dockpointList_currentItemChanged(QListWidgetItem* current)
{
	if (!current)
		return;
	_curDockerPoint = current->data(Qt::UserRole).toInt();
	updateUi();
}
void ShipInitialStatusDialog::on_dockeeComboBox_currentIndexChanged(int index)
{
	auto dockeeData = ui->dockeeComboBox->itemData(index).toInt();
	_curDockee = dockeeData;
	_model->setDockee(_curDockerPoint, dockeeData);

	if (_curDockee >= 0 && ui->dockeePointComboBox->count() > 0) {
		_curDockeePoint = ui->dockeePointComboBox->itemData(0).toInt();
		_model->setDockeePoint(_curDockerPoint, _curDockeePoint);
	}
}
void ShipInitialStatusDialog::on_dockeePointComboBox_currentIndexChanged(int index)
{
	auto dockeeData = ui->dockeePointComboBox->itemData(index).toInt();
	_curDockeePoint = dockeeData;
	_model->setDockeePoint(_curDockerPoint, dockeeData);
}
void ShipInitialStatusDialog::on_hullSpinBox_valueChanged(int value)
{
	_model->setHull(value);
}
void ShipInitialStatusDialog::on_hasShieldCheckBox_stateChanged(int state)
{
	_model->setHasShield(state);
}
void ShipInitialStatusDialog::on_shieldHullSpinBox_valueChanged(int value)
{
	_model->setShieldHull(value);
}
void ShipInitialStatusDialog::on_forceShieldsCheckBox_stateChanged(int state)
{
	_model->setForceShield(state);
}
void ShipInitialStatusDialog::on_shipLockCheckBox_stateChanged(int state)
{
	_model->setShipLocked(state);
}
void ShipInitialStatusDialog::on_weaponLockCheckBox_stateChanged(int state)
{
	_model->setWeaponLocked(state);
}
void ShipInitialStatusDialog::on_primaryLockCheckBox_stateChanged(int state)
{
	_model->setPrimariesDisabled(state);
}
void ShipInitialStatusDialog::on_secondaryLockCheckBox_stateChanged(int state)
{
	_model->setSecondariesDisabled(state);
}
void ShipInitialStatusDialog::on_turretLockCheckBox_stateChanged(int state)
{
	_model->setTurretsDisabled(state);
}
void ShipInitialStatusDialog::on_afterburnerLockCheckBox_stateChanged(int state)
{
	_model->setAfterburnerDisabled(state);
}
void ShipInitialStatusDialog::on_subsystemList_currentRowChanged(int index)
{
	_model->changeSubsys(index);
}
void ShipInitialStatusDialog::on_subIntegritySpinBox_valueChanged(int value)
{
	_model->setDamage(value);
}
void ShipInitialStatusDialog::on_cargoEdit_editingFinished()
{
	SCP_string cargo = ui->cargoEdit->text().toUtf8().constData();
	_model->setCargo(cargo);
}
void ShipInitialStatusDialog::on_cargoTitleEdit_editingFinished()
{
	SCP_string title = ui->cargoTitleEdit->text().toUtf8().constData();
	_model->setCargoTitle(title);
}
void ShipInitialStatusDialog::on_colourComboBox_currentIndexChanged(int index)
{
	SCP_string color = ui->colourComboBox->itemText(index).toUtf8().constData();
	_model->setColor(color);
}
void ShipInitialStatusDialog::on_okPushButton_clicked()
{
	accept();
}
void ShipInitialStatusDialog::on_cancelPushButton_clicked() {
	reject();
}
void ShipInitialStatusDialog::on_guardianSpinBox_valueChanged(int value)
{
	_model->setGuardian(value);
}
void ShipInitialStatusDialog::on_moveShipsCheckBox_toggled(bool value)
{
	_model->setMoveShipsWhenUndocking(value);
}
void ShipInitialStatusDialog::updateUi()
{
	util::SignalBlockers blockers(this);
	auto value = _model->getVelocity();
	if (value != BLANKFIELD) {
		ui->velocitySpinBox->setValue(value);
	} else {
		ui->velocitySpinBox->setSpecialValueText("-");
	}
	value = _model->getHull();
	if (value != BLANKFIELD) {
		ui->hullSpinBox->setValue(value);
	} else {
		ui->hullSpinBox->setSpecialValueText("-");
	}
	value = _model->getHasShield();
	ui->hasShieldCheckBox->setCheckState(Qt::CheckState(value));
	if (_model->getHasShield()) {
		ui->shieldHullSpinBox->setEnabled(true);
	} else {
		ui->shieldHullSpinBox->setEnabled(false);
	}
	value = _model->getShieldHull();
	if (value != BLANKFIELD) {
		ui->shieldHullSpinBox->setValue(value);
	} else {
		ui->shieldHullSpinBox->setSpecialValueText("-");
	}
	ui->guardianSpinBox->setValue(_model->getGuardian());
	updateFlags();
	updateDocks();
	updateDockee();
	ui->moveShipsCheckBox->setChecked(_model->getMoveShipsWhenUndocking());
	updateSubsystems();
	ui->colourComboBox->clear();
	if (_model->getUseTeamcolours()) {
		int i = 0;
		ui->colourComboBox->setEnabled(true);
		ui->colourComboBox->addItem("none", i);
		++i;
		for (auto& Team_Name : Team_Names) {
			ui->colourComboBox->addItem(Team_Name.c_str(), i);
			i++;
		}
		auto currentText = _model->getColor();
		ui->colourComboBox->setCurrentIndex(ui->colourComboBox->findText(currentText.c_str()));
	}
}
void ShipInitialStatusDialog::updateFlags()
{
	auto value = _model->getForceShield();
	ui->forceShieldsCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getShipLocked();
	ui->shipLockCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getWeaponLocked();
	ui->weaponLockCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getPrimariesDisabled();
	ui->primaryLockCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getSecondariesDisabled();
	ui->secondaryLockCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getTurretsDisabled();
	ui->turretLockCheckBox->setCheckState(Qt::CheckState(value));
	value = _model->getAfterburnerDisabled();
	ui->afterburnerLockCheckBox->setCheckState(Qt::CheckState(value));
}
void ShipInitialStatusDialog::updateDocks()
{
	int row = ui->dockpointList->currentRow();
	ui->dockpointList->clear();
	if (!_model->getIfMultipleShips()) {
		for (int dockpoint = 0; dockpoint < _model->getNumDockPoints(); dockpoint++) {
			auto newItem = new QListWidgetItem;
			newItem->setText(
				model_get_dock_name(Ship_info[Ships[_model->getShip()].ship_info_index].model_num, dockpoint));
			newItem->setData(Qt::UserRole, dockpoint);
			ui->dockpointList->addItem(newItem);
		}
	}
	if (row >= 0 && row < ui->dockpointList->count())
		ui->dockpointList->setCurrentRow(row);
	if (_curDockerPoint < 0) {
		// clear the dropdowns
		listDockees(-1);
		listDockeePoints(-1);
	} else {
		// populate with all possible dockees
		listDockees(
			model_get_dock_index_type(Ship_info[Ships[_model->getShip()].ship_info_index].model_num, _curDockerPoint));

		// see if there's a dockee here
		if (_model->getDockpointArray()[_curDockerPoint].dockee_shipnum >= 0) {
			// select the dockee
			ui->dockeeComboBox->setCurrentIndex(ui->dockeeComboBox->findText(
				Ships[_model->getDockpointArray()[_curDockerPoint].dockee_shipnum].ship_name));
		} else {
			ui->dockeeComboBox->setCurrentIndex(0);
		}
	}
}
void ShipInitialStatusDialog::updateDockee()
{
	if (_curDockee < 0) {
		// clear the dropdown
		listDockeePoints(-1);
	} else {
		// populate with dockee points
		listDockeePoints(_curDockee);
		if (_model->getDockpointArray()[_curDockerPoint].dockee_point < 0) {
			// select the dockpoint
			ui->dockeePointComboBox->setCurrentIndex(0);
		}

		// see if there's a dockpoint here
		if (_model->getDockpointArray()[_curDockerPoint].dockee_point >= 0) {
			// select the dockpoint
			ui->dockeePointComboBox->setCurrentIndex(ui->dockeePointComboBox->findText(
				model_get_dock_name(Ship_info[Ships[_curDockee].ship_info_index].model_num,
					_model->getDockpointArray()[_curDockerPoint].dockee_point)));
		}
	}
}
void ShipInitialStatusDialog::listDockees(int dock_types)
{ // enable/disable dropdown
	ui->dockeeComboBox->setEnabled((dock_types >= 0));

	// clear the existing dockees
	ui->dockeeComboBox->clear();
	// that might be all we need to do
	if (dock_types < 0)
		return;
	// populate with potential dockees

	// add "nothing"
	ui->dockeeComboBox->addItem("Nothing", QVariant(int(-1)));

	// add ships
	for (object* 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)) {
			int ship = get_ship_from_obj(objp);

			// mustn't be the same ship
			if (ship == _model->getShip()) {
				continue;
			}

			// mustn't also be docked elsewhere
			bool docked_elsewhere = false;
			for (int i = 0; i < _model->getNumDockPoints(); i++) {
				// don't erroneously check the same point
				if (i == _curDockerPoint) {
					continue;
				}

				// see if this ship is also on a different dockpoint
				if (_model->getDockpointArray()[i].dockee_shipnum == ship) {
					docked_elsewhere = true;
					break;
				}
			}

			if (docked_elsewhere) {
				continue;
			}

			// docking must be valid
			if (!ship_docking_valid(_model->getShip(), ship) && !ship_docking_valid(ship, _model->getShip())) {
				continue;
			}

			// dock types must match
			if (!(model_get_dock_types(Ship_info[Ships[ship].ship_info_index].model_num) & dock_types)) {
				continue;
			}

			// add to list
			ui->dockeeComboBox->addItem(Ships[ship].ship_name, QVariant(int(ship)));
		}
	}
}
void ShipInitialStatusDialog::listDockeePoints(int shipnum)
{
	// enable/disable dropdown
	ui->dockeePointComboBox->setEnabled((shipnum >= 0));

	// clear the existing dockee points
	ui->dockeePointComboBox->clear();

	// that might be all we need to do
	if (shipnum < 0) {
		return;
	}

	ship* shipp = &Ships[_model->getShip()];
	ship* other_shipp = &Ships[shipnum];

	// get the required dock type(s)
	int dock_type = model_get_dock_index_type(Ship_info[shipp->ship_info_index].model_num, _curDockerPoint);

	// populate with the right kind of dockee points
	for (int i = 0; i < model_get_num_dock_points(Ship_info[other_shipp->ship_info_index].model_num); i++) {
		// make sure this dockpoint is not occupied by someone else
		object* docked_objp = dock_find_object_at_dockpoint(&Objects[other_shipp->objnum], i);
		if ((docked_objp != nullptr) && (docked_objp != &Objects[shipp->objnum]))
			continue;

		// make sure its type matches
		if (!(model_get_dock_index_type(Ship_info[other_shipp->ship_info_index].model_num, i) & dock_type))
			continue;

		// add to list
		ui->dockeePointComboBox->addItem(model_get_dock_name(Ship_info[other_shipp->ship_info_index].model_num, i),
			QVariant(int(i)));
	}
}
void ShipInitialStatusDialog::updateSubsystems()
{
	ship_subsys* ptr;
	auto index = ui->subsystemList->currentIndex();
	ui->subsystemList->clear();

	bool multiEdit = _model->getIfMultipleShips();
	bool useNewScanning = _model->getUseNewScanningBehavior();
	bool toggleSet = _model->getToggleSubsystemScanning();
	bool scannable = _model->getShipHasScannableSubsystems();

	if (!multiEdit) {
		ui->subsystemList->setEnabled(true);
		ui->subIntegritySpinBox->setEnabled(true);
		for (ptr = GET_FIRST(&Ships[_model->getShip()].subsys_list);
			ptr != END_OF_LIST(&Ships[_model->getShip()].subsys_list);
			ptr = GET_NEXT(ptr)) {
			auto newItem = new QListWidgetItem;
			newItem->setText(ptr->system_info->subobj_name);
			ui->subsystemList->addItem(newItem);
		}
		ui->subsystemList->setCurrentIndex(index);
	} else {
		ui->subsystemList->setEnabled(false);
		ui->subIntegritySpinBox->setEnabled(false);
	}

	// Determine whether subsystem cargo fields should be shown.
	// Classic mode: ship must have scannable subsystems (huge ship, or non-huge with Toggle_subsystem_scanning).
	// Unified mode: any ship with Toggle_subsystem_scanning can use subsystem cargo.
	bool showCargo;
	if (useNewScanning) {
		showCargo = !multiEdit && toggleSet;
	} else {
		showCargo = !multiEdit && scannable;
	}

	ui->subsysCargoHelpLabel->setVisible(!showCargo && !multiEdit);
	ui->cargoLabel->setVisible(showCargo);
	ui->cargoEdit->setVisible(showCargo);
	ui->cargoEdit->setEnabled(showCargo);
	ui->cargoTitleLabel->setVisible(showCargo);
	ui->cargoTitleEdit->setVisible(showCargo);
	ui->cargoTitleEdit->setEnabled(showCargo);

	auto value = _model->getDamage();
	ui->subIntegritySpinBox->setValue(value);
	auto cargovalue = _model->getCargo();
	ui->cargoEdit->setText(cargovalue.c_str());
	ui->cargoTitleEdit->setText(_model->getCargoTitle().c_str());
}
} // namespace fso::fred::dialogs