//
//

#if defined(_MSC_VER) && _MSC_VER <= 1920
	// work around MSVC 2015 and 2017 compiler bug
	// https://bugreports.qt.io/browse/QTBUG-72073
	#define QT_NO_FLOAT16_OPERATORS
#endif

#include "MissionSpecDialogModel.h"
#include "ui/dialogs/MissionSpecDialog.h"

#include "cfile/cfile.h"
#include "localization/localize.h"
#include "mission/missionmessage.h"
#include "mission/mission_flags.h"
#include "scripting/global_hooks.h"

#include <QtWidgets>

namespace fso::fred::dialogs {

MissionSpecDialogModel::MissionSpecDialogModel(QObject* parent, EditorViewport* viewport) :
	AbstractDialogModel(parent, viewport) {
	initializeData();
}

void MissionSpecDialogModel::initializeData() {
	prepareSquadLogoList();

	_m_mission_title = The_mission.name;
	_m_designer_name = The_mission.author;
	_m_created = The_mission.created;
	_m_modified = The_mission.modified;
	_m_mission_notes = The_mission.notes;
	_m_mission_desc = The_mission.mission_desc;

	_m_full_war = Mission_all_attack;
	_m_disallow_support = (The_mission.support_ships.max_support_ships == 0);

	_m_flags = The_mission.flags;

	_m_loading_640 = The_mission.loading_screen[GR_640];
	_m_loading_1024 = The_mission.loading_screen[GR_1024];

	// squad info
	if (strlen(The_mission.squad_name) > 0) { //-V805
		_m_squad_name = The_mission.squad_name;
		_m_squad_filename = The_mission.squad_filename;
	}
	else {
		_m_squad_name = NO_SQUAD;
		_m_squad_filename = "";
	}

	_m_type = The_mission.game_type;
	_m_ai_profile = AI_PROFILES_INDEX(The_mission.ai_profile);

	_m_event_music = Current_soundtrack_num + 1;
	_m_substitute_event_music = The_mission.substitute_event_music_name;

	_m_command_persona = The_mission.command_persona;
	_m_command_sender = The_mission.command_sender;

	_m_num_respawns = The_mission.num_respawns;
	_m_max_respawn_delay = The_mission.max_respawn_delay;
	_m_player_entry_delay = f2fl(Entry_delay_time);
	_m_max_hull_repair_val = The_mission.support_ships.max_hull_repair_val;
	_m_max_subsys_repair_val = The_mission.support_ships.max_subsys_repair_val;

	_m_support_rearm_settings.disallowSupportShips = (The_mission.support_ships.max_support_ships == 0);
	_m_support_rearm_settings.supportRepairsHull = The_mission.flags[Mission::Mission_Flags::Support_repairs_hull];
	_m_support_rearm_settings.maxHullRepair = The_mission.support_ships.max_hull_repair_val;
	_m_support_rearm_settings.maxSubsysRepair = The_mission.support_ships.max_subsys_repair_val;
	_m_support_rearm_settings.disallowSupportRearm = The_mission.support_ships.disallow_rearm;
	_m_support_rearm_settings.limitRearmToPool = The_mission.flags[Mission::Mission_Flags::Limited_support_rearm_pool];
	_m_support_rearm_settings.rearmPoolFromLoadout = The_mission.support_ships.rearm_pool_from_loadout;
	_m_support_rearm_settings.allowWeaponPrecedence = The_mission.support_ships.allow_rearm_weapon_precedence;
	for (int team = 0; team < MAX_TVT_TEAMS; ++team) {
		for (int i = 0; i < MAX_WEAPON_TYPES; ++i) {
			_m_support_rearm_settings.rearmWeaponPool[team][i] = The_mission.support_ships.rearm_weapon_pool[team][i];
		}
	}

	_m_contrail_threshold = The_mission.contrail_threshold;
	_m_contrail_threshold_flag = (_m_contrail_threshold != CONTRAIL_THRESHOLD_DEFAULT);
	_m_large_ship_no_collide_collision_group = The_mission.large_ship_no_collide_collision_group;

	_m_custom_data = The_mission.custom_data;
	_m_custom_strings = The_mission.custom_strings;
	_m_sound_env = The_mission.sound_environment;

	// init starting wings
	for (int i = 0; i < MAX_STARTING_WINGS; i++) {
		_m_custom_starting_wings[i] = Starting_wing_names[i];
	}

	// init squadron wings
	for (int i = 0; i < MAX_SQUADRON_WINGS; i++) {
		_m_custom_squadron_wings[i] = Squadron_wing_names[i];
	}

	// init tvt wings
	for (int i = 0; i < MAX_TVT_WINGS; i++) {
		_m_custom_tvt_wings[i] = TVT_wing_names[i];
	}

	modelChanged();
	_modified = false;
}

void MissionSpecDialogModel::prepareSquadLogoList()
{
	pilot_load_squad_pic_list();

	for (int i = 0; i < Num_pilot_squad_images; i++) {
		_m_squadLogoList.emplace_back(Pilot_squad_image_names[i]);
	}
}

bool MissionSpecDialogModel::apply() {
	int new_m_type;

	// deal with changing the mission type.  Code is done this way since training missions
	// just override anything else.
	new_m_type = _m_type;

	if (!new_m_type) {
		auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Invalid mission type", "You must select the game type: training, single, or multiplayer",
		{ DialogButton::Ok });
		if (button == DialogButton::Ok) {
			return false;
		}
	}

