#include "ShipGoalsDialog.h"

#include "ui_ShipGoalsDialog.h"

#include "mission/util.h"

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

#include <QCloseEvent>

namespace fso::fred::dialogs {
ShipGoalsDialog::ShipGoalsDialog(QWidget* parent, EditorViewport* viewport, bool editMultiple, int shipID, int wingID)
	: QDialog(parent), ui(new Ui::ShipGoalsDialog()),
	  _model(new ShipGoalsDialogModel(this, viewport, editMultiple, shipID, wingID)), _viewport(viewport)
{
	ui->setupUi(this);
	for (int i = 0; i < ED_MAX_GOALS; i++) {
		const int row = i + 1; // row 0 is the header

		auto* orderLabel = new QLabel(tr("Order %1").arg(i + 1), this);
		behaviors[i] = new QComboBox(this);
		objects[i]   = new QComboBox(this);
		subsys[i]    = new QComboBox(this);
		docks[i]     = new QComboBox(this);
		priority[i]  = new QSpinBox(this);

		ui->gridLayout->addWidget(orderLabel,   row, 0);
		ui->gridLayout->addWidget(behaviors[i], row, 1);
		ui->gridLayout->addWidget(objects[i],   row, 2);
		ui->gridLayout->addWidget(subsys[i],    row, 3);
		ui->gridLayout->addWidget(docks[i],     row, 4);
		ui->gridLayout->addWidget(priority[i],  row, 5);
	}

	connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipGoalsDialog::updateUi);
	for (int i = 0; i < ED_MAX_GOALS; i++) {
		connect(behaviors[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) {
			_model->setBehavior(i, index);
		});
		connect(objects[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) {
			int datap = objects[i]->itemData(index).value<int>();
			_model->setObject(i, datap);
		});
		connect(subsys[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) {
			auto datap = subsys[i]->itemData(index).value<QString>().toStdString();
			_model->setSubsys(i, datap);
		});
		connect(docks[i], QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index) {
			int datap = docks[i]->itemData(index).value<int>();
			_model->setDock(i, datap);
		});
		connect(priority[i], QOverload<int>::of(&QSpinBox::valueChanged), [=]() {
			int datap = priority[i]->value();
			_model->setPriority(i, datap);
		});
	}

	updateUi();

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

ShipGoalsDialog::~ShipGoalsDialog() = default;

void ShipGoalsDialog::accept()
{
	// If apply() returns true, close the dialog
	if (_model->apply()) {
		QDialog::accept();
	}
	// else: validation failed, don't close
}

