/* timezone-history.c generated by valac 0.56.19, the Vala compiler
 * generated from timezone-history.vala, do not modify */

/*
 * Copyright (c) 2024-2025 focus-timer contributors
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 * Authors: Kamil Prusko <kamilprusko@gmail.com>
 */

#include <glib.h>
#include "ft_core.h"
#include <glib-object.h>
#include <gom/gom.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <math.h>

#define FT_TIMEZONE_HISTORY_FETCH_LIMIT ((guint) 50)
#if !defined(VALA_STRICT_C)
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ >= 14)
#pragma GCC diagnostic warning "-Wincompatible-pointer-types"
#elif defined(__clang__) && (__clang_major__ >= 16)
#pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
#endif
#endif

typedef struct _FtTimezoneMarker FtTimezoneMarker;
#define _g_time_zone_unref0(var) ((var == NULL) ? NULL : (var = (g_time_zone_unref (var), NULL)))
enum  {
	FT_TIMEZONE_HISTORY_0_PROPERTY,
	FT_TIMEZONE_HISTORY_NUM_PROPERTIES
};
static GParamSpec* ft_timezone_history_properties[FT_TIMEZONE_HISTORY_NUM_PROPERTIES];
#define _g_array_unref0(var) ((var == NULL) ? NULL : (var = (g_array_unref (var), NULL)))
#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))

#define FT_TYPE_TIMEZONE_ENTRY (ft_timezone_entry_get_type ())
#define FT_TIMEZONE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FT_TYPE_TIMEZONE_ENTRY, FtTimezoneEntry))
#define FT_TIMEZONE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FT_TYPE_TIMEZONE_ENTRY, FtTimezoneEntryClass))
#define FT_IS_TIMEZONE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FT_TYPE_TIMEZONE_ENTRY))
#define FT_IS_TIMEZONE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FT_TYPE_TIMEZONE_ENTRY))
#define FT_TIMEZONE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FT_TYPE_TIMEZONE_ENTRY, FtTimezoneEntryClass))

typedef struct _FtTimezoneEntry FtTimezoneEntry;
typedef struct _FtTimezoneEntryClass FtTimezoneEntryClass;
#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
#define _ft_timezone_marker_free0(var) ((var == NULL) ? NULL : (var = (ft_timezone_marker_free (var), NULL)))
#define _g_free0(var) (var = (g_free (var), NULL))
#define _g_date_time_unref0(var) ((var == NULL) ? NULL : (var = (g_date_time_unref (var), NULL)))
enum  {
	FT_TIMEZONE_HISTORY_CHANGED_SIGNAL,
	FT_TIMEZONE_HISTORY_NUM_SIGNALS
};
static guint ft_timezone_history_signals[FT_TIMEZONE_HISTORY_NUM_SIGNALS] = {0};
#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; }
#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);

struct _FtTimezoneMarker {
	gint64 timestamp;
	GTimeZone* timezone;
};

struct _FtTimezoneHistoryPrivate {
	GArray* data;
	gint64 fetched_timestamp;
	gboolean fetched_all;
};

static gint FtTimezoneHistory_private_offset;
static gpointer ft_timezone_history_parent_class = NULL;

VALA_EXTERN void ft_timezone_marker_free (FtTimezoneMarker * self);
static void ft_timezone_marker_instance_init (FtTimezoneMarker * self);
VALA_EXTERN FtTimezoneMarker* ft_timezone_marker_new (gint64 timestamp,
                                          GTimeZone* timezone);
static inline FtTimezoneMarker* ft_timezone_history_create_marker (FtTimezoneHistory* self,
                                                     gint64 timestamp,
                                                     GTimeZone* timezone);
static guint ft_timezone_history_fetch (FtTimezoneHistory* self);
VALA_EXTERN GType ft_timezone_entry_get_type (void) G_GNUC_CONST ;
VALA_EXTERN const gchar* ft_timezone_entry_get_identifier (FtTimezoneEntry* self);
VALA_EXTERN gint64 ft_timezone_entry_get_time (FtTimezoneEntry* self);
static gboolean ft_timezone_history_replace_in_database (FtTimezoneHistory* self,
                                                  gint64 timestamp,
                                                  GTimeZone* timezone);
VALA_EXTERN void ft_timezone_entry_set_identifier (FtTimezoneEntry* self,
                                       const gchar* value);
static inline void ft_timezone_history_search_internal (FtTimezoneHistory* self,
                                          gint64 timestamp,
                                          FtTimezoneMarker** marker,
                                          guint* index);
VALA_EXTERN FtTimezoneEntry* ft_timezone_entry_new (void);
VALA_EXTERN FtTimezoneEntry* ft_timezone_entry_construct (GType object_type);
VALA_EXTERN void ft_timezone_entry_set_time (FtTimezoneEntry* self,
                                 gint64 value);
static inline void ft_timezone_history_split_timezone (FtTimezoneHistory* self,
                                         gint64 start_time,
                                         gint64 end_time,
                                         GTimeZone* timezone,
                                         FtTimezoneScanFunc func,
                                         gpointer func_target);
VALA_EXTERN gboolean ft_is_test (void);
static void _ft_timezone_marker_free0_ (gpointer var);
static void _vala_FtTimezoneMarker_free_function_content_of (gpointer data);
static void ft_timezone_history_real_dispose (GObject* base);
static GObject * ft_timezone_history_constructor (GType type,
                                           guint n_construct_properties,
                                           GObjectConstructParam * construct_properties);
static void ft_timezone_history_finalize (GObject * obj);
static GType ft_timezone_history_get_type_once (void);
static inline gpointer _vala_memdup2 (gconstpointer mem,
                        gsize byte_size);

static gpointer
_g_time_zone_ref0 (gpointer self)
{
	return self ? g_time_zone_ref (self) : NULL;
}

