/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *  Copyright (C) 2007  Umberto Salsi
 *
 *  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 <string.h>
#include "../util/memory.h"
#include "pm.h"
#include "navaid.h"
#include "vpath_gallery.h"

#define adf_IMPORT
#include "adf.h"

/** Repeat station reception check every ... (s). */
#define RECEPTION_CHECK_INTERVAL 1.0

/* Every station sends its Morse ID every ... (s). */
#define ID_DELAY 10.0

/**
 * State of the ADF display panel.
 */
typedef struct adf_data {
	/** Next ADF in the list. */
	struct adf_data *next;
	/** Associated viewer. */
	viewer * v;
	/** If currently visible, as the panel slot is shared with radar and HSI. */
	_BOOL     enabled;
	/** Tuned frequency. */
	navaid_Channel    frequency;
	/** Tuned station, if in range. */
	navaid_Type *station;
	/** Next planned station tuning check (simulation time, s). */
	double    next_check;
	/** Next expected ID transmission from tuned station (simulation time, s). */
	double    id_update_at;
	/** Current station ID, if available. */
	char     *id;
	/** Current needle heading (DEG). */
	int       hdg;
} adf_data;


static adf_data *free_list = NULL;


/**
 * Module cleanup function to be registered in the memory manager.
 */
static void adf_cleanup()
{
	adf_data *p;

	while( free_list != NULL ){
		p = free_list;
		free_list = free_list->next;
		memory_dispose(p);
	}
}


void adf_enable(viewer *u)
{
	adf_data *adf;

	if( u->adf == NULL ){

		/* Allocate memory for ADF status: */
		if( free_list == NULL ){
			adf = memory_allocate( sizeof(adf_data), NULL);
			memory_registerCleanup(adf_cleanup);
		} else {
			adf = free_list;
			free_list = adf->next;
		}

		/* Initialize: */
		adf->v = u;
		adf->frequency = 200;
		adf->station = NULL;
		adf->next_check = curTime + RECEPTION_CHECK_INTERVAL;
		adf->id_update_at = curTime + ID_DELAY;
		adf->id = NULL;
		adf->hdg = 0;

		u->adf = adf;
	} else {
		adf = u->adf;
	}

	if( ! adf->enabled ){
		adf->enabled = TRUE;
		adf->station = NULL;
		adf->next_check = curTime + RECEPTION_CHECK_INTERVAL;
		adf->id_update_at = curTime + ID_DELAY;
		adf->id = NULL;
	}
}


void adf_disable(viewer *u)
{
	adf_data *adf;

	adf = u->adf;
	if( adf == NULL )
		return;
	
	adf->enabled = FALSE;
}


static void adf_retune(adf_data *adf)
{
	adf->station = NULL;
	adf->next_check = curTime + RECEPTION_CHECK_INTERVAL;
	adf->id_update_at = curTime + ID_DELAY;
	adf->id = NULL;
}


static void adf_update_reception(craft *c, adf_data *adf)
{
	navaid_Type *n;

	if( curTime < adf->next_check )
		return;

	adf->next_check = curTime + RECEPTION_CHECK_INTERVAL;

	n = navaid_reception_check(c, adf->frequency);

	if( n == NULL ){
		/* no station found. */
		adf->station = NULL;
		adf->id_update_at = curTime + ID_DELAY;
		adf->id = NULL;
	
	} else if( adf->station != n ){
		/* Station change: */
		adf->station = n;
		adf->id_update_at = curTime + ID_DELAY;
		adf->id = NULL;
	} else {
		/* Still the same station. Update station ID: */
		if( adf->id == NULL && curTime > adf->id_update_at ){
			adf->id = n->id;
		}
	}
}


static _BOOL is_auto_repeat( double *timeout )
{
	_BOOL res;

	res = (curTime < *timeout);
	*timeout = curTime + 0.1;
	return res;
}


void adf_frq_inc(viewer * u, int step)
{
	adf_data *adf;
	static double timeout = 0.0;

	adf = u->adf;
	if( adf == NULL || ! adf->enabled )
		return;
	
	if( is_auto_repeat(&timeout) )
		step *= 2;
	adf->frequency += step;
	if( adf->frequency < navaid_NDB_CHANNEL_MIN )
		adf->frequency = navaid_NDB_CHANNEL_MIN;
	if( adf->frequency > navaid_NDB_CHANNEL_MAX )
		adf->frequency = navaid_NDB_CHANNEL_MAX;
	adf_retune(adf);
}


void adf_hdg_inc(viewer *u, int step)
{
	adf_data *adf;
	static double timeout = 0.0;

	adf = u->adf;
	if( adf == NULL || ! adf->enabled )
		return;
	
	if( is_auto_repeat(&timeout) )
		step *= 2;
	adf->hdg += step;
	while( adf->hdg < 0 )
		adf->hdg += 360;
	while( adf->hdg >= 360 )
		adf->hdg -= 360;
}