void ShipGoalsDialog::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 ShipGoalsDialog::closeEvent(QCloseEvent* e)
{
	reject();
	e->ignore(); // Don't let the base class close the window
}
void ShipGoalsDialog::on_okButton_clicked()
{
	accept();
}
void ShipGoalsDialog::on_cancelButton_clicked()
{
	reject();
}
void ShipGoalsDialog::updateUi()
{
	if (_updatingUi)
		return;
	_updatingUi = true;
	util::SignalBlockers blockers(this);

	for (int i = 0; i < ED_MAX_GOALS; i++) {
		behaviors[i]->clear();
		objects[i]->clear();
		subsys[i]->clear();
		docks[i]->clear();

		for (const auto& entry : _model->getAiGoalComboData()) {
			behaviors[i]->addItem(entry.first);
		}

		auto value = _model->getBehavior(i);
		behaviors[i]->setCurrentIndex(value);

		auto mode = _model->getFirstModeFromComboBox(i);

		SCP_vector<waypoint_list>::iterator ii;
		if (value < 1) {
			objects[i]->setEnabled(false);
			subsys[i]->setEnabled(false);
			docks[i]->setEnabled(false);
			priority[i]->setEnabled(false);
			SCP_string blank;
			if (!_model->getSubsys(i).empty()) {
				_model->setSubsys(i, blank);
			}
			_model->setDock(i, -1);
		} else if ((mode == AI_GOAL_CHASE_ANY) || (mode == AI_GOAL_UNDOCK) || (mode == AI_GOAL_KEEP_SAFE_DISTANCE) ||
				   (mode == AI_GOAL_PLAY_DEAD) || (mode == AI_GOAL_PLAY_DEAD_PERSISTENT) || (mode == AI_GOAL_WARP)) {
			priority[i]->setEnabled(true);
			objects[i]->setEnabled(false);
			subsys[i]->setEnabled(false);
			docks[i]->setEnabled(false);
			SCP_string blank;
			_model->setSubsys(i, blank);
			_model->setDock(i, -1);
			priority[i]->setValue(_model->getPriority(i));
		} else {
			objects[i]->setEnabled(true);
			// for goals that deal with waypoint paths or individual waypoints
			switch (mode) {
			case AI_GOAL_WAYPOINTS:
			case AI_GOAL_WAYPOINTS_ONCE:
				// case AI_GOAL_WARP:
				int j;
				for (j = 0, ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++j, ++ii) {
					objects[i]->addItem(ii->get_name(), QVariant(int(j | TYPE_PATH)));
					if (_model->getObject(i) == (j | TYPE_PATH)) {
						objects[i]->setCurrentIndex(j);
					}
				}
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));

				break;
			case AI_GOAL_STAY_STILL:
				object* ptr;
				ptr = GET_FIRST(&obj_used_list);
				while (ptr != END_OF_LIST(&obj_used_list)) {
					if (ptr->type == OBJ_WAYPOINT) {
						objects[i]->addItem(object_name(OBJ_INDEX(ptr)), QVariant(int(OBJ_INDEX(ptr) | TYPE_WAYPOINT)));
						if ((_model->getObject(i) == (OBJ_INDEX(ptr) | TYPE_WAYPOINT)))
							objects[i]->setCurrentIndex(
								objects[i]->findData(QVariant(int(OBJ_INDEX(ptr) | TYPE_WAYPOINT))));
					}

					ptr = GET_NEXT(ptr);
				}
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));

				break;

			default:
				break;
			}
			// for goals that deal with ship classes
			switch (mode) {
			case AI_GOAL_CHASE_SHIP_CLASS:
				int j;
				for (j = 0; j < ship_info_size(); j++) {
					objects[i]->addItem(Ship_info[j].name, QVariant(int(j | TYPE_SHIP_CLASS)));
					if (_model->getObject(i) == (j | TYPE_SHIP_CLASS))
						objects[i]->setCurrentIndex(j);
				}
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));

				break;
			default:
				break;
			} // for goals that deal with individual ships
			switch (mode) {
			case AI_GOAL_DESTROY_SUBSYSTEM:
			case AI_GOAL_CHASE:
			case AI_GOAL_CHASE_WING:
			case AI_GOAL_DOCK:
			case AI_GOAL_GUARD:
			case AI_GOAL_GUARD_WING:
			case AI_GOAL_DISABLE_SHIP:
			case AI_GOAL_DISABLE_SHIP_TACTICAL:
			case AI_GOAL_DISARM_SHIP:
			case AI_GOAL_DISARM_SHIP_TACTICAL:
			case AI_GOAL_EVADE_SHIP:
			case AI_GOAL_IGNORE:
			case AI_GOAL_IGNORE_NEW:
			case AI_GOAL_STAY_NEAR_SHIP:
			case AI_GOAL_FORM_ON_WING:
			case AI_GOAL_STAY_STILL:
				object* ptr;
				int inst, t;
				ptr = GET_FIRST(&obj_used_list);
				while (ptr != END_OF_LIST(&obj_used_list)) {
					if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
						inst = ptr->instance;
						if (ptr->type == OBJ_SHIP)
							t = TYPE_SHIP;
						else
							t = TYPE_PLAYER;

						Assert(inst >= 0 && inst < MAX_SHIPS);
						// remove all marked ships from list
						if ((_model->getGoal() != nullptr) && (ptr->flags[Object::Object_Flags::Marked])) {
							inst = -1;
						}

						// when docking, remove invalid dock targets
						else if (mode == AI_GOAL_DOCK) {
							if (_model->getShip() < 0 || !ship_docking_valid(_model->getShip(), inst))
								inst = -1;
						}

						// disallow ship being its own target
						if (inst >= 0 && inst != _model->getShip()) {
							objects[i]->addItem(Ships[inst].ship_name, QVariant(int(inst | t)));
							if (_model->getObject(i) == -1)
								_model->setObject(i, (inst | t));
							if (_model->getObject(i) == (inst | t))
								objects[i]->setCurrentIndex(objects[i]->findData((inst | t)));
						}
					}
					priority[i]->setEnabled(true);
					priority[i]->setValue(_model->getPriority(i));

					ptr = GET_NEXT(ptr);
				}
				break;
			default:
				break;
			}
			// for goals that deal with wings
			switch (mode) {
			case AI_GOAL_CHASE:
			case AI_GOAL_CHASE_WING:
			case AI_GOAL_GUARD:
			case AI_GOAL_GUARD_WING:
				int j;
				for (j = 0; j < MAX_WINGS; j++) {
					if (Wings[j].wave_count && j != _model->getWing()) {
						objects[i]->addItem(Wings[j].name, QVariant(int(j | TYPE_WING)));
						if (_model->getObject(i) == (j | TYPE_WING))
							objects[i]->setCurrentIndex(objects[i]->findData((j | TYPE_WING)));
					}
				}
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));
				break;
			default:
				break;
			}
			if (mode == AI_GOAL_DESTROY_SUBSYSTEM) {
				subsys[i]->setEnabled(true);
				docks[i]->setEnabled(false);
				subsys[i]->clear();
				_model->setDock(i, -1);
				ship_subsys* cur_subsys;
				auto subsysvalue = _model->getSubsys(i);
				int target_ship_idx = _model->getObject(i) & DATA_MASK;
				if (target_ship_idx >= 0 && target_ship_idx < MAX_SHIPS) {
					cur_subsys = GET_FIRST(&Ships[target_ship_idx].subsys_list);
					while (cur_subsys != END_OF_LIST(&Ships[target_ship_idx].subsys_list)) {
						subsys[i]->addItem(cur_subsys->system_info->subobj_name,
							QVariant(QString(cur_subsys->system_info->subobj_name)));
						cur_subsys = GET_NEXT(cur_subsys);
					}
				}
				if (subsysvalue.empty()) {
					subsys[i]->setCurrentIndex(0);
				} else {
					subsys[i]->setCurrentIndex(subsys[i]->findData(subsysvalue.c_str()));
				}
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));

			} else if (mode == AI_GOAL_DOCK) {
				SCP_vector<SCP_string> docklist = _viewport->editor->get_docking_list(
					Ship_info[Ships[_viewport->editor->cur_ship].ship_info_index].model_num);
				subsys[i]->setEnabled(true);
				subsys[i]->clear();
				auto subsysvalue = _model->getSubsys(i);
				int j;
				for (j = 0; unsigned(j) < docklist.size(); j++) {
					subsys[i]->addItem(docklist[j].c_str(), QVariant(QString(docklist[j].c_str())));
				}
				subsys[i]->setCurrentIndex(subsys[i]->findData(subsysvalue.c_str()));

				docklist = _viewport->editor->get_docking_list(
					Ship_info[Ships[_model->getObject(i) & DATA_MASK].ship_info_index].model_num);
				docks[i]->setEnabled(true);
				auto dockvalue = _model->getDock(i);
				docks[i]->clear();
				for (j = 0; unsigned(j) < docklist.size(); j++) {
					docks[i]->addItem(docklist[j].c_str(), QVariant(QString(docklist[j].c_str())));
				}
				docks[i]->setCurrentIndex(dockvalue);
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));

			} else {
				subsys[i]->setEnabled(false);
				docks[i]->setEnabled(false);
				_model->setDock(i, -1);
				SCP_string blank;
				_model->setSubsys(i, blank);
				priority[i]->setEnabled(true);
				priority[i]->setValue(_model->getPriority(i));
			}
		}
	}
	_updatingUi = false;
}
} // namespace fso::fred::dialogs