FtTimezoneMarker*
ft_timezone_marker_new (gint64 timestamp,
                        GTimeZone* timezone)
{
	FtTimezoneMarker* self;
	GTimeZone* _tmp0_;
	g_return_val_if_fail (timezone != NULL, NULL);
	self = g_slice_new0 (FtTimezoneMarker);
	ft_timezone_marker_instance_init (self);
	self->timestamp = timestamp;
	_tmp0_ = _g_time_zone_ref0 (timezone);
	_g_time_zone_unref0 (self->timezone);
	self->timezone = _tmp0_;
	return self;
}

static void
ft_timezone_marker_instance_init (FtTimezoneMarker * self)
{
}

void
ft_timezone_marker_free (FtTimezoneMarker * self)
{
	_g_time_zone_unref0 (self->timezone);
	self->timezone = NULL;
	_g_time_zone_unref0 (self->timezone);
	g_slice_free (FtTimezoneMarker, self);
}

static inline gpointer
ft_timezone_history_get_instance_private (FtTimezoneHistory* self)
{
	return G_STRUCT_MEMBER_P (self, FtTimezoneHistory_private_offset);
}

static inline FtTimezoneMarker*
ft_timezone_history_create_marker (FtTimezoneHistory* self,
                                   gint64 timestamp,
                                   GTimeZone* timezone)
{
	FtTimezoneMarker* _tmp0_;
	FtTimezoneMarker* result;
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (timezone != NULL, NULL);
	_tmp0_ = ft_timezone_marker_new (timestamp, timezone);
	result = _tmp0_;
	return result;
}

/**
         * Try to fill in `data`.
         *
         * Returns the number of fetched entries.
         */
static gpointer
_g_object_ref0 (gpointer self)
{
	return self ? g_object_ref (self) : NULL;
}

static GTimeZone*
vala_g_time_zone_new_identifier (const gchar* identifier,
                                 GError** error)
{
	GTimeZone* self;
	GError* _inner_error0_ = NULL;
	self = (GTimeZone*) g_time_zone_new_identifier (identifier);
	if (((GTimeZone*) self) == NULL) {
		GError* _tmp0_;
		_tmp0_ = g_error_new_literal (G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "Invalid identifier argument");
		_inner_error0_ = _tmp0_;
		g_propagate_error (error, _inner_error0_);
		_g_time_zone_unref0 (self);
		return NULL;
	}
	return self;
}

