// 
// smeg - A satellite modelling and prediction tool
// Copyright (C) 1999  Tom Rothamel
// 
// 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 2 of the License, 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; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

#include "sts2.h"

double fminbr(double, double, double (*)(double), double);
double zeroin(double, double, double (*)(double), double);

List<Pass> *passlist = NULL;
List<Pass> *fpasslist = NULL;

void FreePassList() {
	Pass *p;

	if (!passlist) return;
	if (fpasslist != passlist) delete fpasslist;

	passlist->reset();
	while (p = passlist->next()) {
		delete p;
	}

	delete passlist;
	
	passlist = NULL;
	fpasslist = NULL;
}

extern List<Sat> *fsatlist;
extern double lat;
extern double lon;
extern double alt;

extern double minele;
extern double bigstep;

Site *psite = NULL;
Sat *psat;

double ele(double t) {
	return Elevation(psat, psite, t) - minele;
}

double negele(double t) {
	return minele - Elevation(psat, psite, t);
}

void PredictSat(double start, double end) {
	double min;
	double max;
	double bigsteps;
	char smeg[100];
	Pass *p;
	
	Sat *s;

	s = psat;
	
	bigsteps = bigstep * SECDAY;

	s->calcpos(start);


	min = fminbr(start - 2 * M_PI / (s->s->n), start, ele, bigsteps); 
	max = min;
	
	while (max < end) {
		max = fminbr(min, min + 2 * M_PI / (s->s->n), negele, bigsteps);

		if (max < end && max > start && ele(max) > 0) {
			char buf[100];
 
			p = new Pass;
			p->s = s;
			p->max = max;
			p->min0 = zeroin(max - 1 * M_PI / (s->s->n),
					 max, ele, bigsteps);
			p->min1 = zeroin(max, max + 1 * M_PI / (s->s->n),
					 ele, bigsteps);

			if (p->min0 < start) p->min0 = start;
			if (p->min1 > end) p->min1 = end;
			
			passlist->add_sorted(p, sgp_unix(p->min0));
		}

		min = fminbr(max, max + 2 * M_PI / (s->s->n), ele, bigsteps);
	}
	
	
}

void Predict(double start, double end) {
	int sc = 0;

	if (!fsatlist) return;
	if (end < start) {
		fprintf(stderr, "Error: Start time must be less than end time.\n");
		return;
	}

	if (psite) delete psite;
	psite = new Site(lat, lon, alt);

	FreePassList();
	passlist = new List<Pass>;
	
	fsatlist->reset();
	while (psat = fsatlist->next()) {
		PredictSat(start, end);
		if (++sc % 25 == 0) {
			if (verbose) printf("\r%d/%d sats completed.",
					    sc, fsatlist->count);
			if (verbose) fflush(stdout);
		}
	}

	if (verbose) printf("\rPrediction complete. %d passes found.\n",
			    passlist->count);
			    
	
	fpasslist = passlist;
}

	
void ShowPasslist() {
	Pass *p;
	Azrael *min0;
	Azrael *max;
	Azrael *min1;

	char buf0[100];
	char buf1[100];
	char buf2[100];


	if (!fpasslist) return;
	
	fpasslist->reset();
	while (p = fpasslist->next()) {
		min0 = new Azrael(p->s, psite, p->min0);
		max = new Azrael(p->s, psite, p->max);
		min1 = new Azrael(p->s, psite, p->min1);

		strfsgptime(buf0, 100, "%Y%m%d", p->min0);
		strfsgptime(buf1, 100, "%Y%m%d", p->min1);
		fprintf(out, "%s %s % 6d %s\n", buf0, buf1,
			p->s->s->norad, p->s->name);

		strfsgptime(buf0, 100, "%H:%M:%S", p->min0);
		strfsgptime(buf1, 100, "%H:%M:%S", p->max);
		strfsgptime(buf2, 100, "%H:%M:%S", p->min1);
	
		fprintf(out, "    %s %4.0f %s   %s %4.0f %s %4.1f   %s %4.0f %s\n",
			buf0, min0->az, CompassDir(min0->az),
			buf1, max->az, CompassDir(max->az), max->el,
			buf2, min1->az, CompassDir(min1->az));

		delete min0;
		delete min1;
		delete max;
	}
}
	

void PasslistFilter(STEP *expr) {
	List<Pass> *npasslist;
	Pass *p;
	CONTEXT c;
	
	if (!fpasslist) {
		fprintf(stderr, "Error: No passes. Use 'predict passes' to predict some.\n");
		return;
	}

	npasslist = new List<Pass>;
	
	fpasslist->reset();
	while (p = fpasslist->next()) {
		c.pass = p;
		c.sat = p->s;
		c.site = psite;
		c.t = p->max;
		if (run(&c, expr)) npasslist->add(p);
	}

	if (fpasslist->count == npasslist->count) {
		if (verbose) printf("%d passes passed filter. (unfilter first?)\n", npasslist->count);
	} else {
		if (verbose) printf("%d passes passed filter.\n", npasslist->count);
	}
	if (fpasslist != passlist) delete fpasslist;
	fpasslist = npasslist;
}

void PasslistUnfilter() {
	if (fpasslist == passlist) return;
	if (passlist == NULL) return;

	delete fpasslist;
	fpasslist = passlist;

	if (verbose) printf("Unfiltered list contains %d passes.\n", fpasslist->count);
}

void PrintStep(double t, Pass *p, Azrael *a) {
	char timestr[100];

	strfsgptime(timestr, 100, "%Y%m%d %H:%M:%S", t);

	if (p->s->hasMagnitude()) {

		if (isLit(p->s, t))
			fprintf(out, "\t%s % 6.1f %s % 4.1f %+ 3.1f\n",
				timestr, a->az, CompassDir(a->az), a->el,
				p->s->magnitude(psite));
	} else {

		if (isLit(p->s, t))
			fprintf(out, "\t%s % 6.1f %s % 4.1f\n",
				timestr, a->az, CompassDir(a->az),
				a->el);
	}

}

void StepPass(Pass *p, int step, STEP *expr) {	
	double ds;
	double t;
	int needbanner = 1;
	Azrael *a;
	
	CONTEXT c;
	
	ds = step * SECDAY;

	t = p->max;
	while (t > p->min0) t -= ds;

	while (t < p->min1) {
		c.pass = p;
		c.sat = p->s;
		c.site = psite;
		c.t = t;

		if (isLit(p->s, t))
		if (run(&c, expr)) {

			if (needbanner) {
				needbanner = 0;
				fprintf(out, "%d %s (%.3f days old)\n",
					p->s->norad,
					p->s->name,
					p->max - p->s->s->t0);
			}
			
			a = new Azrael(p->s, psite, t);

			PrintStep(t, p, a);
						
			delete a;
		}
		t += ds;
	}
}

void StepPasses(int step, STEP *filter) {
	Pass *p;
	
	if (fpasslist == NULL) return;

	fpasslist->reset();
	while (p = fpasslist->next()) {
		StepPass(p, step, filter);
	}
}