	The_mission.game_type = new_m_type;
	The_mission.num_respawns = _m_num_respawns;
	The_mission.max_respawn_delay = _m_max_respawn_delay;
	Entry_delay_time = fl2f(_m_player_entry_delay);
	The_mission.large_ship_no_collide_collision_group = _m_large_ship_no_collide_collision_group;

	The_mission.support_ships.max_support_ships = (_m_support_rearm_settings.disallowSupportShips) ? 0 : -1;
	The_mission.support_ships.max_hull_repair_val = _m_support_rearm_settings.maxHullRepair;
	The_mission.support_ships.max_subsys_repair_val = _m_support_rearm_settings.maxSubsysRepair;
	The_mission.support_ships.disallow_rearm = _m_support_rearm_settings.disallowSupportRearm;
	The_mission.support_ships.allow_rearm_weapon_precedence = _m_support_rearm_settings.allowWeaponPrecedence;
	The_mission.support_ships.rearm_pool_from_loadout = _m_support_rearm_settings.rearmPoolFromLoadout;
	for (int team = 0; team < MAX_TVT_TEAMS; ++team) {
		for (int i = 0; i < weapon_info_size(); ++i) {
			The_mission.support_ships.rearm_weapon_pool[team][i] = _m_support_rearm_settings.rearmWeaponPool[team][i];
		}
	}
	
	// Copy mission flags
	The_mission.flags = _m_flags;

	// set ship trail threshold
	if (_m_contrail_threshold_flag) {
		The_mission.contrail_threshold = _m_contrail_threshold;
	} else {
		The_mission.contrail_threshold = CONTRAIL_THRESHOLD_DEFAULT;
	}
	
	// originally the dialog stripped out quotation marks here;
	// now it handles all special characters
	lcl_fred_replace_stuff(_m_mission_title);
	lcl_fred_replace_stuff(_m_designer_name);
	lcl_fred_replace_stuff(_m_mission_notes);
	lcl_fred_replace_stuff(_m_mission_desc);
	lcl_fred_replace_stuff(_m_squad_name);

	// puts "$End Notes:" on a different line to ensure it's not interpreted as part of a comment
	Editor::pad_with_newline(_m_mission_notes, NOTES_LENGTH - 1);