static guint
ft_timezone_history_fetch (FtTimezoneHistory* self)
{
	GomFilter* filter = NULL;
	GomSorting* sorting = NULL;
	GObject* _tmp2_;
	GObject* _tmp3_;
	GomSorting* _tmp4_;
	GomRepository* repository = NULL;
	GomRepository* _tmp5_;
	GomRepository* _tmp6_;
	GError* _inner_error0_ = NULL;
	guint result;
	g_return_val_if_fail (self != NULL, 0U);
	filter = NULL;
	if (self->priv->fetched_all) {
		result = 0U;
		_g_object_unref0 (filter);
		return result;
	}
	if (ft_timestamp_is_defined (self->priv->fetched_timestamp)) {
		GValue _tmp0_ = {0};
		GomFilter* _tmp1_;
		g_value_init (&_tmp0_, G_TYPE_INT64);
		g_value_set_int64 (&_tmp0_, self->priv->fetched_timestamp);
		_tmp1_ = gom_filter_new_lt (FT_TYPE_TIMEZONE_ENTRY, "time", &_tmp0_);
		g_object_ref_sink (_tmp1_);
		_g_object_unref0 (filter);
		filter = _tmp1_;
		G_IS_VALUE (&_tmp0_) ? (g_value_unset (&_tmp0_), NULL) : NULL;
	}
	_tmp2_ = g_object_new (gom_sorting_get_type (), NULL);
	_tmp3_ = G_IS_INITIALLY_UNOWNED (_tmp2_) ? g_object_ref_sink (_tmp2_) : _tmp2_;
	sorting = G_TYPE_CHECK_INSTANCE_CAST (_tmp3_, gom_sorting_get_type (), GomSorting);
	_tmp4_ = sorting;
	gom_sorting_add (_tmp4_, FT_TYPE_TIMEZONE_ENTRY, "time", GOM_SORTING_DESCENDING);
	_tmp5_ = ft_database_get_repository ();
	_tmp6_ = _g_object_ref0 (_tmp5_);
	repository = _tmp6_;
	{
		GomResourceGroup* results = NULL;
		GomRepository* _tmp7_;
		GomFilter* _tmp8_;
		GomSorting* _tmp9_;
		GomResourceGroup* _tmp10_;
		guint results_count = 0U;
		GomResourceGroup* _tmp11_;
		guint _tmp12_;
		guint _tmp13_;
		guint fetch_count = 0U;
		GomResourceGroup* _tmp14_;
		_tmp7_ = repository;
		_tmp8_ = filter;
		_tmp9_ = sorting;
		_tmp10_ = gom_repository_find_sorted_sync (_tmp7_, FT_TYPE_TIMEZONE_ENTRY, _tmp8_, _tmp9_, &_inner_error0_);
		results = _tmp10_;
		if (G_UNLIKELY (_inner_error0_ != NULL)) {
			goto __catch0_g_error;
		}
		_tmp11_ = results;
		_tmp12_ = gom_resource_group_get_count (_tmp11_);
		_tmp13_ = _tmp12_;
		results_count = _tmp13_;
		fetch_count = MIN (results_count, FT_TIMEZONE_HISTORY_FETCH_LIMIT);
		_tmp14_ = results;
		gom_resource_group_fetch_sync (_tmp14_, (guint) 0, fetch_count, &_inner_error0_);
		if (G_UNLIKELY (_inner_error0_ != NULL)) {
			_g_object_unref0 (results);
			goto __catch0_g_error;
		}
		{
			gint index = 0;
			index = 0;
			{
				gboolean _tmp15_ = FALSE;
				_tmp15_ = TRUE;
				while (TRUE) {
					FtTimezoneEntry* entry = NULL;
					GomResourceGroup* _tmp17_;
					GomResource* _tmp18_;
					FtTimezoneEntry* _tmp19_;
					GTimeZone* _tmp20_ = NULL;
					FtTimezoneEntry* _tmp21_;
					const gchar* _tmp22_;
					const gchar* _tmp23_;
					GTimeZone* _tmp24_;
					GArray* _tmp25_;
					FtTimezoneEntry* _tmp26_;
					gint64 _tmp27_;
					gint64 _tmp28_;
					FtTimezoneMarker* _tmp29_;
					FtTimezoneEntry* _tmp30_;
					gint64 _tmp31_;
					gint64 _tmp32_;
					if (!_tmp15_) {
						gint _tmp16_;
						_tmp16_ = index;
						index = _tmp16_ + 1;
					}
					_tmp15_ = FALSE;
					if (!(((guint) index) < fetch_count)) {
						break;
					}
					_tmp17_ = results;
					_tmp18_ = gom_resource_group_get_index (_tmp17_, (guint) index);
					_tmp19_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_CAST (_tmp18_, FT_TYPE_TIMEZONE_ENTRY, FtTimezoneEntry));
					entry = _tmp19_;
					_tmp21_ = entry;
					_tmp22_ = ft_timezone_entry_get_identifier (_tmp21_);
					_tmp23_ = _tmp22_;
					_tmp24_ = vala_g_time_zone_new_identifier (_tmp23_, &_inner_error0_);
					_tmp20_ = _tmp24_;
					if (G_UNLIKELY (_inner_error0_ != NULL)) {
						_g_object_unref0 (entry);
						_g_object_unref0 (results);
						goto __catch0_g_error;
					}
					_tmp25_ = self->priv->data;
					_tmp26_ = entry;
					_tmp27_ = ft_timezone_entry_get_time (_tmp26_);
					_tmp28_ = _tmp27_;
					_tmp29_ = ft_timezone_history_create_marker (self, _tmp28_, _tmp20_);
					g_array_append_val (_tmp25_, _tmp29_);
					_tmp30_ = entry;
					_tmp31_ = ft_timezone_entry_get_time (_tmp30_);
					_tmp32_ = _tmp31_;
					self->priv->fetched_timestamp = _tmp32_;
					_g_time_zone_unref0 (_tmp20_);
					_g_object_unref0 (entry);
				}
			}
		}
		if (results_count < FT_TIMEZONE_HISTORY_FETCH_LIMIT) {
			self->priv->fetched_all = TRUE;
		}
		result = fetch_count;
		_g_object_unref0 (results);
		_g_object_unref0 (repository);
		_g_object_unref0 (sorting);
		_g_object_unref0 (filter);
		return result;
	}
	goto __finally0;
	__catch0_g_error:
	{
		GError* _error_ = NULL;
		GError* _tmp33_;
		const gchar* _tmp34_;
		_error_ = _inner_error0_;
		_inner_error0_ = NULL;
		_tmp33_ = _error_;
		_tmp34_ = _tmp33_->message;
		g_warning ("timezone-history.vala:116: Failed to fetch timezones: %s", _tmp34_);
		_g_error_free0 (_error_);
	}
	__finally0:
	if (G_UNLIKELY (_inner_error0_ != NULL)) {
		guint _tmp35_ = 0U;
		_g_object_unref0 (repository);
		_g_object_unref0 (sorting);
		_g_object_unref0 (filter);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error0_->message, g_quark_to_string (_inner_error0_->domain), _inner_error0_->code);
		g_clear_error (&_inner_error0_);
		return _tmp35_;
	}
	result = 0U;
	_g_object_unref0 (repository);
	_g_object_unref0 (sorting);
	_g_object_unref0 (filter);
	return result;
}

