/* moondata.c
 *
 * Copyright (C) 2000-2004 josh buhl <jbuhl@users.sourceforge.net>
 *
 * 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, 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
 * */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gnome.h>
#include <time.h>
#include <X11/X.h>		/* need this for CurrentTime */
#include <glade/glade-xml.h>
#include <glade/glade.h>

#include "MoonRise.h"
#include "moondata.h"
#include "CalcEphem.h"

#define RADIUS_OF_EARTH 6371	/* kilometers */

typedef struct CTrans MoonData;

typedef struct _MoonWidgets MoonWidgets;
struct _MoonWidgets {
	GtkWidget *lst;		/* local standard time */
	GtkWidget *lmt;		/* local mean time */
	GtkWidget *lat;		/* local apparent time */
	GtkWidget *lsdt;	/* local sidereal time */
	GtkWidget *ut;		/* greenwich mean time */

	GtkWidget *frc;		/* fraction of cycle completed */
	GtkWidget *flm;		/* days to full moon */
	GtkWidget *nwm;		/* days to new moon */
	GtkWidget *vis;		/* whether moon is above horizon */

	GtkWidget *uys;		/* moon-up yesterday */
	GtkWidget *utd;		/* moon-up today */
	GtkWidget *utm;		/* moon-up tomorrow */
	GtkWidget *dys;		/* moon-down yesterday */
	GtkWidget *dtd;		/* moon-down today */
	GtkWidget *dtm;		/* moon-down tomorrow */

	GtkWidget *ra;		/* right ascension of moon */
	GtkWidget *dec;		/* declination of moon */
	GtkWidget *az;		/* azimuth of moon */
	GtkWidget *alt;		/* altitude of moon */
	GtkWidget *dist;	/* distance from earth */
};

MoonData moondata;
MoonWidgets mw;

gboolean 
visible()
{
	return moondata.Visible;
}

gdouble
moonphase()
{
	return moondata.MoonPhase;
}

static void 
format_time(gchar * buf, gint size, gdouble time)
{
	gint hour, min, sec;

	hour = (gint) (time);

	min = (gint) ((ABS(time - hour)) * 60);

	sec = (gint) (ABS(ABS((time - hour)*60)-min)*60);

	g_snprintf(buf, size, "%02d:%02d:%02d", hour, min, sec);

	return;
}

static void
mod_time(gdouble * time)
{
	g_assert(time);

	if (*time < 0.0)
		*time += 24.0;
	else if (*time > 24.0)
		*time -= 24.0;

	return;
}

static void 
format_angle_hours(gchar * buf, gint size, gdouble angle)
{
	gint hour, min, sec;

	hour = (gint) (angle / 15.0);
	min = (gint) ((ABS(angle - hour * 15.0)) * 4.0);
	sec = (gint) (ABS(ABS((angle - hour * 15.0)*4.0)-min)*60);

	g_snprintf(buf, size, "%02d:%02d:%02d", hour, min, sec);

	return;
}


static void
calc_riseset_time(gchar * buf, gchar * buf2, gint size, CTrans * c)
{
	gdouble rise, set;

	MoonRise(c, &rise, &set);

	if (ABS(rise) > 24.0)
		g_snprintf(buf, size, _("no rise"));
	else{
/* 		rise = rise + (moondata.LST-moondata.UT); */
/* 		mod_time(&rise); */
		format_time(buf, size, rise);
	}
	if (ABS(set) > 24.0)
		g_snprintf(buf2, size, _("no set"));
	else{
/* 		set = set + (moondata.LST-moondata.UT); */
/* 		mod_time(&set); */
		format_time(buf2, size, set);
	}
}