	The_mission.name = _m_mission_title;
	The_mission.author = _m_designer_name;
	strncpy(The_mission.loading_screen[GR_640], _m_loading_640.c_str(), NAME_LENGTH-1);
	strncpy(The_mission.loading_screen[GR_1024], _m_loading_1024.c_str(), NAME_LENGTH-1);
	strncpy(The_mission.notes, _m_mission_notes.c_str(), NOTES_LENGTH);
	strncpy(The_mission.mission_desc, _m_mission_desc.c_str(), MISSION_DESC_LENGTH);

	// copy squad stuff
	if (_m_squad_name == NO_SQUAD) {
		strcpy_s(The_mission.squad_name, "");
		strcpy_s(The_mission.squad_filename, "");
	} else {
		strncpy(The_mission.squad_name, _m_squad_name.c_str(), NAME_LENGTH);
		strncpy(The_mission.squad_filename, _m_squad_filename.c_str(), MAX_FILENAME_LEN);
	}

	The_mission.ai_profile = &Ai_profiles[_m_ai_profile];

	Current_soundtrack_num = _m_event_music - 1;
	strcpy_s(The_mission.substitute_event_music_name, _m_substitute_event_music.c_str());

	The_mission.command_persona = _m_command_persona;
	if (_m_command_sender.at(0) == '#')
		strcpy_s(The_mission.command_sender, _m_command_sender.substr(1).c_str());
	else
		strcpy_s(The_mission.command_sender, _m_command_sender.c_str());

	Mission_all_attack = (int)_m_full_war;

	// update the Num_teams variable accoriding to mission types
	Num_teams = 1;
	if ((The_mission.game_type & MISSION_TYPE_MULTI) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)) {
		Num_teams = 2;
	}

	The_mission.custom_data = _m_custom_data;
	The_mission.custom_strings = _m_custom_strings;

	The_mission.sound_environment = _m_sound_env;

	// copy starting wings
	for (int i = 0; i < MAX_STARTING_WINGS; i++) {
		strcpy_s(Starting_wing_names[i], _m_custom_starting_wings[i].c_str());
	}

	// copy squadron wings
	for (int i = 0; i < MAX_SQUADRON_WINGS; i++) {
		strcpy_s(Squadron_wing_names[i], _m_custom_squadron_wings[i].c_str());
	}

	// copy tvt wings
	for (int i = 0; i < MAX_TVT_WINGS; i++) {
		strcpy_s(TVT_wing_names[i], _m_custom_tvt_wings[i].c_str());
	}

	Editor::update_custom_wing_indexes();

	// scripts may rebuild LuaEnums when custom data/strings change.
	if (scripting::hooks::FredOnMissionSpecsSave->isActive()) {
		scripting::hooks::FredOnMissionSpecsSave->run();
	}

	return true;
}

void MissionSpecDialogModel::reject() {

}

void MissionSpecDialogModel::setMissionTitle(const SCP_string& m_mission_title) {
	modify(_m_mission_title, m_mission_title);
}

SCP_string MissionSpecDialogModel::getMissionTitle() {
	return _m_mission_title;
}

void MissionSpecDialogModel::setDesigner(const SCP_string& m_designer_name) {
	modify(_m_designer_name, m_designer_name);
}

SCP_string MissionSpecDialogModel::getDesigner() {
	return _m_designer_name;
}

SCP_string MissionSpecDialogModel::getCreatedTime() {
	return _m_created;
}

SCP_string MissionSpecDialogModel::getModifiedTime() {
	return _m_modified;
}

void MissionSpecDialogModel::setMissionType(int m_type) {
	if (m_type & (MISSION_TYPE_SINGLE |
		MISSION_TYPE_MULTI |
		MISSION_TYPE_TRAINING |
		MISSION_TYPE_MULTI_COOP |
		MISSION_TYPE_MULTI_DOGFIGHT |
		MISSION_TYPE_MULTI_TEAMS)) {
		modify(_m_type, m_type);
		modelChanged();
	} else {
		auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Invalid mission type", "You must select the game type: training, single, or multiplayer",
		{ DialogButton::Ok });
		if (button == DialogButton::Ok) {
			return;
		}
	}
}