static gboolean
ft_timezone_history_replace_in_database (FtTimezoneHistory* self,
                                         gint64 timestamp,
                                         GTimeZone* timezone)
{
	GValue timestamp_value = {0};
	GValue _tmp0_ = {0};
	GomRepository* repository = NULL;
	GomRepository* _tmp1_;
	GomRepository* _tmp2_;
	GomFilter* filter = NULL;
	GValue _tmp3_;
	GomFilter* _tmp4_;
	GError* _inner_error0_ = NULL;
	gboolean result;
	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (timezone != NULL, FALSE);
	g_value_init (&_tmp0_, G_TYPE_INT64);
	timestamp_value = _tmp0_;
	g_value_set_int64 (&timestamp_value, timestamp);
	_tmp1_ = ft_database_get_repository ();
	_tmp2_ = _g_object_ref0 (_tmp1_);
	repository = _tmp2_;
	_tmp3_ = timestamp_value;
	_tmp4_ = gom_filter_new_eq (FT_TYPE_TIMEZONE_ENTRY, "time", &_tmp3_);
	g_object_ref_sink (_tmp4_);
	filter = _tmp4_;
	{
		GomResource* _tmp5_ = NULL;
		GomRepository* _tmp6_;
		GomFilter* _tmp7_;
		GomResource* _tmp8_;
		FtTimezoneEntry* entry = NULL;
		FtTimezoneEntry* _tmp9_;
		FtTimezoneEntry* _tmp10_;
		FtTimezoneEntry* _tmp11_;
		const gchar* _tmp12_;
		FtTimezoneEntry* _tmp13_;
		_tmp6_ = repository;
		_tmp7_ = filter;
		_tmp8_ = gom_repository_find_one_sync (_tmp6_, FT_TYPE_TIMEZONE_ENTRY, _tmp7_, &_inner_error0_);
		_tmp5_ = _tmp8_;
		if (G_UNLIKELY (_inner_error0_ != NULL)) {
			goto __catch0_g_error;
		}
		_tmp9_ = _g_object_ref0 (G_TYPE_CHECK_INSTANCE_CAST (_tmp5_, FT_TYPE_TIMEZONE_ENTRY, FtTimezoneEntry));
		entry = _tmp9_;
		_tmp10_ = entry;
		if (_tmp10_ == NULL) {
			result = FALSE;
			_g_object_unref0 (entry);
			_g_object_unref0 (_tmp5_);
			_g_object_unref0 (filter);
			_g_object_unref0 (repository);
			G_IS_VALUE (&timestamp_value) ? (g_value_unset (&timestamp_value), NULL) : NULL;
			return result;
		}
		_tmp11_ = entry;
		_tmp12_ = g_time_zone_get_identifier (timezone);
		ft_timezone_entry_set_identifier (_tmp11_, _tmp12_);
		_tmp13_ = entry;
		gom_resource_save_sync ((GomResource*) _tmp13_, &_inner_error0_);
		if (G_UNLIKELY (_inner_error0_ != NULL)) {
			_g_object_unref0 (entry);
			_g_object_unref0 (_tmp5_);
			goto __catch0_g_error;
		}
		_g_object_unref0 (entry);
		_g_object_unref0 (_tmp5_);
	}
	goto __finally0;
	__catch0_g_error:
	{
		g_clear_error (&_inner_error0_);
		result = FALSE;
		_g_object_unref0 (filter);
		_g_object_unref0 (repository);
		G_IS_VALUE (&timestamp_value) ? (g_value_unset (&timestamp_value), NULL) : NULL;
		return result;
	}
	__finally0:
	if (G_UNLIKELY (_inner_error0_ != NULL)) {
		gboolean _tmp14_ = FALSE;
		_g_object_unref0 (filter);
		_g_object_unref0 (repository);
		G_IS_VALUE (&timestamp_value) ? (g_value_unset (&timestamp_value), NULL) : NULL;
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error0_->message, g_quark_to_string (_inner_error0_->domain), _inner_error0_->code);
		g_clear_error (&_inner_error0_);
		return _tmp14_;
	}
	result = TRUE;
	_g_object_unref0 (filter);
	_g_object_unref0 (repository);
	G_IS_VALUE (&timestamp_value) ? (g_value_unset (&timestamp_value), NULL) : NULL;
	return result;
}

static gpointer
vala_g_array_remove_index (GArray* self,
                           guint index)
{
	gpointer g = NULL;
	gpointer* _tmp0_;
	gint _tmp0__length1;
	gpointer _tmp1_;
	gpointer result;
	g_return_val_if_fail (self != NULL, NULL);
	_vala_assert (self->len > index, "length > index");
	_tmp0_ = self->data;
	_tmp0__length1 = self->len;
	_tmp1_ = _tmp0_[index];
	_tmp0_[index] = NULL;
	g = _tmp1_;
	g_array_remove_index (self, index);
	result = g;
	return result;
}

void
ft_timezone_history_insert (FtTimezoneHistory* self,
                            gint64 timestamp,
                            GTimeZone* timezone)
{
	FtTimezoneMarker* existing_marker = NULL;
	guint index = 0U;
	FtTimezoneMarker* _tmp0_ = NULL;
	guint _tmp1_ = 0U;
	FtTimezoneMarker* _tmp2_;
	FtTimezoneEntry* entry = NULL;
	FtTimezoneEntry* _tmp15_;
	FtTimezoneEntry* _tmp16_;
	GomRepository* _tmp17_;
	FtTimezoneEntry* _tmp18_;
	FtTimezoneEntry* _tmp19_;
	const gchar* _tmp20_;
	GError* _inner_error0_ = NULL;
	g_return_if_fail (self != NULL);
	g_return_if_fail (timezone != NULL);
	_vala_return_if_fail (ft_timestamp_is_defined (timestamp), "Ft.Timestamp.is_defined (timestamp)");
	ft_timezone_history_search_internal (self, timestamp, &_tmp0_, &_tmp1_);
	existing_marker = _tmp0_;
	index = _tmp1_;
	_tmp2_ = existing_marker;
	if (_tmp2_ != NULL) {
		FtTimezoneMarker* _tmp3_;
		GTimeZone* _tmp4_;
		const gchar* _tmp5_;
		const gchar* _tmp6_;
		FtTimezoneMarker* _tmp7_;
		GArray* _tmp11_;
		FtTimezoneMarker* _tmp12_;
		_tmp3_ = existing_marker;
		_tmp4_ = _tmp3_->timezone;
		_tmp5_ = g_time_zone_get_identifier (_tmp4_);
		_tmp6_ = g_time_zone_get_identifier (timezone);
		if (g_strcmp0 (_tmp5_, _tmp6_) == 0) {
			return;
		}
		_tmp7_ = existing_marker;
		if (_tmp7_->timestamp == timestamp) {
			GArray* _tmp8_;
			FtTimezoneMarker* _tmp9_;
			FtTimezoneMarker* _tmp10_;
			_tmp8_ = self->priv->data;
			_tmp9_ = vala_g_array_remove_index (_tmp8_, index);
			_tmp10_ = _tmp9_;
			_ft_timezone_marker_free0 (_tmp10_);
		}
		_tmp11_ = self->priv->data;
		_tmp12_ = ft_timezone_history_create_marker (self, timestamp, timezone);
		g_array_insert_val (_tmp11_, index, _tmp12_);
	} else {
		GArray* _tmp13_;
		FtTimezoneMarker* _tmp14_;
		_tmp13_ = self->priv->data;
		_tmp14_ = ft_timezone_history_create_marker (self, timestamp, timezone);
		g_array_append_val (_tmp13_, _tmp14_);
	}
	_tmp15_ = ft_timezone_entry_new ();
	entry = _tmp15_;
	_tmp16_ = entry;
	_tmp17_ = ft_database_get_repository ();
	g_object_set ((GomResource*) _tmp16_, "repository", _tmp17_, NULL);
	_tmp18_ = entry;
	ft_timezone_entry_set_time (_tmp18_, timestamp);
	_tmp19_ = entry;
	_tmp20_ = g_time_zone_get_identifier (timezone);
	ft_timezone_entry_set_identifier (_tmp19_, _tmp20_);
	{
		FtTimezoneEntry* _tmp21_;
		_tmp21_ = entry;
		gom_resource_save_sync ((GomResource*) _tmp21_, &_inner_error0_);
		if (G_UNLIKELY (_inner_error0_ != NULL)) {
			goto __catch0_g_error;
		}
		g_signal_emit (self, ft_timezone_history_signals[FT_TIMEZONE_HISTORY_CHANGED_SIGNAL], 0);
	}
	goto __finally0;
	__catch0_g_error:
	{
		GError* _error_ = NULL;
		_error_ = _inner_error0_;
		_inner_error0_ = NULL;
		if (!ft_timezone_history_replace_in_database (self, timestamp, timezone)) {
			const gchar* _tmp22_;
			gchar* _tmp23_;
			gchar* _tmp24_;
			GError* _tmp25_;
			const gchar* _tmp26_;
			_tmp22_ = g_time_zone_get_identifier (timezone);
			_tmp23_ = g_strdup_printf ("%" G_GINT64_FORMAT, timestamp);
			_tmp24_ = _tmp23_;
			_tmp25_ = _error_;
			_tmp26_ = _tmp25_->message;
			g_warning ("timezone-history.vala:189: Failed to save timezone %s at %s: %s", _tmp22_, _tmp24_, _tmp26_);
			_g_free0 (_tmp24_);
		}
		_g_error_free0 (_error_);
	}
	__finally0:
	if (G_UNLIKELY (_inner_error0_ != NULL)) {
		_g_object_unref0 (entry);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error0_->message, g_quark_to_string (_inner_error0_->domain), _inner_error0_->code);
		g_clear_error (&_inner_error0_);
		return;
	}
	_g_object_unref0 (entry);
}

