/*
 *  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 <math.h>
#include "pm.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#define MAX_MAPPED_STRING_LEN	20
#define MAX_POPUP_STRING_LEN	40
#include <stdio.h>
#include "dis.h"

#define REF_X		-1.3
#define REF_Y		-1.1

extern int debug;

/*  There's a bug lurking here, but for now ... */

#ifdef WIN32
#define SCALE_1		5.0
#else
#define SCALE_1		7.0
#endif

struct _dis_browse {
	char  info[32];
	Entity_t  *p;
	craftType *cinfo;   /* craftType iff this an aircraft we can grab */
};

#define BROWSE_MAX 256
#define ITEM_LIMIT 5
#define LINE_SPACING 0.2

static struct _dis_browse browse_info[BROWSE_MAX];
static int bcount;

int controlRequestCallback( dis_pdu *p, void *pu );

/* from drone.c */
extern endGameDistanceCheck ( char *, char * );

/*
 *  Generate the stealth browsing table from the current DIS
 *  entity database
 */

void
buildBrowseInfoTable ()
{
	Entity_t * e = dis_getEntityTable(), *ep;
	long etop = dis_getEntityTop();
	int i=0;
	craftType * cinfo;
	char *marker;

	ep = e;
	bcount = 0;

	/*
	 *  Update the list of entities we might be interested in following
	 */

	for (i = 0; i <= etop && bcount < BROWSE_MAX; ++i, ++ep) {
		if (ep->local != -1 && 
			ep->entityType.kind == DISKindPlatform &&
			ep->entityType.domain == DISDomainAir) {
			browse_info[bcount].p = ep;

			/*
			 *  Was this an aircraft type defined in the inventory file?
			 *  If so, mark it as "flyable".
			 */

			cinfo = lookupCraftByEntityType( &ep->entityType );
			if (cinfo && cinfo->CLift) {
				browse_info[bcount].cinfo = cinfo;
				marker = "* ";
			}
			else {
				browse_info[bcount].cinfo = NULL;
				marker = "  ";
			}

			sprintf (browse_info[bcount].info, "%s%d,%d,%d",
					 marker,
					 ep->entityId.sim_id.site_id, 
					 ep->entityId.sim_id.application_id,
					 ep->entityId.entity_id
					 );
			++ bcount;
		}
	}
}

/*
 *  Display the stealth browser page in the MFD
 */

void
doBrowsePage(craft * c, viewer * u)
{
	XSegment  seg[2048], m_seg[256];
	char buf[256], buf1[256];
	XRectangle rect[1];
	int       m_i = 0, i = 0, xc, yc, h, x, y;
	int       xscale, yscale, xoffset;
	int item_count;
	static ZInfo z, zm;
	double yy;


	if (c->radarMode != RM_DIS_BROWSE)
		return;

	rect[0].x = u->rx;
	rect[0].y = u->ry;
	rect[0].width = u->radarWidth;
	rect[0].height = u->radarWidth;

	z.depth = --u->v->depth;
	z.color = (Color) (u->v->pixel[radarBackgroundColor->cIndex]);
	zm.depth = z.depth;
	zm.color = (Color) (u->v->pixel[HSIMagentaColor->cIndex]);
	FillRectangle(u->v->w, u->rx, u->ry,
				  u->radarWidth, u->radarWidth, &z);

	xoffset = (int) ((-15.0 * u->scaleFactor) + 0.5);
	xc = u->rx + (u->radarWidth + 1) / 2;
	yc = u->ry + (u->radarWidth + 1) / 2;

	xscale = (int) (u->v->Scale.x / (SCALE_1 * 4));
	yscale = (int) (u->v->Scale.y / (SCALE_1 * 4));

	h = (int) (11.0 * u->scaleFactor + 0.5);
	y = (int) ((REF_Y + yy) * u->v->Scale.y / (SCALE_1 * 4));

	yy = 0.0;

	/*
	 *  Update the list of entities we might be interested in following
	 */

	buildBrowseInfoTable ();

	/*
	 *  display current DIS entity browsing page
	 */

	item_count = 0;
	for (i=u->browseBase; 
		 i<bcount && item_count<ITEM_LIMIT; 
		 ++i, ++item_count) {
		strcpy(buf, browse_info[i].info);
		x = (int) (REF_X * u->v->Scale.x / (SCALE_1 * 4));
		y = (int) ((REF_Y + yy) * u->v->Scale.y / (SCALE_1 * 4));
		if (u->browseSelectedItem == i) {
			VDrawStrokeString(u->v, x + xc, y + yc,
							  buf,
							  strlen(buf), h, &zm);
		}
		else {
			VDrawStrokeString(u->v, x + xc, y + yc,
							  buf,
							  strlen(buf), h, &u->z);
		}
		yy += LINE_SPACING;
	}

	u->v->w->clip.x1 = rect[0].x;
	u->v->w->clip.y1 = rect[0].y;
	u->v->w->clip.x2 = rect[0].x + rect[0].width - 1;
	u->v->w->clip.y2 = rect[0].y + rect[0].height - 1;

	VDrawSegments(u->v, m_seg, m_i, 
				  (Color) (u->v->pixel[HSIMagentaColor->cIndex]));

	VDrawSegments(u->v, seg, i, 
				  (Color) (u->v->pixel[HUDPixel]));

	u->v->w->clip.x1 = 0;
	u->v->w->clip.y1 = 0;
	u->v->w->clip.x2 = u->v->w->width - 1;
	u->v->w->clip.y2 = u->v->w->height - 1;

	return;
}