void 
update_moondata(MoonApplet * moon)
{
	struct tm *time_struc;	/* The tm struct is defined in <time.h> */
	gdouble local_std_time, univ_time, eot;
	glong current_gmt, date;
	gint day_of_month, month, year;

	current_gmt = time(CurrentTime);/* CurrentTime defined in <X11/X.h> */

	time_struc = gmtime(&current_gmt);
	univ_time =
	    time_struc->tm_hour + time_struc->tm_min / 60.0 +
	    time_struc->tm_sec / 3600.0;

	/* The date needs to be the date in UTC, i.e. in greenwich, so
	 * be sure not to call the localtime function until after date
	 * has been set (there's only one tm structure).  */

	year = time_struc->tm_year + 1900;
	month = time_struc->tm_mon + 1;
	day_of_month = time_struc->tm_mday;

	date = year * 10000 + month * 100 + day_of_month;

	time_struc = localtime(&current_gmt);
	local_std_time =
	    time_struc->tm_hour + time_struc->tm_min / 60.0 +
	    time_struc->tm_sec / 3600.0;

	/* CalcEphem assumes longitude degrees west to be positive
	 * and degrees east negative.  I think the opposite convention
	 * is more intuitive, since degrees east means you add
	 * time to gmt, but we'll stick to CalcEphem's convention to
	 * prevent mixups. */

	moondata.Glat = moon->is_north ?  moon->latitude : -moon->latitude;
	moondata.Glon = moon->is_east  ? -moon->longitude : moon->longitude ;

	CalcEphem(date, univ_time, &moondata);


	/* eot is the equation of time. gmst is Greenwich Sidereal
	 * Time.  This equation below is correct, but confusing at
	 * first.  It's easy to see when you draw the following
	 * picture: A sphere with 0 and 180 degree longitude, North on
	 * top, a meridian for the real sun, a meridian for a fictive
	 * average sun, a meridian denoting the vernal equinox.  Note
	 * that universal time is the hour angle between 180 degrees
	 * and the fictive sun's meridian measured clockwise.  gmst is
	 * the hour angle between 0 degrees and the meridian of the
	 * vernal equinox measured clockwise.  RA_sun/15.0 is the hour
	 * angle of the real sun measured counterclockwise from the
	 * vernal equinox. eot is the difference between the real and
	 * the fictive sun.  Looking at the picture, it's easy to see
	 * that 12=RA_sun/15-gmst+eot+utc (12 hours = 180 deg.) */

	eot =
	    12.0 - univ_time + moondata.gmst - moondata.RA_sun / 15.0;

	mod_time(&eot);

	moondata.LST = local_std_time;

	moondata.LMT = univ_time - moondata.Glon / 15.0;
	mod_time(&moondata.LMT);

	moondata.LSD =  (moondata.gmst - moondata.Glon / 15.0);
	mod_time(&moondata.LSD);

	moondata.LAT = moondata.LMT + eot;
	mod_time(&moondata.LAT);
}

static gboolean
delete_event (GtkWidget  *widget,
	      MoonApplet *moon)
{
	gtk_widget_hide (widget);
}

static gboolean
close_data_dialog (GtkWidget  *widget,
		   MoonApplet *moon)
{
	gtk_widget_hide (moon->moondata_dialog);
}

static void
handle_data_response (GtkWidget  *widget,
		 int         id,
		 MoonApplet *moon)
{
	if (id == GTK_RESPONSE_HELP) {
		show_help (moon, "glunarclock-detailed");
		return;
	}

	gtk_widget_hide (moon->moondata_dialog);
}


void
update_moondata_dialog(MoonApplet * moon)
{
	const gint size = 64;
	gchar buf[size];
	gchar buf2[size];

	format_time(buf, size, moondata.LST);
	gtk_label_set_text(GTK_LABEL(mw.lst), buf);

	format_time(buf, size, moondata.LMT);
	gtk_label_set_text(GTK_LABEL(mw.lmt), buf);

	format_time(buf, size, moondata.LAT);
	gtk_label_set_text(GTK_LABEL(mw.lat), buf);

	format_time(buf, size, moondata.LSD);
	gtk_label_set_text(GTK_LABEL(mw.lsdt), buf);

	format_time(buf, size, moondata.UT);
	gtk_label_set_text(GTK_LABEL(mw.ut), buf);

	g_snprintf(buf, size, "%4.2f%%", 100.0 * moondata.MoonPhase);
	gtk_label_set_text(GTK_LABEL(mw.frc), buf);


/* 	g_snprintf(buf, size, _("%4.2f Days"), moondata.MoonAge); */
/* 	gtk_label_set_text(GTK_LABEL(mw.age), buf); */
/* 	g_snprintf(buf, size, "%6.4f%%", */
/* 		   50.0 * (1.0 - cos(moondata.MoonPhase * 6.2831853))); */
/* 	gtk_label_set_text(GTK_LABEL(mw.ilm), buf); */
/* 	format_time(buf, size, moondata.NewMoon*24.0); */

	g_snprintf(buf, size, _("%2d%"),(gint)moondata.FullMoon);
	gtk_label_set_text(GTK_LABEL(mw.flm), buf);

	g_snprintf(buf, size, "%2d%",(gint)(moondata.NewMoon));
	gtk_label_set_text(GTK_LABEL(mw.nwm), buf);

/*  	g_snprintf(buf, size, _("%8.4f Hours"), moondata.NewMoon*24.0); */
/*  	gtk_label_set_text(GTK_LABEL(mw.nwm), buf); */


	g_snprintf(buf, size, (moondata.Visible) ? _("Yes") : _("No"));
	gtk_label_set_text(GTK_LABEL(mw.vis), buf);


	/* Changing the moon data to get the rise and set times for
	   yesterday and tomorrow is a hack.  Besides, it's not
	   exactly correct.  I don't really want to create two more
	   MoonData and then CalcEphem them, since that would be a
	   total of three calls to CalcEphem (which is computationally
	   expensive) for every update, i.e. once a second. 
	   Suggestions? */

	moondata.day -= 1;
	calc_riseset_time(buf, buf2, size, &moondata);
	gtk_label_set_text(GTK_LABEL(mw.uys), buf);
	gtk_label_set_text(GTK_LABEL(mw.dys), buf2);

	moondata.day += 2;
	calc_riseset_time(buf, buf2, size, &moondata);
	gtk_label_set_text(GTK_LABEL(mw.utm), buf);
	gtk_label_set_text(GTK_LABEL(mw.dtm), buf2);

	moondata.day -= 1;
	calc_riseset_time(buf, buf2, size, &moondata);
	gtk_label_set_text(GTK_LABEL(mw.utd), buf);
	gtk_label_set_text(GTK_LABEL(mw.dtd), buf2);

	/* end hack */

	format_angle_hours(buf, size, moondata.RA_moon);
	gtk_label_set_text(GTK_LABEL(mw.ra), buf);

	format_time(buf, size, moondata.DEC_moon);
	gtk_label_set_text(GTK_LABEL(mw.dec), buf);

	format_time(buf, size, moondata.A_moon);
	gtk_label_set_text(GTK_LABEL(mw.az), buf);

	format_time(buf, size, moondata.h_moon);
	gtk_label_set_text(GTK_LABEL(mw.alt), buf);

	g_snprintf(buf, size, "%6.0f km", (moondata.EarthMoonDistance)*(RADIUS_OF_EARTH));
	gtk_label_set_text(GTK_LABEL(mw.dist), buf);

}

