/*
 *   tle2amsat.c         Copyright (C) 1999      by Hamish Moffatt VK3SB
 *   Based on get_data.c Copyright (C) 1991-2000 by Jonathan Naylor HB9DRD/G4KLX
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */

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

#include <glib.h>

#ifndef TRUE
#define	TRUE		1
#endif

#ifndef FALSE
#define	FALSE		0
#endif

struct Sat_Struct {
	char   Name[21];
	char   Category[21];
	int    Epoch_Year;
	double Epoch_Day;
	double Inclination;
	double Mean_Anomoly;
	double Mean_Motion;
	double Eccentricity;
	double Arg_Of_Perigee;
	double RAAN;
	long   Epoch_Orbit;
	double Drag;
	int    Aflag;
	double Alon;
	double Alat;
	int    No_Modes;
	int    MA_Day[10];
	char   Mode[10][5];
	long   Catalogue_No;
	float  Frequency1;
	float  Frequency2;
	float  Frequency3;
};

#define	TLE_NAME	0
#define	TLE_LINE1	1
#define	TLE_LINE2	2

static int Sats_Added;
static int Sats_Updated;

char *Files_Path = VARDIR;

GSList *Sat_List = NULL;

struct Sat_Struct *Find_Satellite_Name(char *name)
{
	struct Sat_Struct *Sat_Data, *Found;
	GSList *Sat;

	Found = NULL;
	Sat   = Sat_List;

	while (Sat != NULL && Found == NULL) {
		Sat_Data = (struct Sat_Struct *)Sat->data;

		if (strcmp(Sat_Data->Name, name) == 0)
			Found = Sat_Data;

		Sat = g_slist_next(Sat);
	}

	return Found;
}

struct Sat_Struct *Find_Satellite_Cat_No(int cat_no)
{
	struct Sat_Struct *Sat_Data, *Found;
	GSList *Sat;

	Found = NULL;
	Sat   = Sat_List;

	while (Sat != NULL && Found == NULL) {
		Sat_Data = (struct Sat_Struct *)Sat->data;

		if (Sat_Data->Catalogue_No != 0L && Sat_Data->Catalogue_No == cat_no)
			Found = Sat_Data;

		Sat = g_slist_next(Sat);
	}

	return Found;
}

gint Sat_Comparison(gconstpointer s1, gconstpointer s2)
{
	const struct Sat_Struct *First  = (struct Sat_Struct *)s1;
	const struct Sat_Struct *Second = (struct Sat_Struct *)s2;
	
	return strcasecmp(First->Name, Second->Name);
}

double Conv_Digits(char *Buffer, int n)
{
	char Temp[20];

	strncpy(Temp, Buffer, n);
	Temp[n] = '\0';

	return atof(Temp);
}

int Assign_Sat_Name(char *Buffer, struct Sat_Struct *Temp_Data)
{
	int  i;

	if (strlen(Buffer) < 2)
		return FALSE;

	for (i = strlen(Buffer) - 1; Buffer[i] == ' ' && i >= 0; i--);
	Buffer[i + 1] = '\0';

	if (strlen(Buffer) > 17 || strlen(Buffer) < 2)
		return FALSE;

	strcpy(Temp_Data->Name, Buffer);

	return TRUE;
}

int Assign_Line1(char *Buffer, struct Sat_Struct *Temp_Data)
{
	if (Buffer[0] != '1' || strlen(Buffer) < 69)
		return FALSE;

	Temp_Data->Catalogue_No = (long)Conv_Digits(Buffer + 2, 5);

	Temp_Data->Epoch_Year = (int)Conv_Digits(Buffer + 18, 2);
	
	if (Temp_Data->Epoch_Year > 56)
		Temp_Data->Epoch_Year += 1900;
	else
		Temp_Data->Epoch_Year += 2000;

	Temp_Data->Epoch_Day  = Conv_Digits(Buffer + 20, 12);

	Temp_Data->Drag = Conv_Digits(Buffer + 33, 10);

	return TRUE;
}