void
selectCockpitItem( craft *c, 
				   viewer *u, 
				   int x, 
				   int y, 
				   unsigned long time
				   )
{
	double dx, dy, yscale;
	int item;
	int xc, yc;

	xc = u->rx + (u->radarWidth + 1) / 2;
	yc = u->ry + (u->radarWidth + 1) / 2;

	yscale = (int) (u->v->Scale.y / (SCALE_1 * 4));

	if (c->radarMode != RM_DIS_BROWSE) {
		return;
	}

	/*
	 * Click on radar set?
	 */

	if (x > u->rx && x < u->rx+u->radarWidth &&
		y > u->ry && y < u->ry + u->radarWidth) {

		/* get index of selected item */

		dx = (x - yc) / yscale;
		dy = (y - yc) / yscale;
		dy -= REF_Y;
		item = (int) ( dy / LINE_SPACING ) + u->browseBase;

		if (item < bcount && item >= 0) {

			/*
			 *  Double-Click?  Activate control request
			 */

			if (u->browseSelectedItem == item &&
				time - u->browseClickTime < 500) {

#ifdef USE_REQUEST_CONTROL

				/*
				 * We can only take control of aircraft that we have a
				 * definition for.  When that's the case for a given
				 * entity, cinfo will be non-NULL.
				 */

				if (browse_info[item].cinfo) {

					/*
					 * If we're in end-game mode, check for hostile
					 * aircraft in our proximity.  Calling 
					 * endGameDistanceCheck once will cause it to be called
					 * once per second.
					 */

					u->viewer_state = ViewerStateControlPending;

					if ( end_game_mode ) {
						 browse_info[item].p->c->flags |= FL_END_GAME_DRONE;
						 endGameDistanceCheck ((char *) browse_info[item].p->c,
											   (char *) u );
					}
					else {
						dis_requestControl ( browse_info[item].p,
											 controlRequestCallback, 
											 u );
					}
				}
				else {
					XBell( u->dpy, 50 );
				}
#else
				XBell( u->dpy, 50 );
#endif

			}
			else {

				stealthCraft ( browse_info[item].p->c, u, item, 0 );

			}
		}

	}

	u->browseClickTime = time;
}

/*
 * Switch attention to the designated aircraft
 */

