/*
 *
 *	background.c
 *
 *	Routines for handling backgrounds for WAN views.
 *	Backgrounds are kept in vector form entirely in memory for quick
 *	resizing.
 *	All background files found in the background directory are loaded.
 *
 *	HNMS User Interface
 *	Version 2.0
 *
 *	February 1994
 *
 *	Leslie Schlecht
 *	Computer Sciences Corporation
 *	Numerical Aerodynamic Simulation Systems Division
 *	NASA Ames Research Center
 *
 *	Copyright (c) 1994 Leslie Schlecht
 *
 *	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; either version 1, or (at your option)
 *	any later version.
 *
 *	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	<stdio.h>
#include	<string.h>
#include	<sys/types.h>
#include	<sys/dir.h>
#include	<math.h>

#include	<X11/StringDefs.h>
#include	<X11/Intrinsic.h>
#include	<Xm/Xm.h>
#include	<Xm/PushB.h>
#include	<Xm/RowColumn.h>

#include	"defines.h"
#include	"externs.h"
#include	"xsupport.h"
#include	"background.h"

#define		CONV		M_PI/180.0

typedef		struct lineseg	{
		float		*xpts;
		float		*ypts;
		int		n;
		struct lineseg	*next;
		} LS;

typedef		struct bkgd	{
		int		id;
		float		xmin, xmax, ymin, ymax;
		int		projected;
		float		vlat, vlon, vdist;
		char		*name, *filename;
		LS		*l;
		Widget		btn;
		} BKGD;

static Widget	background_menu;
static int	default_background = 1;
caddr_t		bkroot=NULL;

static BKGD	*bkcurrent=NULL, *bkdefault;
static char	buf[BUFSIZ];


/*
 *	Destroy a background struct.
 */
void
DestroyBKGD(bk)
BKGD	*bk;
	{
	LS	*l1, *l2;
	
	for (l1=bk->l; l1; l1=l2) {
		l2 = l1->next;
		free(l1->xpts);
		free(l1->ypts);
		free(l1);
		}
	free(bk->name);
	free(bk->filename);
	free(bk);
	}


/*
 *	Destroy all the backgrounds.
 */
void
DestroyBackgrounds()
	{
	RemoveEntryList(bkroot, DestroyBKGD);
	}


/*
 *	Draw a background into a pixmap.
 */
void
FillBackgroundPixmap(id, pm, w, h)
int	id;
Pixmap	pm;
int	w, h;
	{
	BKGD	*bk;
	XPoint	pts[2048];
	float	xscale, yscale;
	LS	*l;
	register	i;
	caddr_t	n;

	if (!id) return;
	n = bkroot;
	while (NextEntry(&n, &bk))
		if (bk->id == id) break;
	if (!bk ) return;
	xscale = w/(bk->xmax-bk->xmin);
	yscale = h/(bk->ymax-bk->ymin);
	ClearBitmap(pm, w, h);
	for (l=bk->l; l; l=l->next) {
		for (i=0; i<l->n; i++) {
			pts[i].x = xscale*l->xpts[i]+0.5;
			pts[i].y = yscale*l->ypts[i]+0.5;
			}
		BitmapLines(pm, pts, l->n);
		}
	}


/*
 *	Given a background name, get its id.
 */
int
GetBackgroundId(name)
char	*name;
	{
	BKGD	*bk;
	caddr_t	n;

	n = bkroot;
	while (NextEntry(&n, &bk))
		if (strcmp(name, bk->name) == 0) return(bk->id);
	LogMessage(logbuf("Unable to find background %s.\n", name));
	return(0);
	}


/*
 *	Given a background id, get the background name.
 */
char *
GetBackgroundName(id)
int	id;
	{
	BKGD	*bk;
	caddr_t	n;

	n = bkroot;
	while (NextEntry(&n, &bk))
		if (bk->id == id) return(bk->name);
	return(NULL);
	}


/*
 *	Given a background index, get its struct contents.
 */
int
GetBackgroundParms(bk, prj, xmn, ymn, xmx, ymx, vlt, vln, vd)
int	bk;
int	*prj;
float	*xmn, *xmx, *ymn, *ymx, *vlt, *vln, *vd;
	{
	BKGD	*bp;
	caddr_t	n;

	n = bkroot;
	while (NextEntry(&n, &bp))
		if (bp->id == bk) break;
	if (!bp) return(0);
	if (!bp->l)  {
		if (!ReadBackgroundFile(bp)) return(0);
		}
	*prj = bp->projected;
	*xmx = bp->xmax;
	*xmn = bp->xmin;
	*ymx = bp->ymax;
	*ymn = bp->ymin;
	*vlt = bp->vlat;
	*vln = bp->vlon;
	*vd = bp->vdist;
	return(1);
	}


