/*
 * Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project.
 * You may not sell or otherwise commercially exploit the source or things you
 * create based on the source.
 */




#include "ai/ai_profiles.h"
#include "ai/aibig.h"
#include "def_files/def_files.h"
#include "globalincs/pstypes.h"
#include "localization/localize.h"
#include "mod_table/mod_table.h"
#include "parse/parselo.h"
#include "ship/ship.h"
#include "weapon/weapon.h"


// global stuff
int Num_ai_profiles;
int Default_ai_profile;
ai_profile_t Ai_profiles[MAX_AI_PROFILES];

// local to this file
static int Ai_profiles_initted = 0;
static char Default_profile_name[NAME_LENGTH];


// utility
void set_flag(ai_profile_t *profile, const char *name, AI::Profile_Flags flag)
{
	if (optional_string(name))
	{
		bool val;
		stuff_boolean(&val);
        profile->flags.set(flag, val);
	}
}

const char *AI_path_types[] = {
	"normal",
	"alt1",
};

int Num_ai_path_types = sizeof(AI_path_types)/sizeof(char*);

int ai_path_type_match(char *p)
{
	int i;
	for(i = 0; i < Num_ai_path_types; i++)
	{
		if(!stricmp(AI_path_types[i], p))
			return i;
	}

	return -1;
}


const char* AI_secondary_range_aware_select_modes[] = {
	"retail",
	"aware",
};

int Num_ai_secondary_range_aware_select_modes = sizeof(AI_secondary_range_aware_select_modes) / sizeof(char*);

int ai_secondary_range_select_mode_match(char* p)
{
	int i;
	for (i = 0; i < Num_ai_secondary_range_aware_select_modes; i++)
	{
		if (!stricmp(AI_secondary_range_aware_select_modes[i], p))
			return i;
	}

	return -1;
}

