// 
// 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"
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

extern "C" {
#include <readline/readline.h>
}

struct options {
	char *name;
	int (*op)(CONTEXT *, STEP *);
};

int op_fortytwo(CONTEXT *, STEP *);
int op_norad(CONTEXT *, STEP *);
int op_alt(CONTEXT *, STEP *);
int op_minmag(CONTEXT *, STEP *);
int op_mag(CONTEXT *, STEP *);
int op_lit(CONTEXT *, STEP *);
int op_sunel(CONTEXT *, STEP *);
int op_name(CONTEXT *, STEP *);

struct options ops[] = {
	{"fortytwo", op_fortytwo},
	{"norad", op_norad},
	{"nn", op_norad},
	{"alt", op_alt},
	{"minmag", op_minmag},
	{"mag", op_mag},
	{"lit", op_lit},
	{"sunel", op_sunel},
	{"name", op_name},
	{NULL, NULL}
};

void NoradFilter() {
	STEP *st;
	STEP *or;
	char *line;
	int nn;
	
	st = newSTEP(op_false, 0, "False");

	while(line = readline("norad number: ")) {
	      nn = atoi(line);
	      if (nn == 0) break;

	      or = newSTEP(op_or, 0, "Or");
	      or->s1 = st;
	      st = newSTEP(op_norad, '=', "Norad");
	      st->i = nn;
	      or->s2 = st;
	      st = or;
	      free(line);
	}

	free(line);
	SatlistFilter(st);
}

	      
STEP *newSTEP(int(* op)(CONTEXT *, STEP *), char con, char *name) {
	STEP *s;

	s = (STEP *) calloc(sizeof(STEP), 1);
	s->op = op;
	s->con = con;
	s->name = name;
	return s;
}

STEP *newSTEPident(char *ident, char con) {
	int i;

	i = 0;
	while (ops[i].name) {
		if (!strcmp(ident, ops[i].name)) break;
		i++;
	}

	if (!ops[i].name) return NULL;

	return newSTEP(ops[i].op, con, ident);
}

int run(CONTEXT *c, STEP *s) {
	if (!s) {
		fprintf(stderr, "Warning: Null expression. Returning False.\n");
		return 0;
	}
	return s->op(c, s);
}

int op_and(CONTEXT *c, STEP *s) {
	return run(c, s->s1) && run(c, s->s2);
}

int op_or(CONTEXT *c, STEP *s) {
	return run(c, s->s1) || run(c, s->s2);
}

int op_true(CONTEXT *c, STEP *s) {
	return 1;
}

int op_false(CONTEXT *c, STEP *s) {
	return 0;
}

inline int compared(double val, STEP *s) {
	switch (s->con) {
	case '<':
		return val < s->d;
	case '>':
		return val > s->d;
	case '=':
		return val == s->d;
	default:
		fprintf(stderr, "Error: Can't use '%c' with %s.\n",
			s->con, s->name);
		return 0;
	}
}

inline int comparei(int val, STEP *s) {
	switch (s->con) {
	case '<':
		return val < s->i;
	case '>':
		return val > s->i;
	case '=':
		return val == s->i;
	default:
		fprintf(stderr, "Error: Can't use '%c' with %s.\n",
			s->con, s->name);
		return 0;
	}
}

inline int compares(char *str, STEP *s) {
	int val = strcasecmp(str, s->str);
	
	switch (s->con) {
	case '<':
		return val < 0;
	case '>':
		return val > 0;
	case '=':
		return val == 0;
	case '~':
		/* Regexp code should go _here_ */
	default:
		fprintf(stderr, "Error: Can't use '%c' with %s.\n",
			s->con, s->name);

		return 0;
	}
}

#define valid(x) if (!x) { printf("Error: " #x " not defined!\n"); return 0; }

/*** fortytwo - always is 42.
     Type: double
     Valid: always
     Useful: never

     The fortytwo expressor is always 42. Apart from debugging, it's
     almost never useful.

     Example: fortytwo < 50  is always true
     Example: fortytwo > 50  is always false
***/

int op_fortytwo(CONTEXT *c, STEP *s) {
	return compared(42, s);
}

/*** norad - compares against norad #.
     Type: integer
     Valid: filter
     Useful: filter
     
     The norad expressor is used to compare against the norad
     catalog number of the satellite.

     Example: norad < 1000   finds old satellites
     Example: norad = 25544  selects ISS by catalog number
***/
     
int op_norad(CONTEXT *c, STEP *s) {
	return comparei(c->sat->s->norad, s);
}

/*** alt - compares against sat altitude
     Type: double
     Valid: current position
     Useful: current position

     The alt expressor is used to compare against the satellite's
     current altitude. (Altitude is in km.)

     Example: alt > 100000  shows stuff really up there
***/

int op_alt(CONTEXT *c, STEP *s) {
	valid(c->lla);
	return compared(c->lla->alt, s);
}

/*** minmag - compares against maximum brightness
     Type: double
     Valid: filter
     Useful: filter

     The minmag expressor is used to filter a satlist based on the
     theoretical minmum magnitude of a satellite. This is the
     magnitude as seen from a site the satellite's perigee away when
     the satellite is fully illuminated.

     It returns false if there is no magnitude information on a satellite.
     
     Example: minmag < 3  shows stuff that's brighter than third magnitude.
***/

int op_minmag(CONTEXT *c, STEP *s) {
	valid(c->sat);
	if (!c->sat->hasMagnitude()) return 0;
	c->sat->calcpos(sgp_now());

	return compared(c->sat->maxMagnitude(), s);
}

int op_mag(CONTEXT *c, STEP *s) {
	valid(c->sat);
	if (!c->sat->hasMagnitude()) return 0;
	c->sat->calcpos(c->t);

	return compared(c->sat->magnitude(c->site), s);
}

int op_lit(CONTEXT *c, STEP *s) {
	valid(c->pass);
	valid(c->sat);

	return(isLit(c->sat, c->pass->min0) || isLit(c->sat, c->pass->min1));
}

int op_sunel(CONTEXT *c, STEP *s) {
	valid(c->site);

	return compared(Elevation(theSun, c->site, c->t), s);
}


int op_name(CONTEXT *c, STEP *s) {
	int rv;
	valid(c->sat);


	if (s->con != '~') {
		fprintf(stderr, "Error: Can't use '%c' with %s.\n",
			s->con, s->name);
		return 0;
	}
		
	rv = regexec(&(s->reg), c->sat->s->name, 0, NULL, 0);
	
	return (!rv);
}