int MissionSpecDialogModel::getMissionType() {
	return _m_type;
}

void MissionSpecDialogModel::setNumRespawns(uint m_num_respawns) {
	modify(_m_num_respawns, m_num_respawns);
}

uint MissionSpecDialogModel::getNumRespawns() {
	return _m_num_respawns;
}

void MissionSpecDialogModel::setMaxRespawnDelay(int m_max_respawn_delay) {
	modify(_m_max_respawn_delay, m_max_respawn_delay);
}

int MissionSpecDialogModel::getMaxRespawnDelay() {
	return _m_max_respawn_delay;
}

void MissionSpecDialogModel::setPlayerEntryDelay(float m_player_entry_delay) {
	if (m_player_entry_delay < 0.0f) {
		m_player_entry_delay = 0.0f;
	}

	modify(_m_player_entry_delay, m_player_entry_delay);
}

float MissionSpecDialogModel::getPlayerEntryDelay() const {
	return _m_player_entry_delay;
}

void MissionSpecDialogModel::setSquadronName(const SCP_string& m_squad_name) {
	modify(_m_squad_name, m_squad_name);
}

SCP_string MissionSpecDialogModel::getSquadronName() {
	return _m_squad_name;
}

void MissionSpecDialogModel::setSquadronLogo(const SCP_string& m_squad_filename) {
	modify(_m_squad_filename, m_squad_filename);
}

SCP_string MissionSpecDialogModel::getSquadronLogo() {
	return _m_squad_filename;
}

void MissionSpecDialogModel::setLowResLoadingScreen(const SCP_string& m_loading_640) {
	modify(_m_loading_640, m_loading_640);
}

SCP_string MissionSpecDialogModel::getLowResLoadingScren() {
	return _m_loading_640;
}

void MissionSpecDialogModel::setHighResLoadingScreen(const SCP_string& m_loading_1024) {
	modify(_m_loading_1024, m_loading_1024);
}

SCP_string MissionSpecDialogModel::getHighResLoadingScren() {
	return _m_loading_1024;
}

void MissionSpecDialogModel::setDisallowSupport(bool m_disallow_support) {
	modify(_m_disallow_support, m_disallow_support);
	_m_support_rearm_settings.disallowSupportShips = m_disallow_support;
}

bool MissionSpecDialogModel::getDisallowSupport() {
	return _m_disallow_support;
}

void MissionSpecDialogModel::setHullRepairMax(float m_max_hull_repair_val) {
	modify(_m_max_hull_repair_val, m_max_hull_repair_val);
	_m_support_rearm_settings.maxHullRepair = m_max_hull_repair_val;
}

int MissionSpecDialogModel::getHullRepairMax() {
	return _m_max_hull_repair_val;
}

void MissionSpecDialogModel::setSubsysRepairMax(float m_max_subsys_repair_val){
	modify(_m_max_subsys_repair_val, m_max_subsys_repair_val);
}

int MissionSpecDialogModel::getSubsysRepairMax() {
	return _m_max_subsys_repair_val;
}

SupportRearmSettings MissionSpecDialogModel::getSupportRearmSettings() const
{
	return _m_support_rearm_settings;
}

void MissionSpecDialogModel::setSupportRearmSettings(const SupportRearmSettings& settings)
{
	modify(_m_support_rearm_settings, settings);
	_m_disallow_support = settings.disallowSupportShips;
	_m_max_hull_repair_val = settings.maxHullRepair;
	_m_max_subsys_repair_val = settings.maxSubsysRepair;
	setMissionFlagDirect(Mission::Mission_Flags::Support_repairs_hull, settings.supportRepairsHull);
	setMissionFlagDirect(Mission::Mission_Flags::Limited_support_rearm_pool, settings.limitRearmToPool);
}