static inline void
ft_timezone_history_search_internal (FtTimezoneHistory* self,
                                     gint64 timestamp,
                                     FtTimezoneMarker** marker,
                                     guint* index)
{
	FtTimezoneMarker* _vala_marker = NULL;
	guint _vala_index = 0U;
	guint _index = 0U;
	g_return_if_fail (self != NULL);
	_vala_marker = NULL;
	_vala_index = 0U;
	_index = _vala_index;
	{
		gboolean _tmp0_ = FALSE;
		_tmp0_ = TRUE;
		while (TRUE) {
			if (!_tmp0_) {
				if (!(ft_timezone_history_fetch (self) > ((guint) 0))) {
					break;
				}
			}
			_tmp0_ = FALSE;
			while (TRUE) {
				GArray* _tmp1_;
				FtTimezoneMarker* _marker = NULL;
				GArray* _tmp2_;
				FtTimezoneMarker* _tmp3_;
				FtTimezoneMarker* _tmp4_;
				guint _tmp6_;
				_tmp1_ = self->priv->data;
				if (!(_index < _tmp1_->len)) {
					break;
				}
				_tmp2_ = self->priv->data;
				_tmp3_ = g_array_index (_tmp2_, FtTimezoneMarker*, _index);
				_marker = _tmp3_;
				_tmp4_ = _marker;
				if (timestamp >= _tmp4_->timestamp) {
					FtTimezoneMarker* _tmp5_;
					_tmp5_ = _marker;
					_vala_marker = _tmp5_;
					_vala_index = _index;
					if (marker) {
						*marker = _vala_marker;
					}
					if (index) {
						*index = _vala_index;
					}
					return;
				}
				_tmp6_ = _index;
				_index = _tmp6_ + 1;
			}
		}
	}
	if (marker) {
		*marker = _vala_marker;
	}
	if (index) {
		*index = _vala_index;
	}
}

GTimeZone*
ft_timezone_history_search (FtTimezoneHistory* self,
                            gint64 timestamp)
{
	FtTimezoneMarker* marker = NULL;
	FtTimezoneMarker* _tmp0_ = NULL;
	FtTimezoneMarker* _tmp1_ = NULL;
	FtTimezoneMarker* _tmp2_;
	GTimeZone* _tmp3_ = NULL;
	GTimeZone* result;
	g_return_val_if_fail (self != NULL, NULL);
	ft_timezone_history_search_internal (self, timestamp, &_tmp0_, NULL);
	marker = _tmp0_;
	_tmp2_ = marker;
	_tmp1_ = _tmp2_;
	_tmp3_ = NULL;
	if (_tmp1_ != NULL) {
		GTimeZone* _tmp4_;
		_tmp4_ = _tmp1_->timezone;
		_tmp3_ = _tmp4_;
	}
	result = _tmp3_;
	return result;
}