void parse_ai_profiles_tbl(const char *filename)
{
	int i;
	char profile_name[NAME_LENGTH];
	ai_profile_t dummy_profile;
	char *saved_Mp = NULL;
	char buf[NAME_LENGTH];

	try
	{
		if (filename == NULL)
			read_file_text_from_default(defaults_get_file("ai_profiles.tbl"));
		else
			read_file_text(filename, CF_TYPE_TABLES);

		reset_parse();


		// start parsing
		required_string("#AI Profiles");

		// new default?
		if (optional_string("$Default Profile:"))
			stuff_string(Default_profile_name, F_NAME, NAME_LENGTH);

		// begin reading data
		while (required_string_either("#End", "$Profile Name:"))
		{
			ai_profile_t *profile = &dummy_profile;
			ai_profile_t *previous_profile = NULL;
			bool no_create = false;

			// get the name
			required_string("$Profile Name:");
			stuff_string(profile_name, F_NAME, NAME_LENGTH);

			// see if it exists
			for (i = 0; i < Num_ai_profiles; i++)
			{
				if (!stricmp(Ai_profiles[i].profile_name, profile_name))
				{
					previous_profile = &Ai_profiles[i];
					break;
				}
			}

			// modular table stuff
			if (optional_string("+nocreate"))
			{
				no_create = true;

				// use the previous one if possible,
				// otherwise continue to use the dummy one
				if (previous_profile != NULL)
					profile = previous_profile;
			}
			else
			{
				// don't create multiple profiles with the same name
				if (previous_profile != NULL)
				{
					Warning(LOCATION, "An ai profile named '%s' already exists!  The new one will not be created.\n", profile_name);
				}
				else
				{
					// make sure we're under the limit
					if (Num_ai_profiles >= MAX_AI_PROFILES)
					{
						Warning(LOCATION, "Too many profiles in ai_profiles.tbl!  Max is %d.\n", MAX_AI_PROFILES - 1);	// -1 because one is built-in
						skip_to_string("#End", NULL);
						break;
					}

					profile = &Ai_profiles[Num_ai_profiles];
					Num_ai_profiles++;
				}
			}

			// initialize profile if we're not building from a previously parsed one
			if (!no_create)
			{
				// base profile, so zero it out
				if (profile == &Ai_profiles[0])
				{
                    profile->reset();
				}
				// brand new profile, so set it to the base defaults
				else
				{
                    *profile = Ai_profiles[0];
				}
			}

			// set the name
			strcpy_s(profile->profile_name, profile_name);


			// fill in any and all settings; they're all optional and can be in any order
			while (!check_for_string("$Profile Name:") && !check_for_string("#End"))
			{
				if (optional_string("$Player Afterburner Recharge Scale:"))
					parse_float_list(profile->afterburner_recharge_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Max Beam Friendly Fire Damage:"))
					parse_float_list(profile->beam_friendly_damage_cap, NUM_SKILL_LEVELS);

				if (optional_string("$Max Weapon Friendly Fire Damage:")) {
					parse_float_list(profile->weapon_friendly_damage_cap, NUM_SKILL_LEVELS);
				}
					
				if (optional_string("$Max Weapon Self Damage:")) {
					parse_float_list(profile->weapon_self_damage_cap, NUM_SKILL_LEVELS);
				}

				if (optional_string("$Player Countermeasure Life Scale:"))
					parse_float_list(profile->cmeasure_life_scale, NUM_SKILL_LEVELS);

				if (optional_string("$AI Countermeasure Firing Chance:"))
					parse_float_list(profile->cmeasure_fire_chance, NUM_SKILL_LEVELS);

				if (optional_string("$AI In Range Time:"))
					parse_float_list(profile->in_range_time, NUM_SKILL_LEVELS);

				if (optional_string("$AI Always Links Ammo Weapons:"))
					parse_float_list(profile->link_ammo_levels_always, NUM_SKILL_LEVELS);

				if (optional_string("$AI Maybe Links Ammo Weapons:"))
					parse_float_list(profile->link_ammo_levels_maybe, NUM_SKILL_LEVELS);

				if (optional_string("$Primary Ammo Burst Multiplier:"))
					parse_float_list(profile->primary_ammo_burst_mult, NUM_SKILL_LEVELS);

				if (optional_string("$AI Always Links Energy Weapons:"))
					parse_float_list(profile->link_energy_levels_always, NUM_SKILL_LEVELS);

				if (optional_string("$AI Maybe Links Energy Weapons:"))
					parse_float_list(profile->link_energy_levels_maybe, NUM_SKILL_LEVELS);

				if (optional_string("$Max Missles Locked on Player:") || optional_string("$Max Missiles Locked on Player:"))
					parse_int_list(profile->max_allowed_player_homers, NUM_SKILL_LEVELS);

				if (optional_string("$Max Player Attackers:"))
					parse_int_list(profile->max_attackers, NUM_SKILL_LEVELS);

				if (optional_string("$Max Incoming Asteroids:"))
					parse_int_list(profile->max_incoming_asteroids, NUM_SKILL_LEVELS);

				if (optional_string("$Player Damage Factor:") || optional_string("$AI Damage Reduction to Player Hull:"))
					parse_float_list(profile->player_damage_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Player Subsys Damage Factor:") || optional_string("$AI Damage Reduction to Player Subsys:"))
					parse_float_list(profile->subsys_damage_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Player Damage Inflicted Factor:"))
					parse_float_list(profile->player_damage_inflicted_scale, NUM_SKILL_LEVELS);

				// represented in fractions of F1_0
				if (optional_string("$Predict Position Delay:"))
				{
					int iLoop;
					float temp_list[NUM_SKILL_LEVELS];

					parse_float_list(temp_list, NUM_SKILL_LEVELS);

					for (iLoop = 0; iLoop < NUM_SKILL_LEVELS; iLoop++)
						profile->predict_position_delay[iLoop] = fl2f(temp_list[iLoop]);
				}

				if (optional_string("$AI Shield Manage Delay:") || optional_string("$AI Shield Manage Delays:"))
					parse_float_list(profile->shield_manage_delay, NUM_SKILL_LEVELS);

				if (optional_string("$Friendly AI Fire Delay Scale:"))
					parse_float_list(profile->ship_fire_delay_scale_friendly, NUM_SKILL_LEVELS);

				if (optional_string("$Hostile AI Fire Delay Scale:"))
					parse_float_list(profile->ship_fire_delay_scale_hostile, NUM_SKILL_LEVELS);

				if (optional_string("$Friendly AI Secondary Fire Delay Scale:"))
					parse_float_list(profile->ship_fire_secondary_delay_scale_friendly, NUM_SKILL_LEVELS);

				if (optional_string("$Hostile AI Secondary Fire Delay Scale:"))
					parse_float_list(profile->ship_fire_secondary_delay_scale_hostile, NUM_SKILL_LEVELS);

				if (optional_string("$AI Turn Time Scale:"))
					parse_float_list(profile->turn_time_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Glide Attack Percent:")) {
					parse_float_list(profile->glide_attack_percent, NUM_SKILL_LEVELS);
					//Percent is nice for modders, but here in the code we want it between 0 and 1.0
					//While we're at it, verify the range
					for (i = 0; i < NUM_SKILL_LEVELS; i++) {
						if (profile->glide_attack_percent[i] < 0.0f || profile->glide_attack_percent[i] > 100.0f) {
							Warning(LOCATION, "$Glide Attack Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_attack_percent[i]);
							profile->glide_attack_percent[i] = 0.0f;
						}
						profile->glide_attack_percent[i] /= 100.0;
					}
				}

				if (optional_string("$Circle Strafe Percent:")) {
					parse_float_list(profile->circle_strafe_percent, NUM_SKILL_LEVELS);
					//Percent is nice for modders, but here in the code we want it between 0 and 1.0
					//While we're at it, verify the range
					for (i = 0; i < NUM_SKILL_LEVELS; i++) {
						if (profile->circle_strafe_percent[i] < 0.0f || profile->circle_strafe_percent[i] > 100.0f) {
							Warning(LOCATION, "$Circle Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->circle_strafe_percent[i]);
							profile->circle_strafe_percent[i] = 0.0f;
						}
						profile->circle_strafe_percent[i] /= 100.0;
					}
				}

				if (optional_string("$Glide Strafe Percent:")) {
					parse_float_list(profile->glide_strafe_percent, NUM_SKILL_LEVELS);
					//Percent is nice for modders, but here in the code we want it between 0 and 1.0
					//While we're at it, verify the range
					for (i = 0; i < NUM_SKILL_LEVELS; i++) {
						if (profile->glide_strafe_percent[i] < 0.0f || profile->glide_strafe_percent[i] > 100.0f) {
							Warning(LOCATION, "$Glide Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_strafe_percent[i]);
							profile->glide_strafe_percent[i] = 0.0f;
						}
						profile->glide_strafe_percent[i] /= 100.0;
					}
				}

				if (optional_string("$Random Sidethrust Percent:")) {
					parse_float_list(profile->random_sidethrust_percent, NUM_SKILL_LEVELS);
					//Percent is nice for modders, but here in the code we want it between 0 and 1.0
					//While we're at it, verify the range
					for (i = 0; i < NUM_SKILL_LEVELS; i++) {
						if (profile->random_sidethrust_percent[i] < 0.0f || profile->random_sidethrust_percent[i] > 100.0f) {
							Warning(LOCATION, "$Random Sidethrust Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->random_sidethrust_percent[i]);
							profile->random_sidethrust_percent[i] = 0.0f;
						}
						profile->random_sidethrust_percent[i] /= 100.0;
					}
				}

				if (optional_string("$Stalemate Time Threshold:"))
					parse_float_list(profile->stalemate_time_thresh, NUM_SKILL_LEVELS);

				if (optional_string("$Stalemate Distance Threshold:"))
					parse_float_list(profile->stalemate_dist_thresh, NUM_SKILL_LEVELS);

				if (optional_string("$Player Shield Recharge Scale:"))
					parse_float_list(profile->shield_energy_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Player Weapon Recharge Scale:"))
					parse_float_list(profile->weapon_energy_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Max Turret Target Ownage:"))
					parse_int_list(profile->max_turret_ownage_target, NUM_SKILL_LEVELS);

				if (optional_string("$Max Turret Player Ownage:"))
					parse_int_list(profile->max_turret_ownage_player, NUM_SKILL_LEVELS);

				if (optional_string("$Percentage Required For Kill Scale:"))
					parse_float_list(profile->kill_percentage_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Percentage Required For Assist Scale:"))
					parse_float_list(profile->assist_percentage_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Percentage Awarded For Capship Assist:"))
					parse_float_list(profile->assist_award_percentage_scale, NUM_SKILL_LEVELS);

				if (optional_string("$Repair Penalty:"))
					parse_int_list(profile->repair_penalty, NUM_SKILL_LEVELS);

				if (optional_string("$Delay Before Allowing Bombs to Be Shot Down:"))
					parse_float_list(profile->delay_bomb_arm_timer, NUM_SKILL_LEVELS);

				if (optional_string("$Chance AI Has to Fire Missiles at Player:"))
					parse_int_list(profile->chance_to_use_missiles_on_plr, NUM_SKILL_LEVELS);

				if (optional_string("$Max Aim Update Delay:"))
					parse_float_list(profile->max_aim_update_delay, NUM_SKILL_LEVELS);

				if (optional_string("$Turret Max Aim Update Delay:"))
					parse_float_list(profile->turret_max_aim_update_delay, NUM_SKILL_LEVELS);

				if (optional_string("$Player Autoaim FOV:"))
				{
					float fov_list[NUM_SKILL_LEVELS];
					parse_float_list(fov_list, NUM_SKILL_LEVELS);
					for (i = 0; i < NUM_SKILL_LEVELS; i++)
					{
						//Enforce range
						if (fov_list[i] < 0.0f || fov_list[i] >= 360.0f)
						{
							Warning(LOCATION, "$Player Autoaim FOV should be >= 0 and < 360.0 (read %f). Setting to 0.", fov_list[i]);
							fov_list[i] = 0.0f;
						}

						//Convert units
						profile->player_autoaim_fov[i] = fov_list[i] * PI / 180.0f;
					}
				}

				if (optional_string("$Detail Distance Multiplier:"))
					parse_float_list(profile->detail_distance_mult, MAX_DETAIL_VALUE + 1);

				set_flag(profile, "$big ships can attack beam turrets on untargeted ships:", AI::Profile_Flags::Big_ships_can_attack_beam_turrets_on_untargeted_ships);

				set_flag(profile, "$smart primary weapon selection:", AI::Profile_Flags::Smart_primary_weapon_selection);

				set_flag(profile, "$smart secondary weapon selection:", AI::Profile_Flags::Smart_secondary_weapon_selection);

				set_flag(profile, "$smart shield management:", AI::Profile_Flags::Smart_shield_management);

				set_flag(profile, "$smart afterburner management:", AI::Profile_Flags::Smart_afterburner_management);

				set_flag(profile, "$free afterburner use:", AI::Profile_Flags::Free_afterburner_use);

				set_flag(profile, "$allow rapid secondary dumbfire:", AI::Profile_Flags::Allow_rapid_secondary_dumbfire);

				set_flag(profile, "$huge turret weapons ignore bombs:", AI::Profile_Flags::Huge_turret_weapons_ignore_bombs);

				set_flag(profile, "$don't insert random turret fire delay:", AI::Profile_Flags::Dont_insert_random_turret_fire_delay);

				set_flag(profile, "$hack improve non-homing swarm turret fire accuracy:", AI::Profile_Flags::Hack_improve_non_homing_swarm_turret_fire_accuracy);

				set_flag(profile, "$shockwaves damage small ship subsystems:", AI::Profile_Flags::Shockwaves_damage_small_ship_subsystems);

				set_flag(profile, "$navigation subsystem governs warpout capability:", AI::Profile_Flags::Navigation_subsys_governs_warp);

				set_flag(profile, "$check communications for non-player ships:", AI::Profile_Flags::Check_comms_for_non_player_ships);

				set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AI::Profile_Flags::No_min_dock_speed_cap);

				set_flag(profile, "$disable linked fire penalty:", AI::Profile_Flags::Disable_linked_fire_penalty);

				set_flag(profile, "$disable player secondary doublefire:", AI::Profile_Flags::Disable_player_secondary_doublefire);

				set_flag(profile, "$disable ai secondary doublefire:", AI::Profile_Flags::Disable_ai_secondary_doublefire);

				set_flag(profile, "$disable weapon damage scaling:", AI::Profile_Flags::Disable_weapon_damage_scaling);

				set_flag(profile, "$use additive weapon velocity:", AI::Profile_Flags::Use_additive_weapon_velocity);

				set_flag(profile, "$use newtonian dampening:", AI::Profile_Flags::Use_newtonian_dampening);

				set_flag(profile, "$include beams for kills and assists:", AI::Profile_Flags::Include_beams_in_stat_calcs);

				set_flag(profile, "$score kills based on damage caused:", AI::Profile_Flags::Kill_scoring_scales_with_damage);

				set_flag(profile, "$score assists based on damage caused:", AI::Profile_Flags::Assist_scoring_scales_with_damage);

				set_flag(profile, "$allow event and goal scoring in multiplayer:", AI::Profile_Flags::Allow_multi_event_scoring);

				set_flag(profile, "$fix linked primary weapon decision bug:", AI::Profile_Flags::Fix_linked_primary_bug);

				set_flag(profile, "$fix ramming stationary targets bug:", AI::Profile_Flags::Fix_ramming_stationary_targets_bug);

				set_flag(profile, "$prevent turrets targeting too distant bombs:", AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);

				set_flag(profile, "$smart subsystem targeting for turrets:", AI::Profile_Flags::Smart_subsystem_targeting_for_turrets);

				set_flag(profile, "$fix heat seekers homing on stealth ships bug:", AI::Profile_Flags::Fix_heat_seeker_stealth_bug);

				set_flag(profile, "$multi allow empty primaries:", AI::Profile_Flags::Multi_allow_empty_primaries);

				set_flag(profile, "$multi allow empty secondaries:", AI::Profile_Flags::Multi_allow_empty_secondaries);

				set_flag(profile, "$allow turrets target weapons freely:", AI::Profile_Flags::Allow_turrets_target_weapons_freely);

				set_flag(profile, "$use only single fov for turrets:", AI::Profile_Flags::Use_only_single_fov_for_turrets);

				set_flag(profile, "$allow vertical dodge:", AI::Profile_Flags::Allow_vertical_dodge);

				set_flag(profile, "$force beam turrets to use normal fov:", AI::Profile_Flags::Force_beam_turret_fov);

				set_flag(profile, "$fix ai class bug:", AI::Profile_Flags::Fix_ai_class_bug);

				set_flag(profile, "$turrets ignore targets radius in range checks:", AI::Profile_Flags::Turrets_ignore_target_radius);

				set_flag(profile, "$no extra collision avoidance vs player:", AI::Profile_Flags::No_special_player_avoid);

				set_flag(profile, "$perform fewer checks for death screams:", AI::Profile_Flags::Perform_fewer_scream_checks);

				set_flag(profile, "$advanced turret fov edge checks:", AI::Profile_Flags::Advanced_turret_fov_edge_checks);

				set_flag(profile, "$require turrets to have target in fov:", AI::Profile_Flags::Require_turret_to_have_target_in_fov);

				set_flag(profile, "$all ships manage shields:", AI::Profile_Flags::All_ships_manage_shields);

				set_flag(profile, "$ai aims from ship center:", AI::Profile_Flags::Ai_aims_from_ship_center);

				set_flag(profile, "$allow primary link at mission start:", AI::Profile_Flags::Allow_primary_link_at_start);

				set_flag(profile, "$allow beams to damage bombs:", AI::Profile_Flags::Beams_damage_weapons);

				set_flag(profile, "$disable weapon damage scaling for player:", AI::Profile_Flags::Player_weapon_scale_fix);

				set_flag(profile, "$countermeasures affect aspect seekers:", AI::Profile_Flags::Aspect_lock_countermeasure);

				set_flag(profile, "$ai guards specific ship in wing:", AI::Profile_Flags::Ai_guards_specific_ship_in_wing);

				set_flag(profile, "$support don't add primaries:", AI::Profile_Flags::Support_dont_add_primaries);

				if (optional_string("$max allies rearming threshold:"))
				 	stuff_int(&profile->max_allies_rearming_threshold);

				set_flag(profile, "$firing requires exact los:", AI::Profile_Flags::Require_exact_los);

				if (optional_string("$exact los minimum detection radius:")) {
					stuff_float(&profile->los_min_detection_radius);
				}

				set_flag(profile, "$fighterbay arrivals use carrier orientation:", AI::Profile_Flags::Fighterbay_arrivals_use_carrier_orient);

				set_flag(profile, "$fighterbay departures use carrier orientation:", AI::Profile_Flags::Fighterbay_departures_use_carrier_orient);

				set_flag(profile, "$debris damage respects 'big damage' flag:", AI::Profile_Flags::Debris_respects_big_damage);

				set_flag(profile, "$don't limit change in speed due to physics whack:", AI::Profile_Flags::Dont_limit_change_in_speed_due_to_physics_whack);

				set_flag(profile, "$guards ignore protected attackers:", AI::Profile_Flags::Guards_ignore_protected_attackers);

				if (optional_string("$ai path mode:"))
				{
					stuff_string(buf, F_NAME, NAME_LENGTH);
					int j = ai_path_type_match(buf);
					if (j >= 0) {
						profile->ai_path_mode = j;
					}
					else {
						Warning(LOCATION, "Invalid ai path mode '%s' specified", buf);
					}
				}

				set_flag(profile, "$no warp camera:", AI::Profile_Flags::No_warp_camera);

				set_flag(profile, "$fix ai path order bug:", AI::Profile_Flags::Fix_ai_path_order_bug);

				set_flag(profile, "$strict turret-tagged-only targeting:", AI::Profile_Flags::Strict_turret_tagged_only_targeting);

				set_flag(profile, "$aspect bomb invulnerability fix:", AI::Profile_Flags::Aspect_invulnerability_fix);

				set_flag(profile, "$glide decay requires thrust:", AI::Profile_Flags::Glide_decay_requires_thrust);

				set_flag(profile, "$ai can slow down when attacking big ships:", AI::Profile_Flags::Ai_can_slow_down_attacking_big_ships);

				set_flag(profile, "$use actual primary range:", AI::Profile_Flags::Use_actual_primary_range);

				if (optional_string("$override radius for subsystem path points:")) {
					int path_radii;
					stuff_int(&path_radii);
					if (path_radii >= Minimum_subsystem_path_pt_dist) {
						profile->subsystem_path_radii = path_radii;
					} else {
						mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used.\n", Minimum_subsystem_path_pt_dist, path_radii));
					}
				}

				set_flag(profile, "$use POF radius for subsystem path points:", AI::Profile_Flags::Use_subsystem_path_point_radii);

				if (optional_string("$bay arrive speed multiplier:")) {
					stuff_float(&profile->bay_arrive_speed_mult);
				}
				if (optional_string("$bay depart speed multiplier:")) {
					stuff_float(&profile->bay_depart_speed_mult);
				}

				// ----------

				// compatibility
				if (optional_string("$perform less checks for death screams:"))
				{
					mprintf(("Warning: \"$perform less checks for death screams\" flag is deprecated in favor of \"$perform fewer checks for death screams\"\n"));
					bool temp;
					stuff_boolean(&temp);
                    profile->flags.set(AI::Profile_Flags::Perform_fewer_scream_checks, temp);
				}
				if (optional_string("$allow primary link delay:"))
				{
					mprintf(("Warning: \"$allow primary link delay\" flag is deprecated in favor of \"$allow primary link at mission start\"\n"));
					bool temp;
					stuff_boolean(&temp);
                    profile->flags.set(AI::Profile_Flags::Allow_primary_link_at_start, !temp);
				}
				if (optional_string("$lead indicator second-order prediction factor:")) {
					stuff_float(&profile->second_order_lead_predict_factor);
					if (profile->second_order_lead_predict_factor > 1 || profile->second_order_lead_predict_factor < 0) {
						mprintf(("Warning: \"$lead indicator second-order prediction factor\" must be 0 - 1, resetting to 0.\"\n"));
						profile->second_order_lead_predict_factor = 0;
					}
				}
				if (optional_string("$ships with no shields can manage ETS:"))
				{
					mprintf(("Warning: \"$ships with no shields can manage ETS\" flag is deprecated in favor of \"$any ship with no shields can manage ETS\"\n"));
					bool temp;
					stuff_boolean(&temp);
                    profile->flags.set(AI::Profile_Flags::All_nonshielded_ships_can_manage_ets, temp);
				}

				// ----------

				set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);

				set_flag(profile, "$respect ship axial turnrate differences:", AI::Profile_Flags::Use_axial_turnrate_differences);

				set_flag(profile, "$any ship with no shields can manage ETS:", AI::Profile_Flags::All_nonshielded_ships_can_manage_ets);

				set_flag(profile, "$fighters/bombers with no shields can manage ETS:", AI::Profile_Flags::Fightercraft_nonshielded_ships_can_manage_ets);

				set_flag(profile, "$ships playing dead don't manage ETS:", AI::Profile_Flags::Ships_playing_dead_dont_manage_ets);

				set_flag(profile, "$better combat collision avoidance for fightercraft:", AI::Profile_Flags::Better_combat_collision_avoidance);

				if (optional_string("+combat collision avoidance aggression for fightercraft:")) {
					stuff_float(&profile->better_collision_avoid_aggression_combat);
				}

				set_flag(profile, "+combat collision avoidance for fightercraft includes target:", AI::Profile_Flags::Better_combat_collision_avoid_includes_target);

				set_flag(profile, "$better guard collision avoidance for fightercraft:", AI::Profile_Flags::Better_guard_collision_avoidance);

				if (optional_string("+guard collision avoidance aggression for fightercraft:")) {
					stuff_float(&profile->better_collision_avoid_aggression_guard);
				}

				set_flag(profile, "$improved missile avoidance for fightercraft:", AI::Profile_Flags::Improved_missile_avoidance);

				if (optional_string_either("$friendly ships use AI profile countermeasure chance:", "$unify usage of AI profile countermeasure chance:", true) >= 0) {
					bool val;
					stuff_boolean(&val);
					profile->flags.set(AI::Profile_Flags::Unify_usage_countermeasure_firechance, val);
				}

				set_flag(profile, "$improved subsystem attack pathing:", AI::Profile_Flags::Improved_subsystem_attack_pathing);

				set_flag(profile, "$fixed ship-weapon collisions:", AI::Profile_Flags::Fixed_ship_weapon_collision);

				//Intention is to expand this feature to include a preference for close or long range weapons
				//hence using something besides a simple flag.
				if (optional_string("$AI secondary range awareness:"))
				{
					stuff_string(buf, F_NAME, NAME_LENGTH);
					int j = ai_secondary_range_select_mode_match(buf);
					if (j >= 0) {
						profile->ai_range_aware_secondary_select_mode = j;
					}
					else {
						Warning(LOCATION, "Invalid ai secondary range awareness mode '%s' specified", buf);
					}
				}

				set_flag(profile, "$no shield damage from ship collisions:", AI::Profile_Flags::No_shield_damage_from_ship_collisions);

				set_flag(profile, "$reset last_hit_target_time for player hits:", AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);

				if (optional_string("$turret target recheck time:"))
				{
					stuff_float(&profile->turret_target_recheck_time);
					if (profile->turret_target_recheck_time < 0) {
						Warning(LOCATION, "Turret target recheck time must be positive.");
						profile->turret_target_recheck_time = 2000.0f;
					}
				}

				set_flag(profile, "$prevent negative turret ammo:", AI::Profile_Flags::Prevent_negative_turret_ammo);

				set_flag(profile, "$fix keep-safe-distance:", AI::Profile_Flags::Fix_keep_safe_distance);

				set_flag(profile, "$AI ignores aspect lock for leading:", AI::Profile_Flags::Ignore_aspect_when_leading);

				set_flag(profile, "$fix good-rearm-time bug:", AI::Profile_Flags::Fix_good_rearm_time_bug);

				set_flag(profile, "$no continuous turn on attack:", AI::Profile_Flags::No_continuous_turn_on_attack);

				set_flag(profile, "$remove-goal properly removes play-dead order:", AI::Profile_Flags::Fixed_removing_play_dead_order);

				set_flag(profile, "$bay arrivals not time limited:", AI::Profile_Flags::Disable_bay_emerge_timeout);

				set_flag(profile, "$use adjusted AI class autoscale:", AI::Profile_Flags::Adjusted_AI_class_autoscale);

				set_flag(profile, "$carry shield difficulty scaling bug:", AI::Profile_Flags::Carry_shield_difficulty_scaling_bug);

				set_flag(profile, "$debris affected by physics whacks:", AI::Profile_Flags::Whackable_debris);

				set_flag(profile, "$asteroids affected by physics whacks:", AI::Profile_Flags::Whackable_asteroids);

				set_flag(profile, "$dynamic goals afterburn hard:", AI::Profile_Flags::Dynamic_goals_afterburn_hard);

				set_flag(profile, "$player orders afterburn hard:", AI::Profile_Flags::Player_orders_afterburn_hard);

				set_flag(profile, "$hud squad messages use tactical disarm/disable:", AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable);

				set_flag(profile, "$align to target when guarding stationary ship:", AI::Profile_Flags::Align_to_target_when_guarding_still);

				if (optional_string("$rotation factor multiplier for player collisions:")) {
					stuff_float(&profile->rot_fac_multiplier_ply_collisions);
				}

				set_flag(profile, "$fix avoid-shockwave bugs:", AI::Profile_Flags::Fix_avoid_shockwave_bugs);

				set_flag(profile, "$fix standard strafe:", AI::Profile_Flags::Fix_standard_strafe);

				set_flag(profile, "$standard strafe used more:", AI::Profile_Flags::Standard_strafe_used_more);

				if (optional_string("$standard strafe triggers under this speed:")) {
					stuff_float(&profile->standard_strafe_when_below_speed);
				}

				if (optional_string("$strafe distance from target bounding box:")) {
					stuff_float(&profile->strafe_retreat_box_dist);
				}

				if (optional_string("$strafe stops after time unhit:")) {
					stuff_float(&profile->strafe_max_unhit_time);
				}

				if (optional_string("$guard uses big-orbit for target radius above:")) {
					stuff_float(&profile->guard_big_orbit_above_target_radius);
				}

				if (optional_string("$guard with big-orbit uses max speed percent:")) {
					float max_percent;
					stuff_float(&max_percent);
					if (max_percent > 0.0f && max_percent <= 1.0f) {
						profile->guard_big_orbit_max_speed_percent = max_percent;
					} else {
						mprintf(("Warning: \"$guard with big-orbit uses max speed percent:\" should be > 0 and <= 1 (read %f). Value will not be used.\n", max_percent));
					}
				}

				if (optional_string("$attack-any idle circle distance:")) {
					stuff_float(&profile->attack_any_idle_circle_distance);
				}

				set_flag(profile, "$unify usage of AI Shield Manage Delay:", AI::Profile_Flags::Unify_usage_ai_shield_manage_delay);

				set_flag(profile, "$fix AI shield management bug:", AI::Profile_Flags::Fix_AI_shield_management_bug);

				set_flag(profile, "$AI balances shields instead of directs when attacked:", AI::Profile_Flags::AI_balances_shields_when_attacked);

				set_flag(profile, "$disable AI transferring energy:", AI::Profile_Flags::Disable_ai_transferring_energy);

				set_flag(profile, "$enable freespace 1 style missile behavior:", AI::Profile_Flags::Freespace_1_missile_behavior);

				set_flag(profile, "$ETS uses ship class power output:", AI::Profile_Flags::ETS_uses_power_output);

				set_flag(profile, "$ETS energy same regardless of system presence:", AI::Profile_Flags::ETS_energy_same_regardless_of_system_presence);

				set_flag(profile, "$don't issue form-on-wing goals at mission start:", AI::Profile_Flags::Dont_form_on_wing_at_mission_start);

				if (optional_string("$default form-on-wing priority:")) {
					int priority;
					stuff_int(&priority);
					if (priority > 0) {
						profile->default_form_on_wing_priority = priority;
					} else {
						mprintf(("Warning: $default form-on-wing priority: should be > 0 (read %d).  Value will not be used.\n", priority));
					}
				}

				set_flag(profile, "$do not clear goals when running form-on-wing:", AI::Profile_Flags::Do_not_clear_goals_when_running_form_on_wing);

				set_flag(profile, "$do not clear goals when running stay-still:", AI::Profile_Flags::Do_not_clear_goals_when_running_stay_still);

				set_flag(profile, "$do not set override when assigning form-on-wing:", AI::Profile_Flags::Do_not_set_override_when_assigning_form_on_wing);

				set_flag(profile, "$purge player-issued form-on-wing after subsequent order:", AI::Profile_Flags::Purge_player_issued_form_on_wing_after_subsequent_order);

				set_flag(profile, "$cancel future waves of any wing launched from an exited ship:", AI::Profile_Flags::Cancel_future_waves_of_any_wing_launched_from_an_exited_ship);


				// end of options ----------------------------------------

				// if we've been through once already and are at the same place, force a move
				if (saved_Mp && (saved_Mp == Mp))
				{
					char tmp[60];
					memset(tmp, 0, 60);
					strncpy(tmp, Mp, 59);
					mprintf(("WARNING: Unrecognized parameter in %s: %s\n", filename, tmp));

					Mp++;
				}

				// find next valid option
				if (!skip_to_start_of_string_either("$", "#"))
					break;
				saved_Mp = Mp;
			}
		}

		required_string("#End");
	}
	catch (const parse::ParseException& e)
	{
		mprintf(("TABLES: Unable to parse '%s'!  Error message = %s.\n", (filename) ? filename : "<default ai_profiles.tbl>", e.what()));
		return;
	}
}

void ai_profiles_init()
{
	int temp;

	if (Ai_profiles_initted)
		return;

	Num_ai_profiles = 0;
	Default_ai_profile = 0;
	Default_profile_name[0] = '\0';

	// init retail entry first
	parse_ai_profiles_tbl(NULL);

	// Allow mission_profiles.tbl to be a alias of ai_profiles.tbl, but only load one or the other.
	// mission_profiles.tbl would be newer so assume intended, but print to the log to be sure - Mjn
	char filename[MAX_FILENAME_LEN] = "ai_profiles.tbl";
	if (cf_exists_full("mission_profiles.tbl", CF_TYPE_TABLES)) {
		mprintf(("mission_profiles.tbl was found! Using that instead of ai_profiles.tbl...\n"));
		strcpy_s(filename, "mission_profiles.tbl");
	}

	// now parse the supplied table (if any)
	if (cf_exists_full(filename, CF_TYPE_TABLES))
		parse_ai_profiles_tbl(filename);

	// parse any modular tables
	parse_modular_table("*-aip.tbm", parse_ai_profiles_tbl);
	parse_modular_table("*-msp.tbm", parse_ai_profiles_tbl);

	// set default if specified
	temp = ai_profile_lookup(Default_profile_name);
	if (temp >= 0)
		Default_ai_profile = temp;

	Ai_profiles_initted = 1;
}

int ai_profile_lookup(char *name)
{
	for (int i = 0; i < Num_ai_profiles; i++)
		if (!stricmp(name, Ai_profiles[i].profile_name))
			return i;

	return -1;
}

void ai_profile_t::reset()
{
    memset(profile_name, 0, sizeof(profile_name));

    flags.reset();

    los_min_detection_radius = 10.0f;
    ai_path_mode = AI_PATH_MODE_NORMAL;
	subsystem_path_radii = 0;
    bay_arrive_speed_mult = 1.0f;
    bay_depart_speed_mult = 1.0f;
	second_order_lead_predict_factor = 0;
	ai_range_aware_secondary_select_mode = AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL;
	turret_target_recheck_time = 2000.0f;
	rot_fac_multiplier_ply_collisions = 0.0f;
	max_allies_rearming_threshold = 2;

	better_collision_avoid_aggression_combat = 3.5f;
	better_collision_avoid_aggression_guard = 3.5f;

	standard_strafe_when_below_speed = 3.0f;
	strafe_retreat_box_dist = 300.0f;
	strafe_max_unhit_time = 20.0f;

	guard_big_orbit_above_target_radius = 500.0f;
	guard_big_orbit_max_speed_percent = 1.0f;
	attack_any_idle_circle_distance = 100.0f;

	default_form_on_wing_priority = 99;	// as originally assigned in ai_add_goal_sub_sexp()

    for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
        max_incoming_asteroids[i] = 0;
        max_allowed_player_homers[i] = 0;
        max_attackers[i] = 0;
        predict_position_delay[i] = 0;
        in_range_time[i] = 0;
        shield_manage_delay[i] = 0;

        link_energy_levels_always[i] = 0;
        link_energy_levels_maybe[i] = 0;

        link_ammo_levels_always[i] = 0;
        link_ammo_levels_maybe[i] = 0;
        primary_ammo_burst_mult[i] = 0;

        cmeasure_life_scale[i] = 0;
        cmeasure_fire_chance[i] = 0;
        weapon_energy_scale[i] = 0;
        shield_energy_scale[i] = 0;
        afterburner_recharge_scale[i] = 0;
        player_damage_scale[i] = 0;
        player_damage_inflicted_scale[i] = 0;

        subsys_damage_scale[i] = 0;
        beam_friendly_damage_cap[i] = -1.f;
        weapon_friendly_damage_cap[i] = -1.f;
        weapon_self_damage_cap[i] = -1.f;
        turn_time_scale[i] = 0;
        glide_attack_percent[i] = 0;
        circle_strafe_percent[i] = 0;
        glide_strafe_percent[i] = 0;
        random_sidethrust_percent[i] = 0;
        stalemate_time_thresh[i] = 0;
        stalemate_dist_thresh[i] = 0;
        max_aim_update_delay[i] = 0;
        turret_max_aim_update_delay[i] = 0;

        ship_fire_delay_scale_hostile[i] = 0;
        ship_fire_delay_scale_friendly[i] = 0;

        ship_fire_secondary_delay_scale_hostile[i] = 0;
        ship_fire_secondary_delay_scale_friendly[i] = 0;

        max_turret_ownage_target[i] = 0;
        max_turret_ownage_player[i] = 0;

        kill_percentage_scale[i] = 0;
        assist_percentage_scale[i] = 0;

        assist_award_percentage_scale[i] = 0;

        repair_penalty[i] = 0;
        delay_bomb_arm_timer[i] = 0;
        chance_to_use_missiles_on_plr[i] = 0;
        player_autoaim_fov[i] = 0;
    }

    for (int i = 0; i <= MAX_DETAIL_VALUE; ++i) {
        detail_distance_mult[i] = 0;
    }

	// via Github #2332, enable bugfixes if we are targeting version 20 and up
	if (mod_supports_version(20, 0, 0)) {
		flags.set(AI::Profile_Flags::Huge_turret_weapons_ignore_bombs);
		flags.set(AI::Profile_Flags::Fix_linked_primary_bug);
		flags.set(AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);
		flags.set(AI::Profile_Flags::Fix_heat_seeker_stealth_bug);
		flags.set(AI::Profile_Flags::Allow_vertical_dodge);
		flags.set(AI::Profile_Flags::Fix_ai_class_bug);
		flags.set(AI::Profile_Flags::Ai_guards_specific_ship_in_wing);
		flags.set(AI::Profile_Flags::Fix_ai_path_order_bug);
		flags.set(AI::Profile_Flags::Aspect_invulnerability_fix);
		flags.set(AI::Profile_Flags::Use_actual_primary_range);
		flags.set(AI::Profile_Flags::Fightercraft_nonshielded_ships_can_manage_ets);
	}
	// this flag has been enabled ever since 3.7.2
	if (mod_supports_version(3, 7, 2)) {
		flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
	}
	// and this flag has been enabled ever since 3.6.10
	if (mod_supports_version(3, 6, 10)) {
		flags.set(AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
	}
	// backwards compatible flag for a bug accidentally introduced during this time
	if (mod_supports_version(3, 6, 14) && !mod_supports_version(23, 1, 0)) {
		flags.set(AI::Profile_Flags::Carry_shield_difficulty_scaling_bug);
	}
	if (mod_supports_version(21, 4, 0)) {
		flags.set(AI::Profile_Flags::Fixed_ship_weapon_collision);
	}
	if (mod_supports_version(22, 0, 0)) {
		flags.set(AI::Profile_Flags::Fighterbay_arrivals_use_carrier_orient);
		flags.set(AI::Profile_Flags::Prevent_negative_turret_ammo);
		flags.set(AI::Profile_Flags::Fix_keep_safe_distance);
	}
	if (mod_supports_version(23, 0, 0)) {
		flags.set(AI::Profile_Flags::Fix_good_rearm_time_bug);
		flags.set(AI::Profile_Flags::No_continuous_turn_on_attack);
		flags.set(AI::Profile_Flags::Fixed_removing_play_dead_order);
	}
	if (mod_supports_version(23, 2, 0)) {
		flags.set(AI::Profile_Flags::Ships_playing_dead_dont_manage_ets);
	}
	if (mod_supports_version(23, 4, 0)) {
		flags.set(AI::Profile_Flags::Hudsquadmsg_tactical_disarm_disable);
	}
	if (mod_supports_version(24, 2, 0)) {
		flags.set(AI::Profile_Flags::Debris_respects_big_damage);
		flags.set(AI::Profile_Flags::Force_beam_turret_fov);
		flags.set(AI::Profile_Flags::Guards_ignore_protected_attackers);
	}
	if (mod_supports_version(25, 0, 0)) {
		flags.set(AI::Profile_Flags::Fix_avoid_shockwave_bugs);
		flags.set(AI::Profile_Flags::Purge_player_issued_form_on_wing_after_subsequent_order);
		flags.set(AI::Profile_Flags::Cancel_future_waves_of_any_wing_launched_from_an_exited_ship);
	}
}