int Assign_Line2(char *Buffer, struct Sat_Struct *Temp_Data)
{
	char Temp[15];

	if (Buffer[0] != '2' || strlen(Buffer) < 69)
		return FALSE;

	Temp_Data->Inclination = Conv_Digits(Buffer + 8, 8);

	Temp_Data->RAAN = Conv_Digits(Buffer + 17, 8);

	Temp[0] = '0';
	Temp[1] = '.';
	strncpy(Temp + 2, Buffer + 26, 7);

	Temp_Data->Eccentricity = Conv_Digits(Temp, 9);

	Temp_Data->Arg_Of_Perigee = Conv_Digits(Buffer + 34, 8);

	Temp_Data->Mean_Anomoly = Conv_Digits(Buffer + 43, 8);

	Temp_Data->Mean_Motion = Conv_Digits(Buffer + 52, 11);

	Temp_Data->Epoch_Orbit = (long)Conv_Digits(Buffer + 63, 5);

	return TRUE;
}

int Insert_Sat(struct Sat_Struct New_Data, char *Category)
{
	struct Sat_Struct *Found, *Sat;
	int i;

	if ((Found = Find_Satellite_Cat_No(New_Data.Catalogue_No)) == NULL)
		Found = Find_Satellite_Name(New_Data.Name);

	if (Found == NULL) {
		Sats_Added++;

		strcpy(New_Data.Category, Category);
		New_Data.Aflag       = FALSE;
		New_Data.No_Modes    = 0;
		New_Data.Frequency1  = 0.0;
		New_Data.Frequency2  = 0.0;
		New_Data.Frequency3  = 0.0;

		Sat  = g_malloc(sizeof(struct Sat_Struct));
		*Sat = New_Data;

		Sat_List = g_slist_insert_sorted(Sat_List, Sat, Sat_Comparison);

		return TRUE;
	} else {
		Sats_Updated++;

		strcpy(New_Data.Category, Found->Category);

		for (i = 0; Category[i] != '\0'; i++)
		{
			if (strchr(New_Data.Category, Category[i]) == NULL)
			{
				int n = strlen(New_Data.Category);
				New_Data.Category[n++] = Category[i];
				New_Data.Category[n++] = '\0';
			}
		}

		New_Data.Aflag      = Found->Aflag;
		New_Data.Alon       = Found->Alon;
		New_Data.Alat       = Found->Alat;
		New_Data.No_Modes   = Found->No_Modes;

		for (i = 0; i < Found->No_Modes; i++) {
			New_Data.MA_Day[i] = Found->MA_Day[i];
			strcpy(New_Data.Mode[i], Found->Mode[i]);
		}

		New_Data.Frequency1 = Found->Frequency1;
		New_Data.Frequency2 = Found->Frequency2;
		New_Data.Frequency3 = Found->Frequency3;

		Sat_List = g_slist_remove(Sat_List, Found);
		g_free(Found);

		Sat  = g_malloc(sizeof(struct Sat_Struct));
		*Sat = New_Data;

		Sat_List = g_slist_insert_sorted(Sat_List, Sat, Sat_Comparison);

		return TRUE;
	}	
}

int Read_NASA_Data(FILE *fp, char *Category)
{
	struct Sat_Struct Temp_Data;
	char Buffer[101];
	char *s;
	int  State;

	Sats_Added = 0;
	Sats_Updated = 0;
	State = TLE_NAME;

	while (fgets(Buffer, 100, fp) != NULL) {
		if ((s = strchr(Buffer, '\n')) != NULL) *s = '\0';
		if ((s = strchr(Buffer, '\r')) != NULL) *s = '\0';

		switch (State) {
			case TLE_NAME:
				memset(&Temp_Data, '\0', sizeof(struct Sat_Struct));
				if (!Assign_Sat_Name(Buffer, &Temp_Data)) {
					State = TLE_NAME;
					break;
				}
				State = TLE_LINE1;
				break;
			case TLE_LINE1:
				if (!Assign_Line1(Buffer, &Temp_Data)) {
					State = TLE_NAME;
					break;
				}
				State = TLE_LINE2;
				break;
			case TLE_LINE2:
				if (!Assign_Line2(Buffer, &Temp_Data)) {
					State = TLE_NAME;
					break;
				}
				Insert_Sat(Temp_Data, Category);
				State = TLE_NAME;
				break;
			default:
				break;
		}
	}

	fprintf(stdout, "%d satellites added and %d satellites updated\n", Sats_Added, Sats_Updated);

	return TRUE;
}