void MissionSpecDialogModel::setTrailThresholdFlag(bool m_contrail_threshold_flag) {
	modify(_m_contrail_threshold_flag, m_contrail_threshold_flag);
}

bool MissionSpecDialogModel::getTrailThresholdFlag() {
	return _m_contrail_threshold_flag;
}

void MissionSpecDialogModel::setTrailDisplaySpeed(int m_contrail_threshold) {
	modify(_m_contrail_threshold, m_contrail_threshold);
}

int MissionSpecDialogModel::getTrailDisplaySpeed() {
	return _m_contrail_threshold;
}

void MissionSpecDialogModel::setCommandSender(const SCP_string& m_command_sender) {
	modify(_m_command_sender, m_command_sender);
}

SCP_string MissionSpecDialogModel::getCommandSender() {
	return _m_command_sender;
}

void MissionSpecDialogModel::setCommandPersona(int m_command_persona) {
	modify(_m_command_persona, m_command_persona);
}

int MissionSpecDialogModel::getCommandPersona() {
	return _m_command_persona;
}

void MissionSpecDialogModel::setEventMusic(int m_event_music) {
	modify(_m_event_music, m_event_music);
}

int MissionSpecDialogModel::getEventMusic() {
	return _m_event_music;
}

void MissionSpecDialogModel::setSubEventMusic(const SCP_string& m_substitute_event_music) {
	modify(_m_substitute_event_music, m_substitute_event_music);
}

SCP_string MissionSpecDialogModel::getSubEventMusic() {
	return _m_substitute_event_music;
}

void MissionSpecDialogModel::setMissionFlag(const SCP_string& flag_name, bool enabled)
{
	// Find the matching flagDef by name
	for (size_t i = 0; i < Num_parse_mission_flags; ++i) {
		if (!stricmp(flag_name.c_str(), Parse_mission_flags[i].name)) {
			if (enabled)
				_m_flags.set(Parse_mission_flags[i].def);
			else
				_m_flags.remove(Parse_mission_flags[i].def);
			break;
		}
	}

	set_modified();
}

void MissionSpecDialogModel::setMissionFlagDirect(Mission::Mission_Flags flag, bool enabled) {
	if (_m_flags[flag] != enabled) {
		_m_flags.set(flag, enabled);
		set_modified();
		modelChanged();
	}
}

void MissionSpecDialogModel::setLargeShipNoCollideCollisionGroup(int group)
{
	if (group < 0) {
		group = 0;
	} else if (group > 31) {
		group = 31;
	}

	modify(_m_large_ship_no_collide_collision_group, group);
}

int MissionSpecDialogModel::getLargeShipNoCollideCollisionGroup() const
{
	return _m_large_ship_no_collide_collision_group;
}

bool MissionSpecDialogModel::getMissionFlag(Mission::Mission_Flags flag) const {
	return _m_flags[flag];
}

const SCP_vector<std::pair<SCP_string, bool>>& MissionSpecDialogModel::getMissionFlagsList() {
	_m_flag_data.clear();
	for (size_t i = 0; i < Num_parse_mission_flags; ++i) {
		auto flagDef = Parse_mission_flags[i];

		// Skip flags that have checkboxes elsewhere than the flag list or are inactive
		if (flagDef.is_special || !flagDef.in_use) {
			continue;
		}

		bool checked = _m_flags[flagDef.def];
		_m_flag_data.emplace_back(flagDef.name, checked);
	}
	return _m_flag_data;
}

SCP_vector<std::pair<SCP_string, SCP_string>> MissionSpecDialogModel::getMissionFlagDescriptions()
{
	const size_t num_descs = Num_parse_mission_flag_descriptions;
	SCP_vector<std::pair<SCP_string, SCP_string>> descriptions;
	descriptions.reserve(Num_parse_mission_flags);
	for (size_t i = 0; i < Num_parse_mission_flags; ++i) {
		const auto& flagDef = Parse_mission_flags[i];
		if (flagDef.is_special || !flagDef.in_use)
			continue;
		for (size_t j = 0; j < num_descs; ++j) {
			if (Parse_mission_flag_descriptions[j].def == flagDef.def) {
				descriptions.emplace_back(flagDef.name, Parse_mission_flag_descriptions[j].flag_desc);
				break;
			}
		}
	}
	return descriptions;
}