int
stealthCraft ( craft *c, viewer *u, int item, int take_control)
{
	int i;

	/*
	 *  Locate the browse info entry that corresponds to the
	 *  designated craft.
	 */

	if ( item == -1 ) {

		buildBrowseInfoTable ();

		for(i=0; i<bcount; ++i) {
			if (browse_info[i].p->c == c) {
				item = i;
				break;
			}
		}
	}

	/* follow that aircraft */
	if ( item != -1 ) {
		u->browseSelectedItem = item;
	}
	else {
		u->browseSelectedItem = -1;
	}

	if ( end_game_mode && take_control && item != -1 ) {
		browse_info[item].p->c->flags |= FL_END_GAME_DRONE;
		u->viewer_state = ViewerStateControlPending;
		endGameDistanceCheck ((char *) browse_info[item].p->c,
							  (char *) u );
	}
	else {
		u->viewer_state = ViewerStatePiggyback;
	}
	u->watchedCraft = c;

	return 0;
}

craft *
locateCraftByDISEntityID ( dis_entity_id *id )
{
	Entity_t *e = dis_getEntityTable();
	long top = dis_getEntityTop();
	long i;
	craft *result = NULL;

	for (i=0; i<=top; ++i) {
		if ( e->entityId.sim_id.site_id == id->sim_id.site_id &&
			 e->entityId.sim_id.application_id == id->sim_id.application_id &&
			 e->entityId.entity_id == id->entity_id ) {
			result = e->c;
			break;
		}
		++ e;
	}

	return result;

}

#ifdef USE_REQUEST_CONTROL

/*
 *  This callback is invoked by the DIS code when we receive a grant
 *  control PDU that we've been waiting for.
 */

int
controlRequestCallback( dis_pdu *pdu, void *pu )
{
	viewer *u = (viewer *) pu;
	Entity_t * e = dis_getEntityTable(), *ep;
	dis_simulation_addr my_addr;
	dis_entity_id new_entity_id;
	DISxApplicationInfo *app;
	craft *c;

	if ( pdu->hdr.pdu_type == PDUTypeAcknowledge &&
		 pdu->acknowledge.resp_flag == 1) {

	/*
	 * "take over the craft"
	 *
	 * alter the viewer entry to reflect that we've hijacked an aircraft
	 *
	 * alter the ptbl (craft) entry to reflect that this is now an aircraft
	 * that we are responsible for modeling
	 */

		u->viewer_state = ViewerStateNormal;
		c = u->watchedCraft;
		u->watchedCraft = u->c;
		u->c = c;

		c->type = CT_PLANE;
		c->vl = u;

		c->radarMode = RM_STANDBY;

		/*
		 *  Until we can think of a better way to set fuel state,
		 *  damage bits, etc. this will have to suffice.
		 */

		(*c->cinfo->resupply) (c);

		/*
		 *  Set up the radios
		 */

		initRadio(c, &c->navReceiver[0]);
		initRadio(c, &c->navReceiver[1]);
		c->hsiSelect = &c->navReceiver[0];

		ep = &e[c->disId];

		ep->local = 1;

		app = dis_getApplicationInfo();
		DISxGetSimulationAddress ( app, &my_addr );

		/* SITE ID */

		if ((transferEntityIdBits & 0x4)) {
			new_entity_id.sim_id.site_id = my_addr.site_id;
		}
		else {
			new_entity_id.sim_id.site_id = 
				ep->entityId.sim_id.site_id;
		}

		/* APPLICATION ID */

		if ((transferEntityIdBits & 0x2)) {
			new_entity_id.sim_id.application_id = my_addr.application_id;
		}
		else {
			new_entity_id.sim_id.application_id = 
				ep->entityId.sim_id.application_id;
		}

		/* ENTITY ID */

		if ((transferEntityIdBits & 0x1)) {

			/*  Issue a new entity id (good within this application) */

			dis_entity_id temp_id;

			DISxIssueEntityID( app, &temp_id );
			new_entity_id.entity_id = temp_id.entity_id;
		}
		else {

			/* use existing entity ID */
			new_entity_id.entity_id = ep->entityId.entity_id;
		}

		/* TODO: check for collisions in entity table if mode wasn't 0, or 7 */
		ep->entityId = new_entity_id;

	}

	/*
     *  Transfer Control Request was rejected.  Return to stealth state.
     */

	else {
		u->viewer_state = ViewerStatePiggyback;
		XBell( u->dpy, 50 );
	}

	return 0;
}

