/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <time.h>
#include "pm.h"
#include "sounds.h"
#include "inventory.h"
#include "../util/prng.h"

#define damage_IMPORT
#include "damage.h"


static long
selectSystem(void)
/*
	Randomly select a subsystem to receive damage. Each subsystem has its
	own probability to be damaged, the total probabbility being, obviously,
	100%.

	Return: damaged subsystem mask.
*/
{

	double    r;
	long      i;

	r = prng_getDouble();
	if (r < 0.25) /* 25% */
		i = SYS_ENGINE1;
	else if (r < 0.35) /* 10% */
		i = SYS_RADAR;
	else if (r < 0.40) /* 5% */
		i = SYS_TEWS;
	else if (r < 0.45) /* 5% */
		i = SYS_HYD1;
	else if (r < 0.50) /* 5% */
		i = SYS_HYD2;
	else if (r < 0.53) /* 3% */
		i = SYS_GEN1;
	else if (r < 0.56) /* 3% */
		i = SYS_GEN2;
	else if (r < 0.61) /* 5% */
		i = SYS_FLAPS;
	else if (r < 0.69) /* 8% */
		i = SYS_SPEEDBRAKE;
	else if (r < 0.78) /* 9% */
		i = SYS_FUEL;
	else if (r < 0.85) /* 7% */
		i = SYS_HUD;
	else if (r < 0.90) /* 5% */
		i = SYS_LEFTMAIN;
	else if (r < 0.95) /* 5% */
		i = SYS_RIGHTMAIN;
	else if (r < 0.97) /* 2% */
		i = SYS_WINGS;
	else /* 3% */
		i = SYS_NOSEGEAR;
	return i;
}


/**
 * Set damage to the specified subsystem. If the subsystem is already
 * damaged does nothing, but if it is a fuel leak (SYS_FUEL) then
 * increments the current fuel leak rate. Also accounts for failures to
 * other subsystems that depends on this one.
 */
static void
damageSystem(craft * c, long sys)
{
	if ((c->damageBits & sys) == 0 || (sys == SYS_FUEL)) {

		c->damageBits |= sys;

		switch (sys) {

		case SYS_ENGINE1:
			c->throttle = 0;
			break;

		case SYS_RADAR:
			c->curRadarTarget = -1;
			break;

		case SYS_FUEL:

			/*
			 *  Fuel leaks can be up to 40 pounds per second here.
			 */

			c->leakRate += 40.0 * prng_getDouble();
			break;

		case SYS_HYD1:
		case SYS_HYD2:
			if ( c->damageBits & SYS_HYD1 && c->damageBits & SYS_HYD2 ) {
				c->damageBits |= SYS_SPEEDBRAKE;
				c->damageBits |= SYS_FLAPS;
			}
			break;

		case SYS_GEN1:
		case SYS_GEN2:
			if ( c->damageBits & SYS_GEN1 && c->damageBits & SYS_GEN2 ) {
				c->damageBits |= (SYS_HUD | SYS_RADAR | SYS_TEWS);
			}
			break;
		}

	}
}

#define DAMAGE_DEBUG 0

/**
 * Target craft hit with d points of damage. Every point has 15% of
 * probability to damage some subsystem, and 85% of probability to be
 * absorbed by the aircraft frame. On structural damage, decrements
 * target->structurePts.
 * @param target Hit target aircraft.
 * @param d Points of damage.
 * @return True if the damage has been adsorbed and the target can still live;
 * this means that human players can only die falling to the ground after a
 * severe structural damage.
 * False only if the craft is a drone and all the target->structurePts are
 * exhausted, then the drone should be killed immediately.
 */
