#include <stdio.h>
#include <time.h>
#include <string.h>
#include <glib.h>


typedef struct
{
  gint32     gmt_offset;
  gboolean   is_dst;
  gboolean   is_standard;
  gboolean   is_gmt;
  gchar     *abbrev;
} TransitionInfo;

typedef struct
{
  gint64 time;
  gint   info_index;
} Transition;


struct _GTimeZone
{
  gchar   *name;
  GArray  *t_info;
  GArray  *transitions;
  gint     ref_count;
};


inline static const TransitionInfo*
interval_info (GTimeZone *tz,
               gint       interval)
{
  guint index;
  if (interval <= 0)
    interval = 1;
  if (interval >= tz->transitions->len)
    interval = tz->transitions->len;
  index = (g_array_index (tz->transitions, Transition, interval - 1)).info_index;
  return &(g_array_index (tz->t_info, TransitionInfo, index));
}

inline static gint64
interval_start (GTimeZone *tz,
                gint       interval)
{
  if (!interval)
    return G_MININT64;
  if (interval > tz->transitions->len)
    interval = tz->transitions->len;
  return (g_array_index (tz->transitions, Transition, interval - 1)).time;
}

inline static gint64
interval_end (GTimeZone *tz,
              gint       interval)
{
  if (interval >= tz->transitions->len)
    return G_MAXINT64;
  return (g_array_index (tz->transitions, Transition, interval)).time - 1;
}

inline static gint32
interval_offset (GTimeZone *tz,
                 gint       interval)
{
  guint index;
  if (interval <= 0)
    interval = 1;
  if (interval > tz->transitions->len)
    interval = tz->transitions->len;
  index = (g_array_index (tz->transitions, Transition, interval - 1)).info_index;
  return (g_array_index (tz->t_info, TransitionInfo, index)).gmt_offset;
}

inline static gboolean
interval_isdst (GTimeZone *tz,
                gint       interval)
{
  return interval_info (tz, interval)->is_dst;
}

inline static gboolean
interval_isgmt (GTimeZone *tz,
                gint       interval)
{
  return interval_info (tz, interval)->is_gmt;
}

inline static gboolean
interval_isstandard (GTimeZone *tz,
                     gint       interval)
{
  return interval_info (tz, interval)->is_standard;
}

inline static gchar*
interval_abbrev (GTimeZone *tz,
                 gint       interval)
{
  return interval_info (tz, interval)->abbrev;
}

inline static gint64
interval_local_start (GTimeZone *tz,
                      gint       interval)
{
  if (interval)
    return interval_start (tz, interval) + interval_offset (tz, interval);

  return G_MININT64;
}

inline static gint64
interval_local_end (GTimeZone *tz,
                    gint       interval)
{
  if (interval < tz->transitions->len)
    return interval_end (tz, interval) + interval_offset (tz, interval);

  return G_MAXINT64;
}

static gboolean
interval_valid (GTimeZone *tz,
                gint       interval)
{
  return interval <= tz->transitions->len;
}


int
main (int argc, char *argv[])
{
	GTimeZone *tz;
	GDateTime *dt;
	gint64 t;
	gint32 offset;
	char *s;
	int i;
        TransitionInfo *info;
	Transition *trans;


	tz = g_time_zone_new (argv[1]);


	if (!tz->t_info)
	{
		g_printf ("Can't find or parse time zone \"%s\".\n", argv[1]);

		g_time_zone_unref (tz);

		return 1;
	}


	g_printf ("HEADER:\n\n");

	g_printf ("Transition Times:         %3d\n", tz->transitions->len);
	g_printf ("Local Time Types:         %3d\n", tz->t_info->len);

	g_printf ("\n\n");


	g_printf ("LOCAL TIME TYPES:\n\n");

	for (i = 0; i < tz->t_info->len; i++)
	{
		info = &(g_array_index (tz->t_info, TransitionInfo, i));
		g_printf ("Index:    %d\n", i);

		offset = info->gmt_offset;
		g_printf ("Offset:   %+03d%02d\n",
		          (int) offset / 3600,
			  (int) abs (offset / 60) % 60);

		g_printf ("DST:      %s\n", info->is_dst? "Yes" : "No");

		g_printf ("Abbr:     %s\n", info->abbrev);

		g_printf ("\n");
	}

	g_printf ("\n");


	g_printf ("TRANSITION TIMES:\n\n");

	for (i = 0; i <= tz->transitions->len; i++)
	{
		trans = &(g_array_index (tz->transitions, Transition, i? i - 1 : 0));

		g_printf ("Index:    %d\n", i);

		g_printf ("Type:     %d\n", trans->info_index);

		offset = interval_offset (tz, i);
		g_printf ("Offset:   %+03d%02d\n",
		          (int) offset / 3600,
			  (int) abs (offset / 60) % 60);

		g_printf ("DST:      %s\n", interval_isdst (tz, i)? "Yes" : "No");

		g_printf ("Abbr:     %s\n", interval_abbrev (tz, i));


		t = interval_local_start (tz, i);

		if (interval_start (tz, i) != G_MININT64)
		{
			dt = g_date_time_new_from_unix_utc (t);
			s = g_date_time_format (dt, "%c");
			g_printf ("Start:    %s\n", s? s : "(none)");
			g_free (s);
			g_date_time_unref (dt);

			g_printf ("          %" G_GINT64_FORMAT "\n", t);
		}
		else
			g_printf ("Start:    mininum\n");


		t = interval_local_end (tz, i);

		if (interval_end (tz, i) != G_MAXINT64)
		{
			dt = g_date_time_new_from_unix_utc (t);
			s = g_date_time_format (dt, "%c");
			g_printf ("End:      %s\n", s? s : "(none)");
			g_free (s);
			g_date_time_unref (dt);

			g_printf ("          %" G_GINT64_FORMAT "\n", t);
		}
		else
			g_printf ("End:      maximum\n");

		g_printf ("\n");
	}

	g_time_zone_unref (tz);

	return 0;
}