void Read_Sat_Data(void)
{
	char Buffer[120], *t[12], *u[2];
	char File_Name[81], *s;
	struct Sat_Struct Temp_Data, *Sat;
	FILE *fp;
	int  n;

	sprintf(File_Name, "%s/satfile", Files_Path);

	if ((fp = fopen(File_Name, "r")) == NULL)
		return;

	while (fgets(Buffer, 119, fp) != NULL) {
		if (Buffer[0] == '[') {
			if ((s = strchr(Buffer, ']')) == NULL)
				g_error("mtrack: malformed line %s\n", Buffer);
			*s = '\0';

			strcpy(Temp_Data.Name, Buffer + 1);

			strcpy(Temp_Data.Category, "");
			Temp_Data.Aflag      = FALSE;
			Temp_Data.No_Modes   = 0;
			Temp_Data.Frequency1 = 0.0;
			Temp_Data.Frequency2 = 0.0;
			Temp_Data.Frequency3 = 0.0;
		} else {
			if ((t[0] = strtok(Buffer, "=")) == NULL)
				continue;
			if ((t[1] = strtok(NULL, "\r\n")) == NULL)
				continue;

			if (strcmp(t[0], "categories") == 0) {
				if (t[1] != NULL && strlen(t[1]) > 0)
					strcpy(Temp_Data.Category, t[1]);
			} else if (strcmp(t[0], "epoch_year") == 0) {
				Temp_Data.Epoch_Year = atoi(t[1]);
			} else if (strcmp(t[0], "epoch_day") == 0) {
				Temp_Data.Epoch_Day = atof(t[1]);
			} else if (strcmp(t[0], "inclination") == 0) {
				Temp_Data.Inclination = atof(t[1]);
			} else if (strcmp(t[0], "mean_anomoly") == 0) {
				Temp_Data.Mean_Anomoly = atof(t[1]);
			} else if (strcmp(t[0], "mean_motion") == 0) {
				Temp_Data.Mean_Motion = atof(t[1]);
			} else if (strcmp(t[0], "eccentricity") == 0) {
				Temp_Data.Eccentricity = atof(t[1]);
			} else if (strcmp(t[0], "arg_of_perigee") == 0) {
				Temp_Data.Arg_Of_Perigee = atof(t[1]);
			} else if (strcmp(t[0], "raan") == 0) {
				Temp_Data.RAAN = atof(t[1]);
			} else if (strcmp(t[0], "orbit") == 0) {
				Temp_Data.Epoch_Orbit = atol(t[1]);
			} else if (strcmp(t[0], "drag") == 0) {
				Temp_Data.Drag = atof(t[1]);
			} else if (strcmp(t[0], "alongitude") == 0) {
				Temp_Data.Alon  = atof(t[1]);
				Temp_Data.Aflag = TRUE;
			} else if (strcmp(t[0], "alatitude") == 0) {
				Temp_Data.Alat  = atof(t[1]);
				Temp_Data.Aflag = TRUE;
			} else if (strcmp(t[0], "modes") == 0) {
				s = t[1];
				n = 0;
				while ((t[n + 2] = strtok(s, " ")) != NULL) {
					n++;
					s = NULL;
				}

				Temp_Data.No_Modes = n;

				for (n = 0; n < Temp_Data.No_Modes; n++) {
					u[0] = strtok(t[n + 2], ":");
					u[1] = strtok(NULL, "");

					if (u[0] == NULL || u[1] == NULL)
						g_error("mtrack: cannot parse mode data\n");

					Temp_Data.MA_Day[n] = atoi(u[0]);
					strcpy(Temp_Data.Mode[n], u[1]);
				}
			} else if (strcmp(t[0], "frequencies") == 0) {
				t[2] = strtok(t[1], ",");
				t[3] = strtok(NULL, ",");
				t[4] = strtok(NULL, "");

				if (t[2] == NULL || t[3] == NULL || t[4] == NULL)
					g_error("mtrack: cannot parse frequencies data\n");

				Temp_Data.Frequency1 = atof(t[2]);
				Temp_Data.Frequency2 = atof(t[3]);
				Temp_Data.Frequency3 = atof(t[4]);
			} else if (strcmp(t[0], "catalogue_no") == 0) {
				Temp_Data.Catalogue_No = atol(t[1]);
				Sat  = g_malloc(sizeof(struct Sat_Struct));
				*Sat = Temp_Data;
				Sat_List = g_slist_insert_sorted(Sat_List, Sat, Sat_Comparison);
			} else {
				g_error("mtrack: unknown keyword %s in %s\n", t[0], File_Name);
			}
		}
	}

	fclose(fp);
}