static _BOOL
damage_absorbDamage(craft * target, int d)
{

	double  n, x;
	long    sys;
	
	if( DAMAGE_DEBUG )  printf("damage_absorbDamage, %d points\n", d);

	/*
	 *  Actual damage sustained is adjusted by a damage factor that forms a
	 *  bell curve centered around 0.75 * d.
	 */

	x = 0.5*(prng_getDouble() + prng_getDouble()) + 0.25;

	d = (int) (d * x + 0.5);

	if (d > 0) {
		sounds_playSound(target, sounds_Explosion, FALSE);
	}

	for (; d > 0; --d) {

		/*
		 *  For each damage point absorbed, there is a 15 percent chance that
		 *  it will be absorbed by some subsystem other than the actual
		 *  airframe.
		 */

		if (prng_getDouble() <= 0.15) {
			if( DAMAGE_DEBUG )  printf("damage_absorbDamage: subsystem damaged\n");
			sys = selectSystem();
			damageSystem(target, sys);
		}

		/*
		 *  For each point absorbed by the airframe, there is a 20% chance that
		 *  it'll be absorbed by the wing and induce a rolling moment or a 10 
		 *  percent chance that it will hit a horizontal stabilizer and induce
		 *  a pitching and rolling moment.
		 */

		else {

			if ((n = prng_getDouble()) <= 0.20) {
				if( DAMAGE_DEBUG )  printf("damage_absorbDamage: damaged CL\n");
				target->damageCL += (prng_getDouble() - 0.5) * 0.20;
			}
			else if (n <= 0.30) {
				if( DAMAGE_DEBUG )  printf("damage_absorbDamage: damaged CL, CM\n");
				target->damageCL += (prng_getDouble() - 0.5) * 0.10;
				target->damageCM += (prng_getDouble() - 0.5) * 0.20;
			}

			if( target->structurePts <= 1 ){
				target->structurePts = 0;
				damageSystem(target, SYS_WINGS);
				if( target->type == CT_PLANE )
					return TRUE;  /* human players fall to ground :-) */
				else
					return FALSE; /* destroy immediately drones */
			}
			if( DAMAGE_DEBUG )  printf("damage_absorbDamage: some structural damage\n");
			target->structurePts--;
		}

	}

	return TRUE;

}

int
damage_absorbDISDamage(craft * target,
				dis_entity_type *warhead_dis_type, 
				u_short warhead_type,
				u_short fuze_type,
				double distance_meters,
				double velocity_meters_per_sec,
				double *explosion_diameter_meters)
{
	int i;
	int damage_points;
	munition_map *pmm;

	*explosion_diameter_meters = 0.0;

	for(i = 0; ; i++){
		pmm = inventory_getMunition(i);
		if( pmm == NULL )
			break;
		if (dis_entityWildcardMatch(warhead_dis_type, &pmm->entity_type, &pmm->entity_mask)) {
			if (pmm->warhead_mask == 0 || pmm->warhead_type == warhead_type) {
				
				if( DAMAGE_DEBUG )  printf("damage_absorbDISDamage: hit by %s\n", dis_entityTypeToString(&pmm->entity_type));

				/* found a match; assess damage */

				if (pmm->kinetic_flag) {
					/* Kinetic warhead: */
					damage_points = (int) (0.5 * pmm->damage_factor *
						velocity_meters_per_sec * velocity_meters_per_sec + 0.5);
					if( DAMAGE_DEBUG )  printf("damage_absorbDISDamage: kinetic, damage factor %g, velocity %g m/s\n", pmm->damage_factor, velocity_meters_per_sec);
				} else {
					/* Blast warhead: */
					damage_points = (int) ( pmm->damage_factor / 
						(distance_meters * distance_meters + 1.0) + 0.5 );
					if( DAMAGE_DEBUG )  printf("damage_absorbDISDamage: blast, damage factor %g, distance %g m\n", pmm->damage_factor, distance_meters);
				}

				*explosion_diameter_meters = pmm->explosion_diameter_meters;

				return damage_absorbDamage(target, damage_points);
			}
		}
	}

	printf ("Warning: munition entity lookup failed\n");
	return 1;
}

void
damage_reset(craft * c)
{
	c->damageBits = c->cinfo->damageBits;
	c->structurePts = c->cinfo->structurePts;
	c->leakRate = 0.0;
	c->damageCL = 0.0;
	c->damageCM = 0.0;
}