/*
 *	Get the current background id and name.
 */
int
GetCurrentBackground(name)
char	**name;
	{
	*name = bkcurrent->name;
	return(bkcurrent->id);
	}


/*
 *	Get the default background id.
 */
int
GetDefaultBackground()
	{
	return(default_background);
	}


/*
 *	Make the background selection menu.
 *	This menu is found on the new view panel.
 */
void
LoadBackgrounds(parent, menu)
Widget	parent, *menu;
	{
	caddr_t		n;
	
	background_menu = XmCreatePulldownMenu(parent, "background", NULL, 0);
	LoadBackgroundList();
	*menu = background_menu;
	n = bkroot;
	NextEntry(&n, &bkdefault);
	}


/*
 *	Load all the backgrounds in the background directory.
 */
void
LoadBackgroundList()
	{
	DIR	*d;
	struct direct *entry;
	char	*t, fname[BUFSIZ];
	FILE	*fp;
	int	l, currentid = 0;
	BKGD	*bkload;
	XmString	xs;
	Widget	b;

	/* the backgrounds are in a subdirectory in the HNMS home directory */
	strcpy(buf, hnms_home);
	strcat(buf, BACKGROUNDS);
	if (!(d=opendir(buf))) {
		LogMessage("Unable to open background directory.\n");
		return;
		}
	l = strlen(buf)+1;
	while (entry = readdir(d)) {
		strcpy(fname, hnms_home);
		strcat(fname, BACKGROUNDS);
		strncat(fname, entry->d_name, entry->d_namlen);
		buf[entry->d_namlen+l] = '\0';
		if ((fp=fopen(fname, "r")) == NULL) {
			LogMessage(logbuf("Unable to open background file %s.",
				buf));
			continue;
			}
		fscanf(fp, "%s", buf);
		/* the first line should be HNMS */
		if (strncmp(buf, "HNMS", 4) != 0) {
			close(fp);
			continue;
			}
		bkload = (BKGD*)myalloc(NULL, 1, sizeof(BKGD));
		bkload->filename = newstr(fname);
		/* the second line should contain the name of the background */
		fscanf(fp, "%s", buf);
		bkload->name = newstr(buf);
		fclose(fp);
		ReadBackgroundFile(bkload);
		currentid ++;
		bkload->id = currentid;
		bkload->btn = b = pbutton("pb", background_menu, bkload->name);
		XtAddCallback(b, XmNactivateCallback, SelectBackground,
			(XtPointer)currentid);
		AddEntry(&bkroot, bkload, NULL, 0);
		}
	closedir(d);
	}


/*
 *	Get background from a file.
 */
int
ReadBackgroundFile(bk)
BKGD	*bk;
	{
	int	k, count, pen, screen, i;
	float	x, y;
	int	npoints;
	float	xpoints[2048], ypoints[2048];
	char	bmname[BUFSIZ], *c;
	LS	*root=NULL, *l, *last;
	FILE	*fp;

	bmname[0] = '\0';

	if ((fp=fopen(bk->filename, "r")) == NULL) {
		LogMessage(logbuf("Unable to open background file %s.",
			bk->filename));
		return(0);
		}
	/* we've already looked at these lines */
	fscanf(fp, "%s", buf);
	fscanf(fp, "%s", buf);
	if (!ReadBackgroundParameters(fp, bk)) return(0);
	count = 1;
	npoints = 0;
	/* load line segments */
	while (1) {
		count ++;
		k = fscanf(fp, "%f %f %d", &x, &y, &pen);
		if (k == EOF) {
			if (npoints > 0) {
				l = (LS*)myalloc(NULL, 1, sizeof(LS));
				if (!root) 
					root = l;
				else
					last->next = l;
				l->xpts = (float*)myalloc(NULL, npoints,
					sizeof(float));
				l->ypts = (float*)myalloc(NULL, npoints,
					sizeof(float));
				l->n = npoints;
				bcopy(xpoints, l->xpts, npoints*sizeof(float));
				bcopy(ypoints, l->ypts, npoints*sizeof(float));
				last = l;
				}
			break;
			}
		if (k != 3) {
			printf("Error reading line %d of file.", count);
			return(0);
			}
		if ((npoints > 0) && (pen == 0)) {
			l = (LS*)myalloc(NULL, 1, sizeof(LS));
			if (!root) 
				root = l;
			else
				last->next = l;
			l->xpts = (float*)myalloc(NULL, npoints,
				sizeof(float));
			l->ypts = (float*)myalloc(NULL, npoints,
				sizeof(float));
			l->n = npoints;
			bcopy(xpoints, l->xpts, npoints*sizeof(float));
			bcopy(ypoints, l->ypts, npoints*sizeof(float));
			last = l;
			npoints = 0;
			}
		if (bk->projected) {
			if (!project(y,x,&x,&y,bk->vlat,bk->vlon,bk->vdist)) {
				printf("error projecting %f %f\n", x, y);
				return(0);
				}
			}
		xpoints[npoints] = x-bk->xmin;
		ypoints[npoints] = bk->ymax-y;
		npoints ++;
		if (npoints == 2048) {
			printf( "More than 2048 points in a line.\n");
			return(0);
			}
		}
	bk->l = root;

	fclose(fp);
	return(1);
	}