static void adf_panel_string(viewer *u, double x, double y, double fh,
	char *s1, char *s2, char *s3)
{
	double fw, fh2, fw2;

	fw = fh;

	fh2 = 1.5*fh;
	fw2 = fh2;

	VDrawStrokeString(u->v,
		(int) (x + 0.5), (int) (y + 0.5),
		s1, strlen(s1), (int) (fh + 0.5), radarColor);

	VDrawStrokeString(u->v,
		(int) (x + 13*fw - fw2*strlen(s2) + 0.5), (int) (y + 0.5),
		s2, strlen(s2), (int) (fh2 + 0.5), whiteColor);

	VDrawStrokeString(u->v,
		(int) (x + 14*fw + 0.5), (int) (y + 0.5),
		s3, strlen(s3), (int) (fh + 0.5), radarColor);
}


void adf_panel_draw(viewer * u)
{
	adf_data *adf;
	Alib_Window *w;
	double x, y, fh, fw, il;
	char s[100];

	adf = u->adf;
	if( adf == NULL || ! adf->enabled )
		return;
	
	adf_update_reception(u->c, adf);

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

	fh = RectWidth(u->tuner) / 20.0;
	fw = fh;
	il = 1.2*fh*2.5; /* inter-line spacing */

	x = u->tuner.a.x + fw;
	y = u->tuner.a.y + il;

	/*
		Displays mode:
	*/
	adf_panel_string(u, x, y, fh, "Mode", "ADF", "");
	y += il;

	/*
		Displays frequency
	*/
	sprintf(s, "%d", adf->frequency);
	adf_panel_string(u, x, y, fh, "FRQ", s, "KHz");
	y += il;

	/*
		Displays station ID:
	*/
	if( adf->id != NULL )
		strcpy(s, adf->id);
	else
		strcpy(s, "----");
	adf_panel_string(u, x, y, fh, "STA", s, "");
	y += il;

	/*
		DIsplays signal level "LVL":
	*/
	if( ! adf->enabled )
		adf_panel_string(u, x, y, fh, "LVL", "OFF", "");
	if( adf->station == NULL )
		adf_panel_string(u, x, y, fh, "LVL", "no sgn", "");
	else if( adf->id == NULL )
		adf_panel_string(u, x, y, fh, "LVL", "low", "");
		/* FIXME: actually, station ID still not received */
	else
		adf_panel_string(u, x, y, fh, "LVL", "good", "");
	y += il;
}


void adf_draw(viewer * u)
{
	adf_data *adf;
	Alib_Window  *w;
	int       xc, yc, h, x, y;
	VMatrix   m;
	VPoint    rloc;
	double    scale, hdg, r, a;
	Alib_Pixel     color;

	adf = u->adf;
	if( adf == NULL || ! adf->enabled )
		return;
	
	w = u->v->w;

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

	color = whiteColor;

	x = u->indicator.a.x;
	y = u->indicator.a.y;
	scale = RectWidth(u->indicator);
	r = 0.44*scale;
	xc = x + (int) (0.50*scale+0.5);
	yc = y + (int) (0.50*scale+0.5);

	if (u->c->showMag)
		hdg = pm_mag_heading(u->c);
	else
		hdg = u->c->curHeading;

	h = (int) (0.045*scale + 0.5);

	/* Displays "TH" or "MH": */
	VDrawStrokeString(u->v,
		x + (int) (0.05*scale+0.5), y + (int) (0.06*scale),
		(u->c->showMag)? "MH":"TH", 2, h, color);

	/* Draw compass scale: */
	VIdentMatrix(&m);
	VRotate(&m, ZRotation, -hdg);
	VScaleMatrix(&m, r, r, r);
	VTranslate(&m, xc, yc, -1.0);
	vpath_stroke( vpath_gallery_get_compass_scale(), &m, w, whiteColor);

	/* Draw fixed scale: */
	VIdentMatrix(&m);
	VScaleMatrix(&m, r/0.90, r/0.90, r/0.90);
	VTranslate(&m, xc, yc, -1.0);
	vpath_stroke( vpath_gallery_get_compass_fixed_scale(), &m, w, whiteColor);

	/* Draw stylized aircraft: */
	VIdentMatrix(&m);
	VScaleMatrix(&m, 0.25*r, 0.20*r, 0.25*r);
	VTranslate(&m, xc, yc, -1.0);
	vpath_stroke( vpath_gallery_get_stylized_aircraft(), &m, w, whiteColor);

	/* Draw selected HDG: */
	VIdentMatrix(&m);
	VRotate(&m, ZRotation, -hdg + units_DEGtoRAD(adf->hdg));
	VScaleMatrix(&m, r, r, r);
	VTranslate(&m, xc, yc, -1.0);
	vpath_stroke( vpath_gallery_get_pointer_triangle(), &m, w,
		magentaColor);

	/* Draw pointer: */

	if( adf->station == NULL ){
		/* No signal -- pointer indicates right: */
		a = units_DEGtoRAD(90.0);
	} else {
		VTransform(&adf->station->Sg, &u->c->XYZtoNED, &rloc);
		VReverseTransform(&rloc, &u->c->trihedral, &rloc);
		a = atan2(rloc.y, rloc.x);
	}

	VIdentMatrix(&m);
	VRotate(&m, ZRotation, a);
	VScaleMatrix(&m, 0.90*r, 0.90*r, 0.90*r);
	VTranslate(&m, xc, yc, -1.0);
	vpath_stroke( vpath_gallery_get_pointer_arrow(), &m, w, whiteColor);

}
