// sattool - visual satellite tracking and prediction tool.
// Copyright 2000 Tom Rothamel <tom-idbg@onegeek.org>
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "sattool.h"

// Parts of this file are derived from (or copied wholly out of) the
// file sat.c, which is Copyright 1996 Matthew Francey. The copied
// sections are used under the terms of the GPL. (As is the rest of
// this file.)

// Todo for this file:
//
// * Add checksums of the elements as they're loaded.


///////////////////// File Specific Utility Functions ///////////////////


// an atof() for fixed-fields
// From sat.c
static double sgp_getf(char *s, int b, int e) {
	double	x;
	char	c;

	c = s[e];
	s[e] = '\0';
	x = atof(s + b - 1);
	s[e] = c;
	return x;
}

// an atol() for fixed-fields
// From sat.c
static long sgp_geti(char *s, int b, int e) {
	long	x;
	char	c;

	c = s[e];
	s[e] = '\0';
	x = atol(s + b - 1);
	s[e] = c;
	return x;
}


// decode NORAD's strange "scientific" format
// From sat.c
static double sgp_gete(char *s, int b) {
	char	d[11];

	s    += b - 1;
	d[0]  = s[0];
	d[1]  = '.';
	d[2]  = s[1];
	d[3]  = s[2];
	d[4]  = s[3];
	d[5]  = s[4];
	d[6]  = s[5];
	d[7]  = 'e';
	d[8]  = s[6];
	d[9]  = s[7];
	d[10] = '\0';
	return atof(d);
}

//////////////////////// The Sgp4Sat Class //////////////////////////

// double Sgp4Sat::mm(double t) {
// 	sgp4(s, t);
// 	return s->n;
// }

// Output the object in a string form.
void Sgp4Sat::write(FILE *f, int d) {
	pad(f, d);
	fprintf(f, "(sgp4sat\n");
	pad(f, d+1);
	fprintf(f, "\"%s\"\n", line[0]);
	pad(f, d+1);
	fprintf(f, "\"%s\"\n", line[1]);
	pad(f, d+1);
	fprintf(f, "\"%s\"\n", line[2]);
	pad(f, d);
	fprintf(f, ")\n");
}
		
// The mean motion of the satellite at a given time.
double Sgp4Sat::mm(double nt) {
	if (nt != ct) sgp4(s, nt);
	return s->n;
}

// Find the positon of the satellite.
Position *Sgp4Sat::calcpos(double t, Position *p) {
	int i;
	if (!p) p = new Position;

	sgp4(s, t);
	
	p->t = t;
	ct = t;
	
	for (i=0; i<3; i++) { /* Unroll me! */
		p->x[i] = s->x[i] * R;
		p->dx[i] = s->dx[i] * R * SECDAY;
	}
	
	return p;
}

// Check to see if this satellite has an associated magnitude value.
int Sgp4Sat::hasmag() {
	if (s->mag0 != HUGE_VAL) return 1;
	return 0;
}

// Calculate the satellite's magnitude during a given encounter.
// This uses Mozlcan's formula.
double Sgp4Sat::calcmag(Encounter *e) {
	double ang;
	double fracil;
	double off = 0;
	
	e->calcmore();
	if (!isLit(e->objp)) off = 1000;

	ang = SiteSatSun(e->objp, e->sitep);
	fracil = 0.5 * (1.0 - cos(ang));

	return off + s->mag0 - 15.75 + 2.5 *
		log10(e->range * e->range / fracil);
}
	
	
	
// Create a new Sgp4Sat, from a two line element set.
// Most of this code is from sat.c, although parts have been slightly
// adapted.
Sgp4Sat::Sgp4Sat(char *line0, char *line1, char *line2) {
	int n;

	line[0] = strdup(line0);
	line[1] = strdup(line1);
	line[2] = strdup(line2);

	chomp(line[0]);
	chomp(line[1]);
       	chomp(line[2]);
		
	s = new SAT;
	bzero ((void *) s, sizeof(SAT));


	// Line 0
	s->mag0 = HUGE_VAL;
	
	if (strlen(line0) >= 35) {
		s->len   = sgp_getf(line0, 16, 20);
		s->wid   = sgp_getf(line0, 21, 25);
		s->hgt   = sgp_getf(line0, 26, 30);
		s->mag0  = sgp_getf(line0, 31, 35);
		s->mode |= SGP_SAT_SIZEINFO;
	}

	strncpy(sgpname, line0, 16);
	sgpname[16] = 0;

	n = 15;
	while(sgpname[n] == ' ') {
		sgpname[n] = 0;
		if (n == 0) break;
		n--;
	}

	// Element set line 1
	s->norad  = sgp_geti(line1, 3, 7);

	if (n = 0)
		sprintf(sgpname, "#%d", s->norad);

	s->t0     = sgp_date(sgp_getf(line1, 19, 20)*1000.0
			     + sgp_getf(line1, 21, 32));
	s->dn0    = sgp_getf(line1, 34, 43)*(2.0*M_PI);
	s->ddn0   = sgp_gete(line1, 45)*(2.0*M_PI);
	s->bstar  = sgp_gete(line1, 54);
	s->bulnum = (int) sgp_geti(line1, 65, 68);
	s->ephtyp = line1[63 - 1];
	s->eclass = line1[ 8 - 1];
	strncpy(s->desig, line1 + 9, 8);
	if(s->desig[0] == ' ')
		s->desig[0] = '\0';


	// Element set line 2
	s->incl0 = sgp_getf(line2,  9, 16)*(M_PI/180.0);
	s->node0 = sgp_getf(line2, 18, 25)*(M_PI/180.0);
	s->ecc0  = sgp_getf(line2, 27, 33)*(1.0/10000000.0);
	s->peri0 = sgp_getf(line2, 35, 42)*(M_PI/180.0);
	s->M0    = sgp_getf(line2, 44, 51)*(M_PI/180.0);
	s->n0    = sgp_getf(line2, 53, 63)*(2.0*M_PI);
	s->rev0  = sgp_geti(line2, 64, 68);

	name = sgpname;
	norad = s->norad;
	desig = s->desig;
	epoch = s->t0;
}

Sgp4Sat::~Sgp4Sat() {
	delete s;
	free(line[0]);
	free(line[1]);
	free(line[2]);
}

// This attempts to parse a Sgp4Sat from a tle file. This will only do
// it once.
Sgp4Sat *ReadSgp4Sat(FILE *f) {
	char *tle[3];
	char *buf;
	Sgp4Sat *s = NULL;
	
	tle[0] = NULL;
	tle[1] = NULL;
	tle[2] = NULL;

	while (!feof(f)) {
		buf = (char *) malloc(80);
		if (!fgets(buf, 80, f)) {
			free(buf);
			break;
		}
		switch(buf[0]) {
		case '1':
			if (tle[1]) free(tle[1]);
			tle[1] = buf;
			break;
		case '2':
			if (tle[2]) free(tle[2]);
			tle[2] = buf;
			break;
		default:
			if (tle[0]) free(tle[0]);
			tle[0] = buf;
			break;
		}

		if (tle[0] && tle[1] && tle[2]) {
			s = new Sgp4Sat(tle[0], tle[1], tle[2]);
			break;
		}
	}

	if (tle[0]) free(tle[0]);
	if (tle[1]) free(tle[1]);
	if (tle[2]) free(tle[2]);

	return s;
}

// This repetively calls the above, creating a List from a tle file.
List *ReadTLEFile(FILE *f) {
	List *l;
	Sat *s;

	l = new List;
	
	while (s = ReadSgp4Sat(f)) {
		l->add(s);
	}

	fclose(f);
	
	return l;
}
