/*
 *  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 <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "../V/Vlib.h"
#include "sounds.h"
#include "damage.h"
#include "dis_if.h"
#include "draw.h"
#include "pm.h"
#include "../util/error.h"
#include "../util/units.h"

#define radar_IMPORT
#include "radar.h"

#define RADAR_WINDOW_WIDTH  200

#define scanSlope 	2.1445

#define SetSegment(s, xa, ya, xb, yb) {s.x1=u->indicator.a.x+(xa); \
	s.x2=u->indicator.a.x+(xb); s.y1=u->indicator.a.y+(ya); s.y2=u->indicator.a.y+(yb); }


#ifdef FIXME_NOT_USED

typedef enum (
	RM_OFF,         /* radar is off */
	RM_STANDBY,     /* standby */
	RM_NORMAL,      /* track while scan */
	RM_ACM,         /* 20x30 acm */
	RM_STT          /* single target track */
) radar_mode;

typedef struct _radar_data {
	struct _radar_data *next;

	_BOOL  enabled;

	radar_mode mode;

	Alib_Segment   radarImage[1024]; /* the last radar frame */
	int       radarImageCount;

} radar_data;

static radar_data *free_list = NULL;

#endif


static char *thirty = "30";
static Alib_Point tgt[4];
static _BOOL tgt_valid = FALSE;
static int rftw, rfth, slot_size;


/**
 * Calculates the matrix to rotate from geocentric to radar coordinates, so
 * that the center of the radar beam is the x axis. Azimuth and elevation of the
 * radar beam are referred to the aircraft system; for the record, in ACM they
 * are always zero, so the the radar beam of our aircraft is always directed
 * as the x axis.
 * @param c Emitter.
 * @param XYZtoRadar Here returns the calculated rotation from geocentric to
 * radar.
 * @param el_center Elevation, up positive (RAD).
 * @param az_center Azimuth, left positive (RAD).
 */
static void
GenerateWorldToRadarMatrix(craft * c, VMatrix * XYZtoRadar,
	double el_center, double az_center)
{
	earth_LatLonAlt *w = &c->w;
	VMatrix   m, ABCtoNED;

	VIdentMatrix(&m);

	VRotate(&m, ZRotation, -w->longitude);
	VRotate(&m, YRotation, w->latitude);
	VRotate(&m, YRotation, units_DEGtoRAD(90.0));

	VRotate(&m, YRotation, el_center);
	VRotate(&m, ZRotation, -az_center);
	VMatrixTranspose(&c->trihedral, &ABCtoNED);
	VMatrixMultByRank(&m, &ABCtoNED, XYZtoRadar, 3);
}


static void
plotNormalTarget(viewer * u, int x, int y)
{

	SetSegment(u->radarImage[u->radarImageCount], x - 2, y - 2, x - 2, y + 2);
	u->radarImageCount++;
	SetSegment(u->radarImage[u->radarImageCount], x - 2, y + 2, x + 2, y + 2);
	u->radarImageCount++;
	SetSegment(u->radarImage[u->radarImageCount], x + 2, y + 2, x + 2, y - 2);
	u->radarImageCount++;
	SetSegment(u->radarImage[u->radarImageCount], x + 2, y - 2, x - 2, y - 2);
	u->radarImageCount++;
}