static void
set_moonwidgets(GladeXML * xml)
{

	mw.lst = glade_xml_get_widget (xml, "lst");
	mw.lmt = glade_xml_get_widget (xml, "lmt");
	mw.lat = glade_xml_get_widget (xml, "lat");
	mw.lsdt= glade_xml_get_widget (xml, "lsdt");
	mw.ut  = glade_xml_get_widget (xml, "ut");

/* 	mw.age = glade_xml_get_widget (xml, "age"); */
	mw.frc = glade_xml_get_widget (xml, "frc");
/* 	mw.ilm = glade_xml_get_widget (xml, "ilm"); */
	mw.flm = glade_xml_get_widget (xml, "flm");
	mw.nwm = glade_xml_get_widget (xml, "nwm");
	mw.vis = glade_xml_get_widget (xml, "vis");

	mw.uys = glade_xml_get_widget (xml, "uys");
	mw.utd = glade_xml_get_widget (xml, "utd");
	mw.utm = glade_xml_get_widget (xml, "utm");
	mw.dys = glade_xml_get_widget (xml, "dys");
	mw.dtd = glade_xml_get_widget (xml, "dtd");
	mw.dtm = glade_xml_get_widget (xml, "dtm");

	mw.ra  = glade_xml_get_widget (xml, "ra");
	mw.dec = glade_xml_get_widget (xml, "dec");
	mw.az  = glade_xml_get_widget (xml, "az");
	mw.alt = glade_xml_get_widget (xml, "alt");
	mw.dist= glade_xml_get_widget (xml, "dist");

}

void 
display_moondata_dialog (MoonApplet * moon)
{
	GladeXML  *xml;
	GtkWidget *button;

	if (moon->moondata_dialog) {
		gtk_window_set_screen (GTK_WINDOW (moon->moondata_dialog),
				       gtk_widget_get_screen (GTK_WIDGET (moon)));
		gtk_window_present (GTK_WINDOW (moon->moondata_dialog));
		return;
	}

	xml = glade_xml_new (MOON_GLADEDIR "/glunarclock.glade", "moon_data_dialog", NULL);
	moon->moondata_dialog = glade_xml_get_widget (xml, "moon_data_dialog");

	set_moonwidgets(xml);

/* 	gtk_window_set_wmclass (GTK_WINDOW (moon->moondata_dialog), "moon", "Moon"); */
	
	gtk_dialog_set_default_response (
		GTK_DIALOG (moon->moondata_dialog), GTK_RESPONSE_CLOSE);

	gnome_window_icon_set_from_file (
		GTK_WINDOW (moon->moondata_dialog),
		GNOME_ICONDIR"/glunarclock-logo.png");
		
	g_signal_connect (moon->moondata_dialog, "delete_event",
			  G_CALLBACK (delete_event), moon);

	g_signal_connect (moon->moondata_dialog, "response",
			  G_CALLBACK (handle_data_response), moon);

	button = glade_xml_get_widget (xml, "close_button");
        g_signal_connect (button, "clicked", G_CALLBACK (close_data_dialog), moon);

	update_moondata_dialog (moon);

	gtk_window_set_screen (GTK_WINDOW (moon->moondata_dialog),
			       gtk_widget_get_screen (GTK_WIDGET (moon)));

	gtk_window_present (GTK_WINDOW (moon->moondata_dialog));

	g_object_unref (xml);
}


syntax highlighted by Code2HTML, v. 0.9.1