/*
 * Species_Defs.CPP
 * Extended Species Support for FS2 Open
 *
 * You may not sell or otherwise commercially exploit the source or things you
 * create based on the source.
 *
 */


#include "cfile/cfile.h"
#include "def_files/def_files.h"
#include "iff_defs/iff_defs.h"
#include "localization/localize.h"
#include "parse/parselo.h"
#include "ship/shipfx.h"
#include "species_defs/species_defs.h"

extern int parse_warp_params(const WarpParams *inherit_from, WarpDirection direction, const char *info_type_name, const char *info_name, bool set_supercap_warp_physics = false);

SCP_vector<species_info> Species_info;

//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// This function parses the data from the species_defs.tbl
// Names only - actual loading is done elsewhere

void parse_thrust_anims(species_info *species, bool no_create)
{
	if (!no_create)
	{
		required_string("$ThrustAnims:");

		generic_anim_init(&species->thruster_info.flames.normal, NULL);
		generic_anim_init(&species->thruster_info.flames.afterburn, NULL);
		generic_bitmap_init(&species->thruster_secondary_glow_info.normal, NULL);
		generic_bitmap_init(&species->thruster_secondary_glow_info.afterburn, NULL);
		generic_bitmap_init(&species->thruster_tertiary_glow_info.normal, NULL);
		generic_bitmap_init(&species->thruster_tertiary_glow_info.afterburn, NULL);
		generic_bitmap_init(&species->thruster_distortion_info.normal, NULL);
		generic_bitmap_init(&species->thruster_distortion_info.afterburn, NULL);
	}
	else if (!optional_string("$ThrustAnims:"))
	{
		return;
	}

	// favor new style
	if (no_create)
	{
		if (optional_string("+Pri_Normal:") || optional_string("+Normal:"))
			stuff_string(species->thruster_info.flames.normal.filename, F_NAME, MAX_FILENAME_LEN);
	}
	else
	{
		if (!optional_string("+Pri_Normal:"))
			required_string("+Normal:");

		stuff_string(species->thruster_info.flames.normal.filename, F_NAME, MAX_FILENAME_LEN);
	}

	// if no primary thruster anim is wanted then clear it
	if ( !VALID_FNAME(species->thruster_info.flames.normal.filename) )
		generic_anim_init(&species->thruster_info.flames.normal, NULL);

	// and again
	if (no_create)
	{
		if (optional_string("+Pri_Afterburn:") || optional_string("+Afterburn:"))
			stuff_string(species->thruster_info.flames.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
	}
	else
	{
		if (!optional_string("+Pri_Afterburn:"))
			required_string("+Afterburn:");

		stuff_string(species->thruster_info.flames.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
	}

	// if no primary thruster anim is wanted then clear it
	if ( !VALID_FNAME(species->thruster_info.flames.afterburn.filename) )
		generic_anim_init(&species->thruster_info.flames.afterburn, NULL);

	// extra thruster stuff, bah
	if (optional_string("+Sec_Normal:"))
		stuff_string(species->thruster_secondary_glow_info.normal.filename, F_NAME, MAX_FILENAME_LEN);

	// etc.
	if (optional_string("+Sec_Afterburn:"))
		stuff_string(species->thruster_secondary_glow_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);

	// etc.
	if (optional_string("+Ter_Normal:"))
		stuff_string(species->thruster_tertiary_glow_info.normal.filename, F_NAME, MAX_FILENAME_LEN);

	// etc.
	if (optional_string("+Ter_Afterburn:"))
		stuff_string(species->thruster_tertiary_glow_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
	
	// etc.
	if (optional_string("+Dist_Normal:"))
		stuff_string(species->thruster_distortion_info.normal.filename, F_NAME, MAX_FILENAME_LEN);

	// etc.
	if (optional_string("+Dist_Afterburn:"))
		stuff_string(species->thruster_distortion_info.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
}

void parse_thrust_glows(species_info *species, bool no_create)
{
	if (!no_create)
	{
		required_string("$ThrustGlows:");

		generic_anim_init(&species->thruster_info.glow.normal, NULL);
		generic_anim_init(&species->thruster_info.glow.afterburn, NULL);
	}
	else if (!optional_string("$ThrustGlows:"))
	{
		return;
	}

	if (no_create)
	{
		if (optional_string("+Normal:"))
			stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN);
	}
	else
	{
		required_string("+Normal:");
		stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN);
	}

	// if no glow is wanted then clear it
	if ( !VALID_FNAME(species->thruster_info.glow.normal.filename) )
		generic_anim_init(&species->thruster_info.glow.normal, NULL);

	if (no_create)
	{
		if (optional_string("+Afterburn:"))
			stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
	}
	else
	{
		required_string("+Afterburn:");
		stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN);
	}

	// if no glow is wanted then clear it
	if ( !VALID_FNAME(species->thruster_info.glow.afterburn.filename) )
		generic_anim_init(&species->thruster_info.glow.afterburn, NULL);
}

void parse_species_tbl(const char *filename)
{
	char species_name[NAME_LENGTH];

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

		reset_parse();


		// start parsing
		required_string("#SPECIES DEFS");

		// no longer required: counted automatically
		if (optional_string("$NumSpecies:"))
		{
			int temp;
			stuff_int(&temp);
		}

		// begin reading data
		while (required_string_either("#END", "$Species_Name:"))
		{
			bool no_create = false;
			species_info *species, new_species;

			species = &new_species;

			// Start Species - Get its name
			required_string("$Species_Name:");
			stuff_string(species_name, F_NAME, NAME_LENGTH);

			if (optional_string("+nocreate"))
			{
				no_create = true;

				int i = species_info_lookup(species_name);
				if (i >= 0)
					species = &Species_info[i];
			}
			else
			{
				strcpy_s(species->species_name, species_name);
			}

			// Goober5000 - IFF
			if (optional_string("$Default IFF:"))
			{
				bool iff_found = false;
				char temp_name[NAME_LENGTH];
				stuff_string(temp_name, F_NAME, NAME_LENGTH);

				// search for it in iffs
				for (int iLoop = 0; iLoop < (int)Iff_info.size(); iLoop++)
				{
					if (!stricmp(Iff_info[iLoop].iff_name, temp_name))
					{
						species->default_iff = iLoop;
						iff_found = true;
					}
				}

				if (!iff_found)
				{
					species->default_iff = 0;
					Warning(LOCATION, "Species %s default IFF %s not found in iff_defs.tbl!  Defaulting to %s.\n", species->species_name, temp_name, Iff_info[species->default_iff].iff_name);
				}
			}
			else if (!no_create)
			{
				// we have no idea which it could be, so default to 0
				species->default_iff = 0;

				// let them know
				Warning(LOCATION, "$Default IFF not specified for species %s in species_defs.tbl!  Defaulting to %s.\n", species->species_name, Iff_info[species->default_iff].iff_name);
			}

			// Goober5000 - FRED color
			if (optional_string("$FRED Color:") || optional_string("$FRED Colour:"))
			{
				stuff_int_list(species->fred_color.a1d, 3, ParseLookupType::RAW_INTEGER_TYPE);
			}
			else if (!no_create)
			{
				// set defaults to Volition's originals
				if (!stricmp(species->species_name, "Terran"))
				{
					species->fred_color.rgb.r = 0;
					species->fred_color.rgb.g = 0;
					species->fred_color.rgb.b = 192;
				}
				else if (!stricmp(species->species_name, "Vasudan"))
				{
					species->fred_color.rgb.r = 0;
					species->fred_color.rgb.g = 128;
					species->fred_color.rgb.b = 0;
				}
				else if (!stricmp(species->species_name, "Shivan"))
				{
					species->fred_color.rgb.r = 192;
					species->fred_color.rgb.g = 0;
					species->fred_color.rgb.b = 0;
				}
				else if (!stricmp(species->species_name, "Ancients") || !stricmp(species->species_name, "Ancient"))
				{
					species->fred_color.rgb.r = 192;
					species->fred_color.rgb.g = 0;
					species->fred_color.rgb.b = 192;
				}
				else
				{
					species->fred_color.rgb.r = 0;
					species->fred_color.rgb.g = 0;
					species->fred_color.rgb.b = 0;
				}

				// let them know
				Warning(LOCATION, "$FRED Color not specified for species %s in species_defs.tbl!  Defaulting to (%d, %d, %d).\n", species->species_name, species->fred_color.rgb.r, species->fred_color.rgb.g, species->fred_color.rgb.b);
			}

			// stuff
			optional_string("$MiscAnims:");

			// Get its Debris Texture
			if ((!no_create && required_string("+Debris_Texture:")) || optional_string("+Debris_Texture:"))
			{
				generic_bitmap_init(&species->debris_texture, NULL);
				stuff_string(species->debris_texture.filename, F_NAME, MAX_FILENAME_LEN);
			}


			// Shield Hit Animation
			if (optional_string("+Shield_Hit_ani:")) // Shouldn't be required -- LPine
			{
				generic_anim_init(&species->shield_anim, NULL);
				stuff_string(species->shield_anim.filename, F_NAME, MAX_FILENAME_LEN);
			}
			else if (!no_create)
			{
				species->shield_anim.filename[0] = '\0';
				species->shield_anim.first_frame = -1; // Landmine to trip up anyone who does end up using this
			}


			// Thruster Anims
			parse_thrust_anims(species, no_create);

			// Thruster Glow Anims
			parse_thrust_glows(species, no_create);


			// Goober5000 - AWACS multiplier
			if (optional_string("$AwacsMultiplier:"))
			{
				stuff_float(&species->awacs_multiplier);
			}
			else if (!no_create)
			{
				// set defaults to Volition's originals
				if (!stricmp(species->species_name, "Vasudan"))
					species->awacs_multiplier = 1.25f;
				else if (!stricmp(species->species_name, "Shivan"))
					species->awacs_multiplier = 1.50f;
				else
					species->awacs_multiplier = 1.0f;

				// let them know
				Warning(LOCATION, "$AwacsMultiplier not specified for species %s in species_defs.tbl!  Defaulting to %.2f.\n", species->species_name, species->awacs_multiplier);
			}

			// Goober5000 - countermeasure type
			// (we won't be able to resolve it until after we've parsed the weapons table)
			if (optional_string("$Countermeasure type:"))
				stuff_string(species->cmeasure_name, F_NAME, NAME_LENGTH);

			// ditto for support ships - naomimyselfandi
			if (optional_string("$Support ship:"))
				stuff_string(species->support_ship_name, F_NAME, NAME_LENGTH);

			if (optional_string("$Borrows Briefing Icons from:")) {
				char temp_name[NAME_LENGTH];
				stuff_string(temp_name, F_NAME, NAME_LENGTH);
				int idx = species_info_lookup(temp_name);
				if (idx >= 0)
					species->borrows_bii_index_species = idx;
				else {
					Warning(LOCATION, "Species %s for '$Borrows Briefing Icons from' in Species %s is either invalid or not yet parsed."
									  "The Species doing the borrowing must be defined after the Species it is borrowing from\n", temp_name, species->species_name);
				}
			}

			if (optional_string("$Borrows Flyby Sounds from:")) {
				char temp_name[NAME_LENGTH];
				stuff_string(temp_name, F_NAME, NAME_LENGTH);
				int idx = species_info_lookup(temp_name);
				if (idx >= 0) {
					species->borrows_flyby_sounds_species = idx;
				} else {
					Warning(LOCATION, "Species %s for '$Borrows Flyby Sounds from' in Species %s is either invalid or not yet parsed."
									  "The Species doing the borrowing must be defined after the Species it is borrowing from\n", temp_name, species->species_name);
				}
			}

			// get species parameters for warpin and warpout
			// Note: if the index is not -1, we must have already assigned warp parameters, probably because we are now
			// parsing a TBM.  In that case, inherit from ourselves.
			species->warpin_params_index = parse_warp_params(species->warpin_params_index >= 0 ? &Warp_params[species->warpin_params_index] : nullptr, WarpDirection::WARP_IN, "Species", species->species_name);
			species->warpout_params_index = parse_warp_params(species->warpout_params_index >= 0 ? &Warp_params[species->warpout_params_index] : nullptr, WarpDirection::WARP_OUT, "Species", species->species_name);

			// don't add new entry if this is just a modified one
			if (!no_create)
				Species_info.push_back(std::move(new_species));
		}

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

int Species_initted = 0;

void species_init()
{
	if (Species_initted)
		return;

	Species_info.clear();


	if (cf_exists_full("species_defs.tbl", CF_TYPE_TABLES))
		parse_species_tbl("species_defs.tbl");
	else
		parse_species_tbl(NULL);

	parse_modular_table("*-sdf.tbm", parse_species_tbl);


	Species_initted = 1;
}

int species_info_lookup(const char *species_name)
{
	for (int i = 0; i < static_cast<int>(Species_info.size()); i++)
	{
		if (!stricmp(Species_info[i].species_name, species_name))
			return i;
	}

	return -1;
}