static void
plotPrimaryTarget(craft * c, viewer * u, int i, int x, int y)
{

	int       xp, yp, interline;
	char      s[16], lr;
	VPoint    tmp, rel, deltaV;

	double    d, cl, targetAspectAngle;

	xp = u->indicator.a.x + slot_size * 6 / 8;
	yp = u->indicator.a.y + slot_size * 5 / 8;
	interline = (int) (1.5 * rfth + 0.5);

	tgt_valid = TRUE;
	tgt[0].x = u->indicator.a.x + x;
	tgt[0].y = u->indicator.a.y + y - 4;
	tgt[1].x = u->indicator.a.x + x + 4;
	tgt[1].y = u->indicator.a.y + y;
	tgt[2].x = u->indicator.a.x + x;
	tgt[2].y = u->indicator.a.y + y + 4;
	tgt[3].x = u->indicator.a.x + x - 4;
	tgt[3].y = u->indicator.a.y + y;
	//Alib_fill3DPolygon(u->v->w, tgt, 4, &u->rz);
	Alib_fillPolygon(u->w, tgt, 4, radarColor);

/*
 *  Heading of target
 */

	sprintf(s, "  %3.3d", (int) (units_RADtoDEG(ptbl[i].curHeading)));
	VGetStrokeString(u->v, xp, yp,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

/*
 *  Groundspeed of target
 */

	rel.x = ptbl[i].Cg.x;
	rel.y = ptbl[i].Cg.y;
	rel.z = 0.0;
	sprintf(s, "%3.3d K", (int) (VMagnitude(&rel) / units_NmToFeetFactor * 3600.0));
	yp += interline;
	VGetStrokeString(u->v, xp, yp,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

/*
 *  Relative heading to target.
 */

	tmp.x = units_METERStoFEET(ptbl[i].Sg.x - c->Sg.x);
	tmp.y = units_METERStoFEET(ptbl[i].Sg.y - c->Sg.y);
	tmp.z = units_METERStoFEET(ptbl[i].Sg.z - c->Sg.z);
	VTransform_(&tmp, &c->XYZtoNED, &rel);
	targetAspectAngle = ptbl[i].curHeading - pm_heading(&rel);
	if (targetAspectAngle > 0.0) {
		lr = 'R';
	}
	else {
		lr = 'L';
		targetAspectAngle = -targetAspectAngle;
	}

	sprintf(s, "%3.3d %c", (int) (units_RADtoDEG(targetAspectAngle) + 0.5), lr);
	yp += interline;
	VGetStrokeString(u->v, xp, yp,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

/*
 *  Closure rate
 */

	deltaV.x = ptbl[i].Cg.x - c->Cg.x;
	deltaV.y = ptbl[i].Cg.y - c->Cg.y;
	deltaV.z = ptbl[i].Cg.z - c->Cg.z;
	d = VMagnitude(&rel);
	cl = -(deltaV.x * rel.x + deltaV.y * rel.y + deltaV.z * rel.z) / d;
	c->targetDistance = d;
	c->targetClosure = cl;
	yp += interline;
	sprintf(s, "%3.3d", (int) (cl / units_NmToFeetFactor * 3600.0));
	VGetStrokeString(u->v, xp, yp,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

/*
 *  Range to target
 */

#define XA	40
#define XB	150

	xp = XA * slot_size / RADAR_WINDOW_WIDTH;
	yp = rfth + 4;
	sprintf(s, "%d", (int) (d / units_NmToFeetFactor));
	VGetStrokeString(u->v, xp + u->indicator.a.x, yp + u->indicator.a.y,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

/*
 *  Altitude of target
 */

	xp = XB * slot_size / RADAR_WINDOW_WIDTH;
	yp = rfth + 4;
	sprintf(s, "%d", (int) (units_METERStoFEET(ptbl[i].w.z) / 1000.0));
	VGetStrokeString(u->v, xp + u->indicator.a.x, yp + u->indicator.a.y,
					 u->radarImage, &u->radarImageCount,
					 s, strlen(s), rfth);

	sprintf(c->rightHUD[0], "R_+__");
	sprintf(c->rightHUD[1], "%05.1f", d / units_NmToFeetFactor);
	sprintf(c->rightHUD[2], " %4.4d", (int) (cl / units_NmToFeetFactor * 3600.0));
}


static double
radarFrameInterval(craft * c)
{
	double    dt = 1.0;

	switch (c->radarMode) {
	case RM_OFF:
	case RM_STANDBY:
		dt = 1.0;
		break;
	case RM_NORMAL:
		dt = 1.0;
		break;
	case RM_STT:
		dt = 1.0;
		break;
	}
	return dt;
}


/**
 * Add information about a target that this aircraft's radar can "see".
 */
static void
addRadarInfo(craft * emitter, craft * target, double d)
{
	if (emitter->rtop < 16) {
		emitter->rinfo[emitter->rtop].rel = emitter->relPos[target->pIndex];
		emitter->rinfo[emitter->rtop].d = d;
		emitter->rinfo[emitter->rtop].targetID = target->pIndex;
		emitter->rinfo[emitter->rtop].beamID = 0;
		emitter->rinfo[emitter->rtop].locked = 0;
		++emitter->rtop;
	}
}


/**
 * Compute the radar energy arriving at the target and if the target is visible.
 * @param emitter Emitter aircraft.
 * @param target Possible target.
 * @param XYZtoRadar Rotation from geocentric to radar system.
 * @param el_ratio Emitter beam, tangent of the half sweep angle height.
 * @param az_ratio Emitter beam, tangent of the half sweep angle width.
 * @param energy Emitter energy irradiated in the sweep cone (W).
 */
static void
computeRadarEnergy(craft * emitter, craft * target, VMatrix * XYZtoRadar,
	double el_ratio, double az_ratio, double energy)
{
	VPoint    r1;
	double    xs, ys, d;

/*
 *  Calculate the coordinates of the target relative to the craft's frame
 *  and the position of the radar search volume.
 */

	r1.x = units_METERStoFEET(target->Sg.x - emitter->Sg.x);
	r1.y = units_METERStoFEET(target->Sg.y - emitter->Sg.y);
	r1.z = units_METERStoFEET(target->Sg.z - emitter->Sg.z);

	VTransform(&r1, XYZtoRadar, &emitter->relPos[target->pIndex]);

	target->rval[emitter->pIndex] = 0.0;

	if (emitter->relPos[target->pIndex].x <= 0.0)
		return;

	xs = emitter->relPos[target->pIndex].y / emitter->relPos[target->pIndex].x;
	ys = emitter->relPos[target->pIndex].z / emitter->relPos[target->pIndex].x;

/*
 *  If the target is inside the radiation cone, then we are painting this target
 *  with radar energy.  Estimate the value of the energy that the target sees.
 */

	if ((fabs(xs) <= az_ratio) && (fabs(ys) <= el_ratio)) {

		d = VMagnitude(&emitter->relPos[target->pIndex]);

		target->rval[emitter->pIndex] = energy / (4.0 * M_PI * d * d);

/*
 *  Can we detect that ?
 */

/*  return_energy = target->rval[emitter->pIndex] / (4.0 * M_PI * d * d); */

/*
 *  Lookdown?
 */

		if (ys > 0.0) {
			d *= 4.0 / 3.0;
		}

		/*
		 *  Reject targets that would not appear on our radar set
		 */

		/*
		 *  Not a DIS platform?
		 */

		if (target->cinfo->entityType.kind != DISKindPlatform) {
			return;
		}

		/*
		 *  Ground-based entity?  Ignore it.
		 */

		if (target->cinfo->entityType.kind == DISKindPlatform &&
			target->cinfo->entityType.domain == DISDomainLand) {
			return;
		}

/*
 *  For now, compute radar visibility based on fixed ranges.  A better
 *  solution would include factoring estimated target radar cross sections,
 *  antenna size and radar set sensitivity.  Wow. Where would you get that
 *  sort of information?
 */

		if (d >= emitter->cinfo->radarDRange) {
			return;
		}
		else if (d >= emitter->cinfo->radarTRange) {
			addRadarInfo(emitter, target, d);
			return;
		}
		else {
			addRadarInfo(emitter, target, d);
			return;
		}
	}
	else {
		return;
	}
}


void
radar_calculateEmissions(craft * emitter)
{
	VMatrix   m1;
	register int i, j, target_idx;
	double    el_center, az_center, el_width, az_width, el_ratio, az_ratio, e;
	craft    *target;
	
	/* Reset no. of possible targets the emitter can see: */
	emitter->rtop = 0;
	
	/* Reset energy the emitter puts on each other player: */
	for (i = 0; i < manifest_MAXPLAYERS; ++i) {
		ptbl[i].rval[emitter->pIndex] = 0.0;
	}
	
	/*
	 * Check if this emitter actually... emits anything! (save time otherwise).
	 * - CT_DIS_PLANE may emit, but must ask to dis_if for emission PDU.
	 * - CT_DIS_STEALTH I don't know what is it, but it was here in the original
	 *   code. FIXME: check if CT_DIS_STEALTH does really emits radar beam.
	 * - CT_DRONE are always attacking!
	 * - CT_PLANE only if radar on.
	 */
	if ( !(
		emitter->type == CT_DIS_PLANE 
		|| emitter->type == CT_DIS_STEALTH
		|| emitter->type == CT_DRONE
		|| (
			/* local plane with radar turned on: */
			emitter->type == CT_PLANE
			&& (emitter->radarMode & (RM_NORMAL | RM_ACM | RM_STT))
		)
	)) {
		return;
	}

	/* Ask dis_if how many radar beams emits (always max 1 for ACM entities): */
	i = dis_if_getBeamCount(emitter);

	/*
	 * For each beam, compute:
	 * 1. How much energy the emitter puts on each other player.
	 * 2. Which other player the emitter can see.
	 */
	for (j = 0; j < i; ++j) {
		dis_if_getRadarParameters(emitter, j, &az_center, &az_width,
				&el_center, &el_width, &e);

/*
 * In the original ACM program, the dis_if.c module sets the full sweep angles
 * and uses degrees for them; in this version we fixed that and we set the
 * half angle in radiants. For compatibility with the original program any
 * angle greater than 89*M_PI/180 RAD is assumed using the original values:
 */
		
		if( az_width <= 89*M_PI/180 && el_width <= 89*M_PI/180 ){
			az_ratio = tan(az_width);
			el_ratio = tan(el_width);
		} else {
			az_ratio = tan(units_DEGtoRAD(az_width/2));
			el_ratio = tan(units_DEGtoRAD(el_width/2));
		}

/*
 *  Note: the energy value returned by DIS for EM emissions is in units of
 *        dBm.  e_dBm = 10.0 * log10 (e_watts / 0.001).
 *
 *  Sooo, to convert to watts, we'd do this:
 *
 *  e_watts = 1000.0 * exp10(e_dBm / 10.0);
 */

/*
 * "Transmission Test Procedures for DIS Compliance: Distributed  Emission
 *  Regeneration Protocol Family" says that the elevation value is
 *  up-positive, and azimuth is left-positive; units in radians (!)
 */

		GenerateWorldToRadarMatrix(emitter, &m1, el_center, az_center);

		for (target = ptbl, target_idx = 0; target_idx < manifest_MAXPLAYERS; ++target_idx, ++target) {
			
			if (target->type == CT_FREE
			|| target->type == CT_DIS_STEALTH
			|| emitter == target)
				continue;
			
			computeRadarEnergy(emitter, target, &m1, el_ratio, az_ratio, e);
		}
	}
}


void
radar_update(craft * c, viewer * u)
{
	register radarInfo *p;
	Alib_Window  *w;
	int       i, x, y, j = -1, state;
	int       xc, yc;
	double    xs, ys, dmax;
	char     *buf;
	Alib_Segment *seg;
	int       primary = 0;
	_BOOL     time_up = FALSE;
	Alib_Rect      rect;

	if ( (c->radarMode & (RM_NORMAL | RM_ACM | RM_STT )) == 0 )
		return;

	rftw = (int) (0.025 * RectWidth(u->indicator) + 0.5);
	rfth = (int) (1.3 * rftw + 0.5);
	slot_size = RectWidth(u->indicator);

	w = u->w;
	Alib_setClipRect(w, &u->indicator);

/*
 *  Compute radar emissions
 */

	seg = u->radarImage;

/*
 *  Are we ready to give the pilot the next frame ? If not, mark the
 *  radar display as an "Unchanged" area, and return.
 */

	if (curTime >= c->nextRadarTime) {
		time_up = TRUE;
	}

/*
 *  If we fall through to here, then a redraw is required.
 *  There are four possible scenarios:
 *
 *  1) The frame redraw timer has expired.
 *     An entire radar frame, including target information,
 *     must be generated and displayed.  A logical copy
 *     of the image is saved for possible future use.
 *
 *  2) The frame redraw timer has not expired, but some
 *     external event has triggered a frame redraw (e.g.
 *     the window was uncovered after being hidden).
 *     In this case, the frame is redrawn from the
 *     information used to generate the last frame.
 *
 *  3) The frame redraw timer has not expired, but
 *     the user changed radar modes.  In this case, we should
 *     redraw just the legend information (i.e. no targets),
 *     save a copy of what was drawn, and reset the frame
 *     timer.
 *
 *  4) If none of the first three cases are true, then simply
 *     mark the screen region associated with the radar display
 *     as unchanged, and return.
 */

	state = 4;

	if (time_up) {
		state = 1;
	}
	else {
		if (c->flags & FL_RADAR_MODE_CHANGE) {
			state = 3;
		}
	}

	/*
	 *  Case 4; nothing interesting happens.  Mark the display as unchanged
	 */

	if (state == 4) {
		/****
			FIXME This code should prevent the updating of the screen
			area, but it does not work because it erases the slot to its
			right (gear+flap) for some obscure reason, probably a bug in
			V/SLDiff.c.	 Skip and continue with the regular redraw.

		ZInfo z;
		z.depth = --w->depth;
		z.color = UnchangedColor;
		AlibFillRectangle(u->w, &rect, UnchangedColor);
		return;
		****/

		state = 1; /* full redraw */
	}

	c->flags &= ~FL_RADAR_MODE_CHANGE;

	if (state == 2) {

		Alib_fillRect(w, &rect, panelBackgroundColor);
		
		VDrawSegments(u->v,
			u->radarImage, u->radarImageCount, radarColor);

		if (tgt_valid) {
			Alib_fillPolygon(u->w, tgt, 4, radarColor);
		}
		return;
	}

	tgt_valid = FALSE;

/*
 *  Verify that the current target is still visible.
 */

	if (c->curRadarTarget != -1) {
		for (i = 0, p = c->rinfo; i < c->rtop; ++i, ++p) {
			if (c->curRadarTarget == p->targetID) {
				p->locked = 1;
				j = i;
				break;
			}
		}
		if (j == -1) {
			c->curRadarTarget = -1;
			dis_if_radarTargetChanged(c);
		}
	}

/*
 *  Go find the closest radar return ... that becomes our victim.
 */

	if (state == 1) {
		dmax = 1000.0 * units_NmToFeetFactor;

		if (c->curRadarTarget == -1) {
			for (i = 0, p = c->rinfo, j = -1; i < c->rtop; ++i, ++p) {
				if (p->d < dmax) {
					dmax = p->d;
					j = i;
				}
			}
			if (j != -1) {
				c->curRadarTarget = c->rinfo[j].targetID;
				c->rinfo[j].locked = 1;
				dis_if_radarTargetChanged(c);
				sounds_playSound(c, sounds_APGLockAcquired, FALSE);
			}
		}
	}

	xc = (slot_size + 1) / 2;
	yc = (slot_size + 1) / 2;

/*
 *  Fill background
 */

	Alib_fillRect(w, &u->indicator, panelBackgroundColor);

/*
 *  Draw reference "thing" and the border
 */

	u->radarImageCount = 0;
	SetSegment(seg[u->radarImageCount], xc, yc, xc - 5, yc + 5);
	u->radarImageCount++;
	SetSegment(seg[u->radarImageCount], xc - 10, yc, xc - 5, yc + 5);
	u->radarImageCount++;
	SetSegment(seg[u->radarImageCount], xc - 10, yc, xc - 18, yc);
	u->radarImageCount++;
	SetSegment(seg[u->radarImageCount], xc, yc, xc + 5, yc + 5);
	u->radarImageCount++;
	SetSegment(seg[u->radarImageCount], xc + 10, yc, xc + 5, yc + 5);
	u->radarImageCount++;
	SetSegment(seg[u->radarImageCount], xc + 10, yc, xc + 18, yc);
	u->radarImageCount++;

/*
 *  Radar dead?
 */

	if (damage_isFunctioning(c, SYS_RADAR) == 0) {
		Alib_drawSegments(w, seg, u->radarImageCount, radarColor);
		return;
	}

	/*
	 *  Radar off?
	 */

	if (c->radarMode <= RM_STANDBY) {
		if (c->radarMode == RM_OFF)
			buf = "RADAR OFF";
		else
			buf = "RADAR STBY";
		c->curRadarTarget = -1;
		VGetStrokeString(u->v, u->indicator.a.x + rftw * 4,
						 u->indicator.a.y + rfth * 3,
					seg, &u->radarImageCount, buf, strlen(buf), rfth);
		return;
	}

	c->nextRadarTime = curTime + radarFrameInterval(c);

/*
 *  Show acm and stt HACK
 */

	if (c->radarMode == RM_ACM) {
		buf = "ACM 20x30";
		VGetStrokeString(u->v, u->indicator.a.x + rftw * 4,
			u->indicator.a.y + rfth * 3,
			seg, &u->radarImageCount, buf, strlen(buf), rfth);
	}
	else if (c->radarMode == RM_STT) {
		buf = "STT";
		VGetStrokeString(u->v, u->indicator.a.x + rftw * 4,
			u->indicator.a.y + rfth * 3,
			seg, &u->radarImageCount, buf, strlen(buf), rfth);
	}

/*
 *  Plot reference lines
 */

	i = (slot_size + 3) / 4;
	y = rfth + 3;
	SetSegment(seg[u->radarImageCount], xc, y, xc, y + 4);
	u->radarImageCount++;
	y = slot_size - 10 - rfth;
	SetSegment(seg[u->radarImageCount], xc, y, xc, y + 4);
	u->radarImageCount++;

	x = xc - i;
	y = rfth + 6;
	SetSegment(seg[u->radarImageCount], x, y, x, y + 4);
	u->radarImageCount++;
	y = slot_size - 10 - rfth;
	SetSegment(seg[u->radarImageCount], x, y, x, y + 4);
	u->radarImageCount++;
	VGetStrokeString(u->v, x - rftw + u->indicator.a.x, y + 6 + rfth + u->indicator.a.y,
		seg, &u->radarImageCount, thirty, 2, rfth);

	x = xc + i;
	y = rfth + 6;
	SetSegment(seg[u->radarImageCount], x, y, x, y + 4);
	u->radarImageCount++;
	y = slot_size - 10 - rfth;
	SetSegment(seg[u->radarImageCount], x, y, x, y + 4);
	u->radarImageCount++;
	VGetStrokeString(u->v, x - rftw + u->indicator.a.x, y + 6 + rfth + u->indicator.a.y,
		seg, &u->radarImageCount, thirty, 2, rfth);

	i = (slot_size + 3) / 4;
	x = 2;
	SetSegment(seg[u->radarImageCount], x, yc, x + 4, yc);
	u->radarImageCount++;
	x = slot_size - 6;
	SetSegment(seg[u->radarImageCount], x, yc, x + 4, yc);
	u->radarImageCount++;

	x = 5 + 2 * rftw;
	y = yc - i;
	SetSegment(seg[u->radarImageCount], x, y, x + 4, y);
	u->radarImageCount++;
	x = 3;
	VGetStrokeString(u->v, x + u->indicator.a.x, y + (rfth + 1) / 2 + u->indicator.a.y,
		seg, &u->radarImageCount, thirty, 2, rfth);
	x = slot_size - 6;
	SetSegment(seg[u->radarImageCount], x, y, x + 4, y);
	u->radarImageCount++;

	x = 5 + 2 * rftw;
	y = yc + i;
	SetSegment(seg[u->radarImageCount], x, y, x + 4, y);
	u->radarImageCount++;
	x = 3;
	VGetStrokeString(u->v, x + u->indicator.a.x, y + (rfth + 1) / 2 + u->indicator.a.y,
		seg, &u->radarImageCount, thirty, 2, rfth);
	x = slot_size - 6;
	SetSegment(seg[u->radarImageCount], x, y, x + 4, y);
	u->radarImageCount++;

/*
 *  If the user was simply changing radar modes skip looking
 *  for new targets ..
 */

	if (state != 3) {

/*
 *  Now plot all targets visible to the radar system.
 */


/*
 *  Scan the radar information table and plot targets
 */

		for (i = 0, p = c->rinfo; i < c->rtop; ++i, ++p) {
			if (p->rel.x >= 0.0) {
				xs = p->rel.y / (p->rel.x * scanSlope);
				ys = p->rel.z / (p->rel.x * scanSlope);
				if (fabs(xs) <= 1.0 && fabs(ys) <= 1.0) {
					x = (int) (xs * (double) xc + xc);
					y = (int) (ys * (double) yc + yc);
					if (p->locked) {
						plotPrimaryTarget(c, u, p->targetID, x, y);
						primary = 1;
					}
					else {
						plotNormalTarget(u, x, y);
					}
				}
			}
		}
	}

	if (primary == 0) {
		strcpy(c->rightHUD[0], "R_+__");
		strcpy(c->rightHUD[1], "");
		strcpy(c->rightHUD[2], "");
	}

	Alib_drawSegments(w, u->radarImage, u->radarImageCount, radarColor);

	return;
}

/*
 *  Get a new radar target.
 */

int
radar_getNewTarget(craft * c)
{
	int       i, j = -1;
	radarInfo *p;

	if (c->curRadarTarget == -1) {
		return -1;
	}

/*
 *  Locate the locked target in the list of visible targets.
 */

	for (i = 0, p = c->rinfo; i < c->rtop; ++i, ++p) {
		if (c->curRadarTarget == p->targetID) {
			j = i;
			p->locked = 0;
			break;
		}
	}
	if (j == -1) {
		return -1;
	}
	++j;
	i = (j == c->rtop) ? 0 : j;
	c->rinfo[i].locked = 1;
	return c->rinfo[i].targetID;
}

void
radar_updateTEWS(craft * c, viewer * u)
{
	Alib_Window  *w;
	int       i, x, y, hostile_found = 0;
	VPoint    rel, tmp;
	double    r, r1, m, unit, xo, yo;
	draw_Type *dd;

	if ( (c->radarMode & (RM_STANDBY | RM_NORMAL | RM_ACM | RM_STT)) == 0
	&& c->radarMode != RM_OFF )
		return;
	
	slot_size = RectWidth(u->indicator);

	w = u->v->w;
	Alib_setClipRect(w, &u->tuner);
	Alib_fillRect(w, &u->tuner, panelBackgroundColor);

	if ( c->radarMode == RM_OFF )
		return;

	r = 0.45 * slot_size;
	r1 = 0.06 * r;
	xo = u->tuner.a.x + 0.5*slot_size;
	yo = u->tuner.a.y + 0.5*slot_size;

	dd = draw_new();

	draw_circle(dd, xo, yo, r);

	if( damage_isFunctioning(c, SYS_TEWS) ){
		for (i = 0; i < manifest_MAXPLAYERS; ++i) {

			if (ptbl[i].type == CT_FREE || c->pIndex == i)
				continue;

			if (c->rval[i] > c->cinfo->TEWSThreshold) {
				VTransform(&ptbl[i].Sg, &c->XYZtoNED, &tmp);
				VReverseTransform_(&tmp, &c->trihedral, &rel);
				m = 1.0/VMagnitude(&rel);
				rel.x *= m;
				rel.y *= m;
				rel.z *= m;
				unit = sqrt(rel.x * rel.x + rel.y * rel.y);
				if (unit == 0.0) {
					rel.x = 1.0;
					rel.y = 0.0;
				}
				x = (int) (xo + (int) (rel.y / unit * r * 0.7));
				y = (int) (yo - (int) (rel.x / unit * r * 0.7));
				if (c->force == ptbl[i].force) {

					draw_circle(dd, x, y, r1);

				}
				else {
					
					// Draw filled circle:
					//draw_Type *ee;
					//ee = draw_new();
					//draw_circle(ee, x, y, r1);
					//draw_fill(ee, u->v, HUDPixel);
					//draw_free(ee);
					
					/* FIXME: draw_fill() does not actually fill! */
					/* Workaround: draw 3 concentric circles (close enough!): */
					draw_circle(dd, x, y, r1);
					draw_circle(dd, x, y, 0.66*r1);
					draw_circle(dd, x, y, 0.33*r1);

					hostile_found = 1;
				}
			}
		}

		if (c->vl == u && hostile_found == 1) {
			sounds_playSound(c, sounds_LockWarning, FALSE);
		}
	}

	draw_stroke(dd, u->v, HUDColor);

	draw_free(dd);

}


void
radar_droneUpdate(craft * c)
{
	int       i, j = -1;
	radarInfo *p;
	double    dmax = 500.0 * units_NmToFeetFactor;
	VPoint    tmp, rel, deltaV;

/*
 *  Go find the closest radar return ... that becomes our victim.
 */

	if (c->curRadarTarget == -1) {
		for (i = 0, p = c->rinfo; i < c->rtop; ++i, ++p) {
			if (ptbl[p->targetID].force != c->force && p->d < dmax) {
				dmax = p->d;
				j = i;
			}
		}
		c->curRadarTarget = j;
		dis_if_radarTargetChanged(c);
	}

/*
 *  Is the current radar target still visible to the radar set?
 */

	else {
		for (i = 0, p = c->rinfo; i < c->rtop; ++i, ++p) {
			if (c->curRadarTarget == p->targetID) {
				j = i;
			}
		}
		if (j == -1) {
			c->curRadarTarget = -1;
			dis_if_radarTargetChanged(c);
		}
	}

/*
 *  Compute tracking parameters
 */

	if ((i = c->curRadarTarget) != -1) {
		tmp.x = units_METERStoFEET(ptbl[i].Sg.x - c->Sg.x);
		tmp.y = units_METERStoFEET(ptbl[i].Sg.y - c->Sg.y);
		tmp.z = units_METERStoFEET(ptbl[i].Sg.z - c->Sg.z);
		VTransform_(&tmp, &c->XYZtoNED, &rel);
		deltaV.x = ptbl[i].Cg.x - c->Cg.x;
		deltaV.y = ptbl[i].Cg.y - c->Cg.y;
		deltaV.z = ptbl[i].Cg.z - c->Cg.z;
		c->targetDistance = VMagnitude(&rel);
		c->targetClosure = -(deltaV.x * rel.x +
				deltaV.y * rel.y + deltaV.z + rel.z) / c->targetDistance;
	}
}


void
radar_setMode(craft * c, int mode)
{
	int       dis_mode;

	switch (mode) {
	case RM_OFF:
		dis_mode = 0;
		break;
	case RM_STANDBY:
		dis_mode = 0;
		break;
	case RM_NORMAL:
		dis_mode = 1;
		break;
	case RM_ACM:
		dis_mode = 2;
		break;
	case RM_STT:
		dis_mode = 3;
		break;
	case RM_HSI:
	case RM_ADF:
	case RM_DIS_BROWSE:
		dis_mode = 0;
		break;
	default:
		error_internal("invalid mode %d", mode);
	}
	
	if( dis_mode == 0 ){
		c->rightHUD[0][0] = '\0';
		c->rightHUD[1][0] = '\0';
		c->rightHUD[2][0] = '\0';
	} else {
		strcpy(c->rightHUD[0], "R_+__");
		c->rightHUD[1][0] = '\0';
		c->rightHUD[2][0] = '\0';
	}

	c->radarMode = mode;
	c->nextRadarTime = curTime + radarFrameInterval(c);
	c->flags |= FL_RADAR_MODE_CHANGE;
	c->curRadarTarget = -1;
	dis_if_setRadarMode(c, dis_mode, 1);
}