GTimeZone*
ft_timezone_history_search_by_date (FtTimezoneHistory* self,
                                    GDate* date,
                                    gint64 offset)
{
	FtTimezoneMarker* marker = NULL;
	FtTimezoneMarker* last_valid_marker = NULL;
	guint index = 0U;
	GDateTime* estimated_datetime = NULL;
	GDateTime* _tmp0_;
	GDateTime* _tmp1_;
	GDateTime* _tmp2_;
	GDateTime* _tmp3_;
	gint64 estimated_timestamp = 0LL;
	GDateTime* _tmp4_;
	FtTimezoneMarker* _tmp5_ = NULL;
	guint _tmp6_ = 0U;
	gboolean _tmp7_ = FALSE;
	FtTimezoneMarker* _tmp8_;
	FtTimezoneMarker* _tmp27_ = NULL;
	FtTimezoneMarker* _tmp28_;
	GTimeZone* _tmp29_ = NULL;
	GTimeZone* result;
	g_return_val_if_fail (self != NULL, NULL);
	g_return_val_if_fail (date != NULL, NULL);
	marker = NULL;
	last_valid_marker = NULL;
	_tmp0_ = g_date_time_new_utc ((gint) g_date_get_year (date), (gint) g_date_get_month (date), (gint) g_date_get_day (date), 0, 0, (gdouble) 0);
	estimated_datetime = _tmp0_;
	_tmp1_ = estimated_datetime;
	_tmp2_ = g_date_time_add_days (_tmp1_, -2);
	_tmp3_ = _tmp2_;
	_g_date_time_unref0 (_tmp3_);
	_tmp4_ = estimated_datetime;
	estimated_timestamp = g_date_time_to_unix (_tmp4_);
	ft_timezone_history_search_internal (self, estimated_timestamp, &_tmp5_, &_tmp6_);
	marker = _tmp5_;
	index = _tmp6_;
	_tmp8_ = marker;
	if (_tmp8_ == NULL) {
		GArray* _tmp9_;
		_tmp9_ = self->priv->data;
		_tmp7_ = _tmp9_->len > ((guint) 0);
	} else {
		_tmp7_ = FALSE;
	}
	if (_tmp7_) {
		GArray* _tmp10_;
		GArray* _tmp11_;
		FtTimezoneMarker* _tmp12_;
		_tmp10_ = self->priv->data;
		index = _tmp10_->len - 1;
		_tmp11_ = self->priv->data;
		_tmp12_ = g_array_index (_tmp11_, FtTimezoneMarker*, index);
		marker = _tmp12_;
	}
	while (TRUE) {
		FtTimezoneMarker* _tmp13_;
		GDateTime* datetime = NULL;
		FtTimezoneMarker* _tmp14_;
		GTimeZone* _tmp15_;
		GDateTime* _tmp16_;
		FtTimezoneMarker* _tmp19_;
		GDateTime* _tmp20_;
		FtTimezoneMarker* _tmp21_;
		guint _tmp22_;
		FtTimezoneMarker* _tmp23_ = NULL;
		GArray* _tmp24_;
		_tmp13_ = marker;
		if (!(_tmp13_ != NULL)) {
			break;
		}
		_tmp14_ = marker;
		_tmp15_ = _tmp14_->timezone;
		_tmp16_ = g_date_time_new (_tmp15_, (gint) g_date_get_year (date), (gint) g_date_get_month (date), (gint) g_date_get_day (date), 0, 0, (gdouble) 0);
		datetime = _tmp16_;
		if (offset != ((gint64) 0)) {
			GDateTime* _tmp17_;
			GDateTime* _tmp18_;
			_tmp17_ = datetime;
			_tmp18_ = g_date_time_add_seconds (_tmp17_, ft_interval_to_seconds (offset));
			_g_date_time_unref0 (datetime);
			datetime = _tmp18_;
		}
		_tmp19_ = marker;
		_tmp20_ = datetime;
		if (_tmp19_->timestamp > g_date_time_to_unix (_tmp20_)) {
			_g_date_time_unref0 (datetime);
			break;
		}
		_tmp21_ = marker;
		last_valid_marker = _tmp21_;
		_tmp22_ = index;
		index = _tmp22_ + 1;
		_tmp24_ = self->priv->data;
		if (index < _tmp24_->len) {
			GArray* _tmp25_;
			FtTimezoneMarker* _tmp26_;
			_tmp25_ = self->priv->data;
			_tmp26_ = g_array_index (_tmp25_, FtTimezoneMarker*, index);
			_tmp23_ = _tmp26_;
		} else {
			_tmp23_ = NULL;
		}
		marker = _tmp23_;
		_g_date_time_unref0 (datetime);
	}
	_tmp28_ = last_valid_marker;
	_tmp27_ = _tmp28_;
	_tmp29_ = NULL;
	if (_tmp27_ != NULL) {
		GTimeZone* _tmp30_;
		_tmp30_ = _tmp27_->timezone;
		_tmp29_ = _tmp30_;
	}
	result = _tmp29_;
	_g_date_time_unref0 (estimated_datetime);
	return result;
}

/**
         * Try to find fist occurrence of an timezone offset change.
         * It only considers one such occurrence for a given time range. For our purposes
         * it's good enough - we need it to be reliable up to a day, preferably up to a month.
         */