void MissionSpecDialogModel::setMissionFullWar(bool enabled) {
	if (_m_full_war != enabled) {
		_m_full_war = enabled;
		_m_flags.set(Mission::Mission_Flags::All_attack, enabled);
		set_modified();
		modelChanged();
	}
}

void MissionSpecDialogModel::setAIProfileIndex(int m_ai_profile) {
	modify(_m_ai_profile, m_ai_profile);
}

int MissionSpecDialogModel::getAIProfileIndex() const {
	return _m_ai_profile;
}

void MissionSpecDialogModel::setMissionDescText(const SCP_string& m_mission_desc) {
	modify(_m_mission_desc, m_mission_desc.substr(0, MIN(static_cast<size_t>(MISSION_DESC_LENGTH), m_mission_desc.length())));
}

SCP_string MissionSpecDialogModel::getMissionDescText() {
	return _m_mission_desc;
}

void MissionSpecDialogModel::setDesignerNoteText(const SCP_string& m_mission_notes) {
	modify(_m_mission_notes, m_mission_notes.substr(0, MIN(static_cast<size_t>(NOTES_LENGTH), m_mission_notes.length())));
}

SCP_string MissionSpecDialogModel::getDesignerNoteText() {
	return _m_mission_notes;
}

void MissionSpecDialogModel::setCustomData(const SCP_map<SCP_string, SCP_string>& custom_data)
{
	modify(_m_custom_data, custom_data);
	set_modified();
}

SCP_map<SCP_string, SCP_string> MissionSpecDialogModel::getCustomData() const
{
	return _m_custom_data;
}

void MissionSpecDialogModel::setCustomStrings(const SCP_vector<custom_string>& custom_strings)
{
	modify(_m_custom_strings, custom_strings);
}

SCP_vector<custom_string> MissionSpecDialogModel::getCustomStrings() const
{
	return _m_custom_strings;
}

void MissionSpecDialogModel::setSoundEnvironmentParams(const sound_env& snd_env)
{
	modify(_m_sound_env, snd_env);
}

sound_env MissionSpecDialogModel::getSoundEnvironmentParams() const
{
	return _m_sound_env;
}

void MissionSpecDialogModel::setCustomStartingWings(const std::array<SCP_string, MAX_STARTING_WINGS>& starting_wings)
{
	for (int i = 0; i < MAX_STARTING_WINGS; i++) {
		modify(_m_custom_starting_wings[i], starting_wings[i]);
	}
}

std::array<SCP_string, MAX_STARTING_WINGS> MissionSpecDialogModel::getCustomStartingWings() const
{
	return _m_custom_starting_wings;
}

void MissionSpecDialogModel::setCustomSquadronWings(const std::array<SCP_string, MAX_SQUADRON_WINGS>& squadron_wings)
{
	for (int i = 0; i < MAX_SQUADRON_WINGS; i++) {
		modify(_m_custom_squadron_wings[i], squadron_wings[i]);
	}
}

std::array<SCP_string, MAX_SQUADRON_WINGS> MissionSpecDialogModel::getCustomSquadronWings() const
{
	return _m_custom_squadron_wings;
}

void MissionSpecDialogModel::setCustomTvTWings(const std::array<SCP_string, MAX_TVT_WINGS>& tvt_wings)
{
	for (int i = 0; i < MAX_TVT_WINGS; i++) {
		modify(_m_custom_tvt_wings[i], tvt_wings[i]);
	}
}

std::array<SCP_string, MAX_TVT_WINGS> MissionSpecDialogModel::getCustomTvTWings() const
{
	return _m_custom_tvt_wings;
}

} // namespace fso::fred::dialogs