/*
 *	Set aspect ratio.
 */
int
ReadBackgroundParameters(fp, bk)
FILE	*fp;
BKGD	*bk;
	{
	int	k;

	int		proj=0;
	float		xmin, xmax, ymin, ymax, vlat=0.0, vlong=0.0, vdist=1.0;
	float		xmind=(-0.5), xmaxd=0.5, ymind=(-0.5), ymaxd=0.5;
	float		vlatd=0.0, vlongd=0.0;
	float		viewdist = 1.0;

	k = fscanf(fp, "%f %f %f %f %f %f %f", &xmind, &xmaxd, &ymind, &ymaxd,
		&vlatd, &vlongd, &viewdist);
	if (k == 7) {
		proj = 1;
		xmin = xmind;
		xmax = xmaxd;
		ymin = ymind;
		ymax = ymaxd;
		vlat = vlatd;
		vlong = vlongd;
		vdist = viewdist;
		k = project(ymin, xmin, &xmin, &ymin, vlat, vlong, vdist);
		if (!k) {
			printf("Error projecting size parameters.\n");
			return(0);
			}
		k = project(ymax, xmax, &xmax, &ymax, vlat, vlong, viewdist);
		if (!k) {
			printf("Error projecting size parameters.\n");
			return(0);
			}
		}
	else if (k != 4) {
		sprintf(buf, "Error reading size parameters.");
		return(0);
		}
	else {
		xmin = xmind;
		xmax = xmaxd;
		ymin = ymind;
		ymax = ymaxd;
		}
	bk->xmin = xmin;
	bk->xmax = xmax;
	bk->ymin = ymin;
	bk->ymax = ymax;
	bk->projected = proj;
	bk->vlat = vlat;
	bk->vlon = vlong;
	bk->vdist = vdist;
	}


/*
 *	The user selected a background.
 */
void
SelectBackground(w, client_data, call_data)
Widget	w;
int	client_data;
caddr_t	*call_data;
	{
	caddr_t		n;
	BKGD		*bk;
	
	n = bkroot;
	bkcurrent = bkdefault;
	while (NextEntry(&n, &bk))
		if (bk->id == client_data) {
			bkcurrent = bk;
			break;
			}
	}


/*
 *	Set the current background id.
 */
void
SetCurrentBackground(id)
int	id;
	{
	caddr_t		n;
	BKGD		*bk;
	
	n = bkroot;
	bkcurrent = bkdefault;
	while (NextEntry(&n, &bk))
		if (bk->id == id) {
			bkcurrent = bk;
			break;
			}
	if (bkcurrent)
		XtVaSetValues(background_menu,
			XmNmenuHistory, bkcurrent->btn,
			NULL);
	}


/*
 *	Set the default background.
 */
void
SetDefaultBackground(id)
int	id;
	{
	caddr_t	n;
	BKGD	*bk;
	
	n = bkroot;
	while (NextEntry(&n, &bk))
		if (bk->id == id) break;
	if (bk) {
		bkdefault = bk;
		default_background = id;
		}
	}