static inline void
ft_timezone_history_split_timezone (FtTimezoneHistory* self,
                                    gint64 start_time,
                                    gint64 end_time,
                                    GTimeZone* timezone,
                                    FtTimezoneScanFunc func,
                                    gpointer func_target)
{
	gint start_interval_id = 0;
	gint end_interval_id = 0;
	gint32 start_offset = 0;
	gint32 end_offset = 0;
	g_return_if_fail (self != NULL);
	g_return_if_fail (timezone != NULL);
	start_interval_id = g_time_zone_find_interval (timezone, G_TIME_TYPE_UNIVERSAL, start_time / FT_INTERVAL_SECOND);
	end_interval_id = g_time_zone_find_interval (timezone, G_TIME_TYPE_UNIVERSAL, end_time / FT_INTERVAL_SECOND);
	start_offset = g_time_zone_get_offset (timezone, start_interval_id);
	end_offset = g_time_zone_get_offset (timezone, end_interval_id);
	if (start_offset != end_offset) {
		gint64 range_start_time = 0LL;
		gint64 range_end_time = 0LL;
		gint64 range_mid_time = 0LL;
		gint32 range_mid_offset = 0;
		gint64 split_time = 0LL;
		range_start_time = start_time / FT_INTERVAL_SECOND;
		range_end_time = end_time / FT_INTERVAL_SECOND;
		range_mid_time = range_start_time;
		range_mid_offset = start_offset;
		while (TRUE) {
			if (!(range_start_time < range_end_time)) {
				break;
			}
			range_mid_time = range_start_time + ((range_end_time - range_start_time) / 2);
			range_mid_offset = g_time_zone_get_offset (timezone, g_time_zone_find_interval (timezone, G_TIME_TYPE_UNIVERSAL, range_mid_time));
			if (range_mid_offset != start_offset) {
				range_end_time = range_mid_time;
			} else {
				range_start_time = range_mid_time + 1;
			}
		}
		if ((range_mid_time % 1800) == ((gint64) 1799)) {
			range_mid_time += (gint64) 1;
		}
		split_time = range_mid_time * FT_INTERVAL_SECOND;
		if (start_time < split_time) {
			func (start_time, split_time, timezone, func_target);
		}
		if (split_time < end_time) {
			func (split_time, end_time, timezone, func_target);
		}
	} else {
		func (start_time, end_time, timezone, func_target);
	}
}

void
ft_timezone_history_scan (FtTimezoneHistory* self,
                          gint64 start_time,
                          gint64 end_time,
                          FtTimezoneScanFunc func,
                          gpointer func_target)
{
	FtTimezoneMarker* marker = NULL;
	FtTimezoneMarker* next_marker = NULL;
	guint index = 0U;
	FtTimezoneMarker* _tmp0_ = NULL;
	guint _tmp1_ = 0U;
	gboolean _tmp2_ = FALSE;
	FtTimezoneMarker* _tmp3_;
	g_return_if_fail (self != NULL);
	if (start_time > end_time) {
		return;
	}
	ft_timezone_history_search_internal (self, start_time, &_tmp0_, &_tmp1_);
	marker = _tmp0_;
	index = _tmp1_;
	_tmp3_ = marker;
	if (_tmp3_ == NULL) {
		_tmp2_ = !ft_is_test ();
	} else {
		_tmp2_ = FALSE;
	}
	if (_tmp2_) {
		gint64 _tmp4_ = 0LL;
		FtTimezoneMarker* _tmp5_;
		GTimeZone* _tmp7_;
		GTimeZone* _tmp8_;
		_tmp5_ = marker;
		if (_tmp5_ != NULL) {
			FtTimezoneMarker* _tmp6_;
			_tmp6_ = marker;
			_tmp4_ = _tmp6_->timestamp;
		} else {
			_tmp4_ = end_time;
		}
		_tmp7_ = g_time_zone_new_local ();
		_tmp8_ = _tmp7_;
		func (start_time, _tmp4_, _tmp8_, func_target);
		_g_time_zone_unref0 (_tmp8_);
		return;
	}
	while (TRUE) {
		gboolean _tmp9_ = FALSE;
		FtTimezoneMarker* _tmp10_;
		FtTimezoneMarker* _tmp12_ = NULL;
		gint64 _tmp15_ = 0LL;
		FtTimezoneMarker* _tmp16_;
		FtTimezoneMarker* _tmp18_;
		FtTimezoneMarker* _tmp19_;
		GTimeZone* _tmp20_;
		FtTimezoneMarker* _tmp21_;
		_tmp10_ = marker;
		if (_tmp10_ != NULL) {
			FtTimezoneMarker* _tmp11_;
			_tmp11_ = marker;
			_tmp9_ = _tmp11_->timestamp < end_time;
		} else {
			_tmp9_ = FALSE;
		}
		if (!_tmp9_) {
			break;
		}
		if (index >= ((guint) 1)) {
			GArray* _tmp13_;
			FtTimezoneMarker* _tmp14_;
			_tmp13_ = self->priv->data;
			_tmp14_ = g_array_index (_tmp13_, FtTimezoneMarker*, index - 1U);
			_tmp12_ = _tmp14_;
		} else {
			_tmp12_ = NULL;
		}
		next_marker = _tmp12_;
		_tmp16_ = next_marker;
		if (_tmp16_ != NULL) {
			FtTimezoneMarker* _tmp17_;
			_tmp17_ = next_marker;
			_tmp15_ = MIN (_tmp17_->timestamp, end_time);
		} else {
			_tmp15_ = end_time;
		}
		_tmp18_ = marker;
		_tmp19_ = marker;
		_tmp20_ = _tmp19_->timezone;
		ft_timezone_history_split_timezone (self, MAX (_tmp18_->timestamp, start_time), _tmp15_, _tmp20_, func, func_target);
		_tmp21_ = next_marker;
		if (_tmp21_ != NULL) {
			FtTimezoneMarker* _tmp22_;
			guint _tmp23_;
			_tmp22_ = next_marker;
			marker = _tmp22_;
			_tmp23_ = index;
			index = _tmp23_ - 1;
		} else {
			break;
		}
	}
}

static void
_ft_timezone_marker_free0_ (gpointer var)
{
	(var == NULL) ? NULL : (var = (ft_timezone_marker_free (var), NULL));
}

static void
_vala_FtTimezoneMarker_free_function_content_of (gpointer data)
{
	FtTimezoneMarker* self;
	self = *((FtTimezoneMarker**) data);
	_ft_timezone_marker_free0_ (self);
}

void
ft_timezone_history_clear_cache (FtTimezoneHistory* self)
{
	GArray* _tmp0_;
	g_return_if_fail (self != NULL);
	_tmp0_ = g_array_new (TRUE, TRUE, sizeof (FtTimezoneMarker*));
	g_array_set_clear_func (_tmp0_, (GDestroyNotify) _vala_FtTimezoneMarker_free_function_content_of);
	_g_array_unref0 (self->priv->data);
	self->priv->data = _tmp0_;
	self->priv->fetched_timestamp = FT_TIMESTAMP_UNDEFINED;
	self->priv->fetched_all = FALSE;
}