int Write_Sat_Data(void)
{
	char File_Name[81];
	GSList *Sat;
	struct Sat_Struct *Data;
	FILE *fp;
	int  i;

	sprintf(File_Name, "%s/satfile", Files_Path);

	if ((fp = fopen(File_Name, "w")) == NULL) {
		fprintf(stderr, "Cannot open %s/satfile for writing\n", Files_Path);
		return FALSE;
	}

	Sat = Sat_List;

	while (Sat != NULL) {
		Data = (struct Sat_Struct *)Sat->data;

		fprintf(fp, "[%s]\n", Data->Name);
		fprintf(fp, "categories=%s\n", Data->Category);
		fprintf(fp, "epoch_year=%d\n", Data->Epoch_Year);
		fprintf(fp, "epoch_day=%f\n", Data->Epoch_Day);
		fprintf(fp, "inclination=%f\n", Data->Inclination);
		fprintf(fp, "mean_anomoly=%f\n", Data->Mean_Anomoly);
		fprintf(fp, "mean_motion=%f\n", Data->Mean_Motion);
		fprintf(fp, "eccentricity=%f\n", Data->Eccentricity);
		fprintf(fp, "arg_of_perigee=%f\n", Data->Arg_Of_Perigee);
		fprintf(fp, "raan=%f\n", Data->RAAN);
		fprintf(fp, "orbit=%ld\n", Data->Epoch_Orbit);
		fprintf(fp, "drag=%f\n", Data->Drag);

		if (Data->Aflag) {
			fprintf(fp, "alongitude=%f\n", Data->Alon);
			fprintf(fp, "alatitude=%f\n", Data->Alat);
		}

		if (Data->No_Modes > 0) {
			fprintf(fp, "modes=");

			for (i = 0; i < Data->No_Modes; i++)
				fprintf(fp, "%d:%s ", Data->MA_Day[i], Data->Mode[i]);

			fprintf(fp, "\n");
		}

		if (Data->Frequency1 != 0.0 || Data->Frequency2 != 0.0 || Data->Frequency3 != 0.0)
			fprintf(fp, "frequencies=%f,%f,%f\n", Data->Frequency1, Data->Frequency2, Data->Frequency3);

		fprintf(fp, "catalogue_no=%ld\n\n", Data->Catalogue_No);

		Sat = g_slist_next(Sat);
	}

	fclose(fp);

	return TRUE;
}



int main(int argc, char** argv)
{
	char Category[21];
	FILE *fp;
	int i;

	Category[0] = '\0';

	while ((i = getopt(argc, argv, "t:")) != -1)
	{
		switch (i)
		{
			case 't':
				if (strlen(optarg) > 20) {
					fprintf(stderr, "tle2mtrack: too many categories\n");
					return 1;
				}
				strcpy(Category, optarg);
				break;
			case ':':
			case '?':
				fprintf(stderr, "Usage: tle2mtrack [-t category] [files ...]\n");
				return 1;
		}
	}

	Read_Sat_Data();

	if (optind == argc) {
		Read_NASA_Data(stdin, Category);
	} else {
		for (i = optind; i < argc; i++) {
			if ((fp = fopen(argv[i], "r")) != NULL) {
				Read_NASA_Data(fp, Category);
				fclose(fp);
			} else {
				fprintf(stderr, "tle2mtrack: cannot open file %s\n", argv[i]);
			}
		}
	}

	Write_Sat_Data();

	return 0;
}