#endif

int
doBrowseKeyEvent(craft * c, viewer * u, XEvent * ev, int player)
{

	KeySym    keysym;
	XComposeStatus compose;
	char      buffer[MAX_MAPPED_STRING_LEN];
	int       buflen = MAX_MAPPED_STRING_LEN;

#ifdef SPECIAL_KEYS
	FILE     *fp;
	craft     pentry;

#endif

	(void) XLookupString((XKeyEvent *) ev, buffer, buflen,
						 &keysym, &compose);

	if (player) {

		switch (keysym) {

#ifdef sun
		case XK_R7:
#else
		case XK_Home:
#endif

			break;

		case XK_Prior:
			u->browseBase -= ITEM_LIMIT;
			if (u->browseBase < 0) {
				u->browseBase = 0;
				XBell ( u->dpy, 50 );
			}
			break;

		case XK_Next:
			u->browseBase += ITEM_LIMIT;
			if (u->browseBase >= bcount) {
				u->browseBase = bcount - ITEM_LIMIT + 1;
			}
			if (u->browseBase < 0) {
				u->browseBase = 0;
				XBell ( u->dpy, 50 );
			}
			break;

#ifdef sun
		case XK_Up:
#else
		case XK_KP_8:
#endif
			u->viewDirection.x = 1.0;
			u->viewDirection.y = 0.0;
			u->viewDirection.z = 0.0;
			u->viewUp.x = 0.0;
			u->viewUp.y = 0.0;
			u->viewUp.z = -1.0;
			break;

/* look right */

#ifdef sun
		case XK_Right:
#else
		case XK_KP_6:
#endif
			u->viewDirection.x = 0.0;
			u->viewDirection.y = 1.0;
			u->viewDirection.z = 0.0;
			u->viewUp.x = 0.0;
			u->viewUp.y = 0.0;
			u->viewUp.z = -1.0;
			break;

/* look left */

#ifdef sun
		case XK_Left:
#else
		case XK_KP_4:
#endif
			u->viewDirection.x = 0.0;
			u->viewDirection.y = -1.0;
			u->viewDirection.z = 0.0;
			u->viewUp.x = 0.0;
			u->viewUp.y = 0.0;
			u->viewUp.z = -1.0;
			break;

/* look back */

#ifdef sun
		case XK_Down:
#else
		case XK_KP_2:
#endif
			u->viewDirection.x = -1.0;
			u->viewDirection.y = 0.0;
			u->viewDirection.z = 0.0;
			u->viewUp.x = 0.0;
			u->viewUp.y = 0.0;
			u->viewUp.z = -1.0;
			break;

/* look up */

#ifdef sun
		case XK_R11:
#else
		case XK_KP_5:
#endif
			u->viewDirection.x = 0.0;
			u->viewDirection.y = 0.0;
			u->viewDirection.z = -1.0;
			u->viewUp.x = -1.0;
			u->viewUp.y = 0.0;
			u->viewUp.z = 0.0;
			break;

		case XK_N:
		case XK_n:
			c->flags ^= FL_CHASE_VIEW;
			break;

#ifdef SPECIAL_KEYS

		case XK_o:
			if (absorbDamage(c, 3) == 0) {
				killPlayerEx(c,
							 "You asked to absorb some damage. The aircraft was destroyed.",
							 "No further details are available.");
				return -1;
			}
			break;


		case XK_semicolon:
			debug ^= 1;
			break;

#endif

		case XK_P:
		case XK_p:
			killPlayer(c);
			return -1;
			return -1;
/*NOTREACHED */ break;

		case XK_braceleft:
			startBlackBoxRecording();
			break;

		case XK_braceright:
			endBlackBoxRecording();
			break;

		case XK_bracketleft:
			startBlackBoxPlayback();
			break;

		case XK_k:
		case XK_K:
			CalibrateJoystick();
			break;

		}

	}
	return 0;
}