static void
ft_timezone_history_real_dispose (GObject* base)
{
	FtTimezoneHistory * self;
	self = (FtTimezoneHistory*) base;
	_g_array_unref0 (self->priv->data);
	self->priv->data = NULL;
	G_OBJECT_CLASS (ft_timezone_history_parent_class)->dispose (G_TYPE_CHECK_INSTANCE_CAST (self, G_TYPE_OBJECT, GObject));
}

FtTimezoneHistory*
ft_timezone_history_construct (GType object_type)
{
	FtTimezoneHistory * self = NULL;
	self = (FtTimezoneHistory*) g_object_new (object_type, NULL);
	return self;
}

FtTimezoneHistory*
ft_timezone_history_new (void)
{
	return ft_timezone_history_construct (FT_TYPE_TIMEZONE_HISTORY);
}

static GObject *
ft_timezone_history_constructor (GType type,
                                 guint n_construct_properties,
                                 GObjectConstructParam * construct_properties)
{
	GObject * obj;
	GObjectClass * parent_class;
	static GWeakRef FtTimezoneHistory_singleton__ref;
	static GMutex FtTimezoneHistory_singleton__lock;
	FtTimezoneHistory * self;
	GArray* _tmp0_;
	g_mutex_lock (&FtTimezoneHistory_singleton__lock);
	obj = g_weak_ref_get (&FtTimezoneHistory_singleton__ref);
	if (obj != NULL) {
		g_mutex_unlock (&FtTimezoneHistory_singleton__lock);
		return obj;
	}
	parent_class = G_OBJECT_CLASS (ft_timezone_history_parent_class);
	obj = parent_class->constructor (type, n_construct_properties, construct_properties);
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, FT_TYPE_TIMEZONE_HISTORY, FtTimezoneHistory);
	_tmp0_ = g_array_new (TRUE, TRUE, sizeof (FtTimezoneMarker*));
	g_array_set_clear_func (_tmp0_, (GDestroyNotify) _vala_FtTimezoneMarker_free_function_content_of);
	_g_array_unref0 (self->priv->data);
	self->priv->data = _tmp0_;
	g_weak_ref_set (&FtTimezoneHistory_singleton__ref, obj);
	g_mutex_unlock (&FtTimezoneHistory_singleton__lock);
	return obj;
}

static void
ft_timezone_history_class_init (FtTimezoneHistoryClass * klass,
                                gpointer klass_data)
{
	ft_timezone_history_parent_class = g_type_class_peek_parent (klass);
	g_type_class_adjust_private_offset (klass, &FtTimezoneHistory_private_offset);
	((GObjectClass *) klass)->dispose = (void (*) (GObject*)) ft_timezone_history_real_dispose;
	G_OBJECT_CLASS (klass)->constructor = ft_timezone_history_constructor;
	G_OBJECT_CLASS (klass)->finalize = ft_timezone_history_finalize;
	ft_timezone_history_signals[FT_TIMEZONE_HISTORY_CHANGED_SIGNAL] = g_signal_new ("changed", FT_TYPE_TIMEZONE_HISTORY, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}

static void
ft_timezone_history_instance_init (FtTimezoneHistory * self,
                                   gpointer klass)
{
	self->priv = ft_timezone_history_get_instance_private (self);
	self->priv->fetched_timestamp = FT_TIMESTAMP_UNDEFINED;
	self->priv->fetched_all = FALSE;
}

static void
ft_timezone_history_finalize (GObject * obj)
{
	FtTimezoneHistory * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, FT_TYPE_TIMEZONE_HISTORY, FtTimezoneHistory);
	_g_array_unref0 (self->priv->data);
	G_OBJECT_CLASS (ft_timezone_history_parent_class)->finalize (obj);
}

/**
     * We use UTC timestamp throughout the app and store local timezone separately
     * as a way to convert it to local time when needed.
     *
     * Internally, data is stored from most recent to oldest entries. Be aware that
     * `GLib.TimeZone` timestamps are in seconds while ours are in microseconds.
     */
static GType
ft_timezone_history_get_type_once (void)
{
	static const GTypeInfo g_define_type_info = { sizeof (FtTimezoneHistoryClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) ft_timezone_history_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (FtTimezoneHistory), 0, (GInstanceInitFunc) ft_timezone_history_instance_init, NULL };
	GType ft_timezone_history_type_id;
	ft_timezone_history_type_id = g_type_register_static (G_TYPE_OBJECT, "FtTimezoneHistory", &g_define_type_info, 0);
	FtTimezoneHistory_private_offset = g_type_add_instance_private (ft_timezone_history_type_id, sizeof (FtTimezoneHistoryPrivate));
	return ft_timezone_history_type_id;
}

GType
ft_timezone_history_get_type (void)
{
	static volatile gsize ft_timezone_history_type_id__once = 0;
	if (g_once_init_enter (&ft_timezone_history_type_id__once)) {
		GType ft_timezone_history_type_id;
		ft_timezone_history_type_id = ft_timezone_history_get_type_once ();
		g_once_init_leave (&ft_timezone_history_type_id__once, ft_timezone_history_type_id);
	}
	return ft_timezone_history_type_id__once;
}

static inline gpointer
_vala_memdup2 (gconstpointer mem,
               gsize byte_size)
{
	gpointer new_mem;
	if (mem && byte_size != 0) {
		new_mem = g_malloc (byte_size);
		memcpy (new_mem, mem, byte_size);
	} else {
		new_mem = NULL;
	}
	return new_mem;
}

