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

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

#include "ft_core.h"
#include <glib.h>
#include <float.h>
#include <math.h>
#include <glib-object.h>

#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

enum  {
	FT_CYCLE_0_PROPERTY,
	FT_CYCLE_SESSION_PROPERTY,
	FT_CYCLE_START_TIME_PROPERTY,
	FT_CYCLE_END_TIME_PROPERTY,
	FT_CYCLE_DURATION_PROPERTY,
	FT_CYCLE_NUM_PROPERTIES
};
static GParamSpec* ft_cycle_properties[FT_CYCLE_NUM_PROPERTIES];
#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
enum  {
	FT_CYCLE_ADDED_SIGNAL,
	FT_CYCLE_REMOVED_SIGNAL,
	FT_CYCLE_CHANGED_SIGNAL,
	FT_CYCLE_NUM_SIGNALS
};
static guint ft_cycle_signals[FT_CYCLE_NUM_SIGNALS] = {0};

struct _FtCyclePrivate {
	GList* time_blocks;
	gint64 _start_time;
	gint64 _end_time;
	gint changed_freeze_count;
	gboolean changed_is_pending;
	gdouble progress_value;
	gint64 progress_reference_start_time;
	gint64 progress_reference_end_time;
	gdouble weight;
	gint64 completion_time;
};

static gint FtCycle_private_offset;
static gpointer ft_cycle_parent_class = NULL;

static void ft_cycle_added (FtCycle* self,
                     FtTimeBlock* time_block);
static void ft_cycle_removed (FtCycle* self,
                       FtTimeBlock* time_block);
static void ft_cycle_changed (FtCycle* self);
static void _g_object_unref0_ (gpointer var);
static inline void _g_list_free__g_object_unref0_ (GList* self);
static void ft_cycle_on_time_block_changed (FtCycle* self);
static void ft_cycle_emit_changed (FtCycle* self);
static void ft_cycle_emit_added (FtCycle* self,
                          FtTimeBlock* time_block);
static void _ft_cycle_on_time_block_changed_ft_time_block_changed (FtTimeBlock* _sender,
                                                            gpointer self);
static void ft_cycle_emit_removed (FtCycle* self,
                            FtTimeBlock* time_block);
static void ft_cycle_update_time_range (FtCycle* self);
static void ft_cycle_remove_link (FtCycle* self,
                           GList* link);
static void ft_cycle_invalidate_cache (FtCycle* self);
static void ft_cycle_real_dispose (GObject* base);
static GObject * ft_cycle_constructor (GType type,
                                guint n_construct_properties,
                                GObjectConstructParam * construct_properties);
static void ft_cycle_finalize (GObject * obj);
static GType ft_cycle_get_type_once (void);
static void _vala_ft_cycle_get_property (GObject * object,
                                  guint property_id,
                                  GValue * value,
                                  GParamSpec * pspec);

static inline gpointer
ft_cycle_get_instance_private (FtCycle* self)
{
	return G_STRUCT_MEMBER_P (self, FtCycle_private_offset);
}

static void
_g_object_unref0_ (gpointer var)
{
	(var == NULL) ? NULL : (var = (g_object_unref (var), NULL));
}

static inline void
_g_list_free__g_object_unref0_ (GList* self)
{
	g_list_free_full (self, (GDestroyNotify) _g_object_unref0_);
}

static void
ft_cycle_on_time_block_changed (FtCycle* self)
{
	g_return_if_fail (self != NULL);
	ft_cycle_emit_changed (self);
}

static void
_ft_cycle_on_time_block_changed_ft_time_block_changed (FtTimeBlock* _sender,
                                                       gpointer self)
{
	ft_cycle_on_time_block_changed ((FtCycle*) self);
}

static void
ft_cycle_emit_added (FtCycle* self,
                     FtTimeBlock* time_block)
{
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	g_signal_connect_object (time_block, "changed", (GCallback) _ft_cycle_on_time_block_changed_ft_time_block_changed, self, 0);
	g_signal_emit (self, ft_cycle_signals[FT_CYCLE_ADDED_SIGNAL], 0, time_block);
}

static void
ft_cycle_emit_removed (FtCycle* self,
                       FtTimeBlock* time_block)
{
	guint _tmp0_;
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	g_signal_parse_name ("changed", FT_TYPE_TIME_BLOCK, &_tmp0_, NULL, FALSE);
	g_signal_handlers_disconnect_matched (time_block, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, _tmp0_, 0, NULL, (GCallback) _ft_cycle_on_time_block_changed_ft_time_block_changed, self);
	g_signal_emit (self, ft_cycle_signals[FT_CYCLE_REMOVED_SIGNAL], 0, time_block);
}

static void
ft_cycle_emit_changed (FtCycle* self)
{
	g_return_if_fail (self != NULL);
	if (self->priv->changed_freeze_count > 0) {
		self->priv->changed_is_pending = TRUE;
	} else {
		self->priv->changed_is_pending = FALSE;
		g_signal_emit (self, ft_cycle_signals[FT_CYCLE_CHANGED_SIGNAL], 0);
	}
}

void
ft_cycle_freeze_changed (FtCycle* self)
{
	gint _tmp0_;
	g_return_if_fail (self != NULL);
	_tmp0_ = self->priv->changed_freeze_count;
	self->priv->changed_freeze_count = _tmp0_ + 1;
}

void
ft_cycle_thaw_changed (FtCycle* self)
{
	gint _tmp0_;
	gboolean _tmp1_ = FALSE;
	g_return_if_fail (self != NULL);
	_tmp0_ = self->priv->changed_freeze_count;
	self->priv->changed_freeze_count = _tmp0_ - 1;
	if (self->priv->changed_freeze_count == 0) {
		_tmp1_ = self->priv->changed_is_pending;
	} else {
		_tmp1_ = FALSE;
	}
	if (_tmp1_) {
		ft_cycle_emit_changed (self);
	}
}

static void
ft_cycle_update_time_range (FtCycle* self)
{
	FtTimeBlock* first_time_block = NULL;
	FtTimeBlock* _tmp0_;
	FtTimeBlock* last_time_block = NULL;
	FtTimeBlock* _tmp1_;
	gint64 old_duration = 0LL;
	gint64 _tmp2_ = 0LL;
	FtTimeBlock* _tmp3_;
	gint64 start_time = 0LL;
	gint64 _tmp7_ = 0LL;
	FtTimeBlock* _tmp8_;
	gint64 end_time = 0LL;
	g_return_if_fail (self != NULL);
	_tmp0_ = ft_cycle_get_first_time_block (self);
	first_time_block = _tmp0_;
	_tmp1_ = ft_cycle_get_last_time_block (self);
	last_time_block = _tmp1_;
	old_duration = self->priv->_end_time - self->priv->_start_time;
	_tmp3_ = first_time_block;
	if (_tmp3_ != NULL) {
		FtTimeBlock* _tmp4_;
		gint64 _tmp5_;
		gint64 _tmp6_;
		_tmp4_ = first_time_block;
		_tmp5_ = ft_schedulable_get_start_time ((FtSchedulable*) _tmp4_);
		_tmp6_ = _tmp5_;
		_tmp2_ = _tmp6_;
	} else {
		_tmp2_ = FT_TIMESTAMP_UNDEFINED;
	}
	start_time = _tmp2_;
	_tmp8_ = last_time_block;
	if (_tmp8_ != NULL) {
		FtTimeBlock* _tmp9_;
		gint64 _tmp10_;
		gint64 _tmp11_;
		_tmp9_ = last_time_block;
		_tmp10_ = ft_schedulable_get_end_time ((FtSchedulable*) _tmp9_);
		_tmp11_ = _tmp10_;
		_tmp7_ = _tmp11_;
	} else {
		_tmp7_ = FT_TIMESTAMP_UNDEFINED;
	}
	end_time = _tmp7_;
	if (self->priv->_start_time != start_time) {
		self->priv->_start_time = start_time;
		g_object_notify ((GObject*) self, "start-time");
	}
	if (self->priv->_end_time != end_time) {
		self->priv->_end_time = end_time;
		g_object_notify ((GObject*) self, "end-time");
	}
	if ((self->priv->_end_time - self->priv->_start_time) != old_duration) {
		g_object_notify ((GObject*) self, "duration");
	}
}

static gpointer
_g_object_ref0 (gpointer self)
{
	return self ? g_object_ref (self) : NULL;
}

static void
ft_cycle_remove_link (FtCycle* self,
                      GList* link)
{
	FtTimeBlock* time_block = NULL;
	gconstpointer _tmp0_;
	FtTimeBlock* _tmp1_;
	FtTimeBlock* _tmp2_;
	g_return_if_fail (self != NULL);
	if (link == NULL) {
		return;
	}
	_tmp0_ = link->data;
	_tmp1_ = _g_object_ref0 ((FtTimeBlock*) _tmp0_);
	time_block = _tmp1_;
	_g_object_unref0 (link->data);
	link->data = NULL;
	self->priv->time_blocks = g_list_delete_link (self->priv->time_blocks, link);
	_tmp2_ = time_block;
	ft_cycle_emit_removed (self, _tmp2_);
	_g_object_unref0 (time_block);
}

void
ft_cycle_remove (FtCycle* self,
                 FtTimeBlock* time_block)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	GList* _tmp2_;
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_find (_tmp0_, time_block);
	link = _tmp1_;
	_tmp2_ = link;
	if (_tmp2_ != NULL) {
		GList* _tmp3_;
		_tmp3_ = link;
		ft_cycle_remove_link (self, _tmp3_);
	} else {
		g_warning ("cycle.vala:161: Ignoring `Cycle.remove()`. Time-block does not belong " \
"to the cycle.");
	}
}

void
ft_cycle_append (FtCycle* self,
                 FtTimeBlock* time_block)
{
	FtTimeBlock* _tmp0_;
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	_tmp0_ = _g_object_ref0 (time_block);
	self->priv->time_blocks = g_list_append (self->priv->time_blocks, _tmp0_);
	ft_cycle_emit_added (self, time_block);
}

FtTimeBlock*
ft_cycle_get_first_time_block (FtCycle* self)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	FtTimeBlock* _tmp2_ = NULL;
	GList* _tmp3_;
	FtTimeBlock* result;
	g_return_val_if_fail (self != NULL, NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	_tmp3_ = link;
	if (_tmp3_ != NULL) {
		GList* _tmp4_;
		gconstpointer _tmp5_;
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		_tmp2_ = (FtTimeBlock*) _tmp5_;
	} else {
		_tmp2_ = NULL;
	}
	result = _tmp2_;
	return result;
}

FtTimeBlock*
ft_cycle_get_last_time_block (FtCycle* self)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	FtTimeBlock* _tmp2_ = NULL;
	GList* _tmp3_;
	FtTimeBlock* result;
	g_return_val_if_fail (self != NULL, NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_last (_tmp0_);
	link = _tmp1_;
	_tmp3_ = link;
	if (_tmp3_ != NULL) {
		GList* _tmp4_;
		gconstpointer _tmp5_;
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		_tmp2_ = (FtTimeBlock*) _tmp5_;
	} else {
		_tmp2_ = NULL;
	}
	result = _tmp2_;
	return result;
}

gboolean
ft_cycle_contains (FtCycle* self,
                   FtTimeBlock* time_block)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	gboolean result;
	g_return_val_if_fail (self != NULL, FALSE);
	g_return_val_if_fail (time_block != NULL, FALSE);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	while (TRUE) {
		GList* _tmp2_;
		GList* _tmp3_;
		gconstpointer _tmp4_;
		GList* _tmp5_;
		GList* _tmp6_;
		_tmp2_ = link;
		if (!(_tmp2_ != NULL)) {
			break;
		}
		_tmp3_ = link;
		_tmp4_ = _tmp3_->data;
		if (((FtTimeBlock*) _tmp4_) == time_block) {
			result = TRUE;
			return result;
		}
		_tmp5_ = link;
		_tmp6_ = _tmp5_->next;
		link = _tmp6_;
	}
	result = FALSE;
	return result;
}

void
ft_cycle_foreach (FtCycle* self,
                  GFunc func,
                  gpointer func_target)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	g_return_if_fail (self != NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	while (TRUE) {
		GList* _tmp2_;
		GList* _tmp3_;
		gconstpointer _tmp4_;
		GList* _tmp5_;
		GList* _tmp6_;
		_tmp2_ = link;
		if (!(_tmp2_ != NULL)) {
			break;
		}
		_tmp3_ = link;
		_tmp4_ = _tmp3_->data;
		func ((FtTimeBlock*) _tmp4_, func_target);
		_tmp5_ = link;
		_tmp6_ = _tmp5_->next;
		link = _tmp6_;
	}
}

gdouble
ft_cycle_get_weight (FtCycle* self)
{
	gdouble result;
	g_return_val_if_fail (self != NULL, -1.0);
	if (isnan (self->priv->weight)) {
		GList* link = NULL;
		GList* _tmp0_;
		GList* _tmp1_;
		gdouble weight = 0.0;
		_tmp0_ = self->priv->time_blocks;
		_tmp1_ = g_list_first (_tmp0_);
		link = _tmp1_;
		weight = 0.0;
		while (TRUE) {
			GList* _tmp2_;
			GList* _tmp3_;
			gconstpointer _tmp4_;
			GList* _tmp8_;
			GList* _tmp9_;
			_tmp2_ = link;
			if (!(_tmp2_ != NULL)) {
				break;
			}
			_tmp3_ = link;
			_tmp4_ = _tmp3_->data;
			if (ft_time_block_get_status ((FtTimeBlock*) _tmp4_) != FT_TIME_BLOCK_STATUS_UNCOMPLETED) {
				gdouble time_block_weight = 0.0;
				GList* _tmp5_;
				gconstpointer _tmp6_;
				gdouble _tmp7_ = 0.0;
				_tmp5_ = link;
				_tmp6_ = _tmp5_->data;
				time_block_weight = ft_time_block_get_weight ((FtTimeBlock*) _tmp6_);
				if (!isnan (time_block_weight)) {
					_tmp7_ = weight + MAX (time_block_weight, 0.0);
				} else {
					_tmp7_ = ((double) NAN);
				}
				weight = _tmp7_;
			}
			_tmp8_ = link;
			_tmp9_ = _tmp8_->next;
			link = _tmp9_;
		}
		self->priv->weight = weight;
	}
	result = self->priv->weight;
	return result;
}

gint64
ft_cycle_get_completion_time (FtCycle* self)
{
	gint64 result;
	g_return_val_if_fail (self != NULL, -1LL);
	if (ft_timestamp_is_undefined (self->priv->completion_time)) {
		GList* link = NULL;
		GList* _tmp0_;
		GList* _tmp1_;
		gint64 completion_time = 0LL;
		gint64 time_block_completion_time = 0LL;
		_tmp0_ = self->priv->time_blocks;
		_tmp1_ = g_list_first (_tmp0_);
		link = _tmp1_;
		completion_time = FT_TIMESTAMP_UNDEFINED;
		time_block_completion_time = FT_TIMESTAMP_UNDEFINED;
		while (TRUE) {
			GList* _tmp2_;
			gboolean _tmp3_ = FALSE;
			GList* _tmp4_;
			gconstpointer _tmp5_;
			GList* _tmp15_;
			GList* _tmp16_;
			_tmp2_ = link;
			if (!(_tmp2_ != NULL)) {
				break;
			}
			_tmp4_ = link;
			_tmp5_ = _tmp4_->data;
			if (ft_time_block_get_status ((FtTimeBlock*) _tmp5_) != FT_TIME_BLOCK_STATUS_UNCOMPLETED) {
				GList* _tmp6_;
				gconstpointer _tmp7_;
				_tmp6_ = link;
				_tmp7_ = _tmp6_->data;
				_tmp3_ = ft_time_block_get_weight ((FtTimeBlock*) _tmp7_) > 0.0;
			} else {
				_tmp3_ = FALSE;
			}
			if (_tmp3_) {
				GList* _tmp8_;
				gconstpointer _tmp9_;
				gint64 _tmp10_ = 0LL;
				_tmp8_ = link;
				_tmp9_ = _tmp8_->data;
				time_block_completion_time = ft_time_block_get_completion_time ((FtTimeBlock*) _tmp9_);
				if (ft_timestamp_is_defined (time_block_completion_time)) {
					_tmp10_ = time_block_completion_time;
				} else {
					GList* _tmp11_;
					gconstpointer _tmp12_;
					gint64 _tmp13_;
					gint64 _tmp14_;
					_tmp11_ = link;
					_tmp12_ = _tmp11_->data;
					_tmp13_ = ft_schedulable_get_end_time ((FtSchedulable*) ((FtTimeBlock*) _tmp12_));
					_tmp14_ = _tmp13_;
					_tmp10_ = _tmp14_;
				}
				completion_time = _tmp10_;
			}
			_tmp15_ = link;
			_tmp16_ = _tmp15_->next;
			link = _tmp16_;
		}
		self->priv->completion_time = completion_time;
	}
	result = self->priv->completion_time;
	return result;
}

gboolean
ft_cycle_is_scheduled (FtCycle* self)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	FtTimeBlockStatus _tmp2_ = 0;
	GList* _tmp3_;
	FtTimeBlockStatus first_status = 0;
	gboolean result;
	g_return_val_if_fail (self != NULL, FALSE);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	_tmp3_ = link;
	if (_tmp3_ != NULL) {
		GList* _tmp4_;
		gconstpointer _tmp5_;
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		_tmp2_ = ft_time_block_get_status ((FtTimeBlock*) _tmp5_);
	} else {
		_tmp2_ = FT_TIME_BLOCK_STATUS_SCHEDULED;
	}
	first_status = _tmp2_;
	result = first_status == FT_TIME_BLOCK_STATUS_SCHEDULED;
	return result;
}

FtTimeBlockStatus
ft_cycle_get_status (FtCycle* self)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	gboolean _tmp2_ = FALSE;
	GList* _tmp3_;
	FtTimeBlockStatus _tmp14_ = 0;
	FtTimeBlockStatus result;
	g_return_val_if_fail (self != NULL, 0);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	_tmp3_ = link;
	if (_tmp3_ == NULL) {
		_tmp2_ = TRUE;
	} else {
		GList* _tmp4_;
		gconstpointer _tmp5_;
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		_tmp2_ = ft_time_block_get_status ((FtTimeBlock*) _tmp5_) == FT_TIME_BLOCK_STATUS_SCHEDULED;
	}
	if (_tmp2_) {
		result = FT_TIME_BLOCK_STATUS_SCHEDULED;
		return result;
	}
	while (TRUE) {
		GList* _tmp6_;
		gboolean _tmp7_ = FALSE;
		GList* _tmp8_;
		gconstpointer _tmp9_;
		GList* _tmp12_;
		GList* _tmp13_;
		_tmp6_ = link;
		if (!(_tmp6_ != NULL)) {
			break;
		}
		_tmp8_ = link;
		_tmp9_ = _tmp8_->data;
		if (ft_time_block_get_status ((FtTimeBlock*) _tmp9_) == FT_TIME_BLOCK_STATUS_IN_PROGRESS) {
			_tmp7_ = TRUE;
		} else {
			GList* _tmp10_;
			gconstpointer _tmp11_;
			_tmp10_ = link;
			_tmp11_ = _tmp10_->data;
			_tmp7_ = ft_time_block_get_status ((FtTimeBlock*) _tmp11_) == FT_TIME_BLOCK_STATUS_SCHEDULED;
		}
		if (_tmp7_) {
			result = FT_TIME_BLOCK_STATUS_IN_PROGRESS;
			return result;
		}
		_tmp12_ = link;
		_tmp13_ = _tmp12_->next;
		link = _tmp13_;
	}
	if (ft_cycle_is_visible (self)) {
		_tmp14_ = FT_TIME_BLOCK_STATUS_COMPLETED;
	} else {
		_tmp14_ = FT_TIME_BLOCK_STATUS_UNCOMPLETED;
	}
	result = _tmp14_;
	return result;
}

/**
         * Hide cycles that were uncompleted.
         */
gboolean
ft_cycle_is_visible (FtCycle* self)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	gboolean result;
	g_return_val_if_fail (self != NULL, FALSE);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	while (TRUE) {
		GList* _tmp2_;
		gboolean _tmp3_ = FALSE;
		GList* _tmp4_;
		gconstpointer _tmp5_;
		GList* _tmp8_;
		GList* _tmp9_;
		_tmp2_ = link;
		if (!(_tmp2_ != NULL)) {
			break;
		}
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		if (ft_time_block_get_status ((FtTimeBlock*) _tmp5_) != FT_TIME_BLOCK_STATUS_UNCOMPLETED) {
			GList* _tmp6_;
			gconstpointer _tmp7_;
			_tmp6_ = link;
			_tmp7_ = _tmp6_->data;
			_tmp3_ = ft_time_block_get_weight ((FtTimeBlock*) _tmp7_) > 0.0;
		} else {
			_tmp3_ = FALSE;
		}
		if (_tmp3_) {
			result = TRUE;
			return result;
		}
		_tmp8_ = link;
		_tmp9_ = _tmp8_->next;
		link = _tmp9_;
	}
	result = FALSE;
	return result;
}

void
ft_cycle_prepare_progress (FtCycle* self,
                           gint64 timestamp)
{
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	gdouble progress = 0.0;
	gdouble total_weight = 0.0;
	gboolean in_progress = FALSE;
	gboolean _tmp22_ = FALSE;
	g_return_if_fail (self != NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	progress = 0.0;
	total_weight = 0.0;
	in_progress = FALSE;
	while (TRUE) {
		GList* _tmp2_;
		gdouble time_block_weight = 0.0;
		GList* _tmp3_;
		gconstpointer _tmp4_;
		FtGap* last_gap = NULL;
		GList* _tmp5_;
		gconstpointer _tmp6_;
		FtGap* _tmp7_;
		FtGap* _tmp8_;
		GList* _tmp9_;
		gconstpointer _tmp10_;
		GList* _tmp11_;
		gconstpointer _tmp12_;
		gboolean _tmp13_ = FALSE;
		FtGap* _tmp14_;
		GList* _tmp18_;
		gconstpointer _tmp19_;
		GList* _tmp20_;
		GList* _tmp21_;
		_tmp2_ = link;
		if (!(_tmp2_ != NULL)) {
			break;
		}
		_tmp3_ = link;
		_tmp4_ = _tmp3_->data;
		time_block_weight = ft_time_block_get_weight ((FtTimeBlock*) _tmp4_);
		_tmp5_ = link;
		_tmp6_ = _tmp5_->data;
		_tmp7_ = ft_time_block_get_last_gap ((FtTimeBlock*) _tmp6_);
		_tmp8_ = _g_object_ref0 (_tmp7_);
		last_gap = _tmp8_;
		_tmp9_ = link;
		_tmp10_ = _tmp9_->data;
		if (ft_time_block_get_status ((FtTimeBlock*) _tmp10_) != FT_TIME_BLOCK_STATUS_UNCOMPLETED) {
			total_weight += time_block_weight;
		}
		_tmp11_ = link;
		_tmp12_ = _tmp11_->data;
		if (ft_time_block_get_status ((FtTimeBlock*) _tmp12_) == FT_TIME_BLOCK_STATUS_IN_PROGRESS) {
			in_progress = TRUE;
		}
		_tmp14_ = last_gap;
		if (_tmp14_ != NULL) {
			FtGap* _tmp15_;
			gint64 _tmp16_;
			gint64 _tmp17_;
			_tmp15_ = last_gap;
			_tmp16_ = ft_schedulable_get_end_time ((FtSchedulable*) _tmp15_);
			_tmp17_ = _tmp16_;
			_tmp13_ = ft_timestamp_is_undefined (_tmp17_);
		} else {
			_tmp13_ = FALSE;
		}
		if (_tmp13_) {
			in_progress = FALSE;
		}
		_tmp18_ = link;
		_tmp19_ = _tmp18_->data;
		progress += time_block_weight * ft_time_block_calculate_progress ((FtTimeBlock*) _tmp19_, timestamp);
		_tmp20_ = link;
		_tmp21_ = _tmp20_->next;
		link = _tmp21_;
		_g_object_unref0 (last_gap);
	}
	if (total_weight != 0.0) {
		progress /= total_weight;
	}
	if (!in_progress) {
		_tmp22_ = TRUE;
	} else {
		_tmp22_ = progress >= 1.0;
	}
	if (_tmp22_) {
		self->priv->progress_value = CLAMP (progress, 0.0, 1.0);
		self->priv->progress_reference_start_time = FT_TIMESTAMP_UNDEFINED;
		self->priv->progress_reference_end_time = FT_TIMESTAMP_UNDEFINED;
	} else {
		gint64 completion_time = 0LL;
		completion_time = ft_cycle_get_completion_time (self);
		self->priv->progress_value = ((double) NAN);
		self->priv->progress_reference_start_time = (gint64) ((((gdouble) timestamp) - (progress * ((gdouble) completion_time))) / (1.0 - progress));
		self->priv->progress_reference_end_time = completion_time;
		if (self->priv->progress_reference_start_time >= self->priv->progress_reference_end_time) {
			self->priv->progress_value = CLAMP (progress, 0.0, 1.0);
			self->priv->progress_reference_start_time = FT_TIMESTAMP_UNDEFINED;
			self->priv->progress_reference_end_time = FT_TIMESTAMP_UNDEFINED;
		}
	}
}

/**
         * Calculate cycle progress.
         *
         * Uses cache to make following calls cheaper to estimate.
         */
gdouble
ft_cycle_calculate_progress (FtCycle* self,
                             gint64 timestamp)
{
	gboolean _tmp0_ = FALSE;
	gboolean _tmp1_ = FALSE;
	gdouble result;
	g_return_val_if_fail (self != NULL, -1.0);
	if (ft_timestamp_is_undefined (self->priv->_end_time)) {
		_tmp0_ = TRUE;
	} else {
		_tmp0_ = ft_timestamp_is_undefined (self->priv->_start_time);
	}
	if (_tmp0_) {
		result = 0.0;
		return result;
	}
	ft_ensure_timestamp (&timestamp);
	if (ft_timestamp_is_undefined (self->priv->progress_reference_start_time)) {
		_tmp1_ = isnan (self->priv->progress_value);
	} else {
		_tmp1_ = FALSE;
	}
	if (_tmp1_) {
		ft_cycle_prepare_progress (self, timestamp);
	}
	if (!isnan (self->priv->progress_value)) {
		result = self->priv->progress_value;
		return result;
	}
	if (self->priv->progress_reference_start_time >= timestamp) {
		result = 0.0;
		return result;
	}
	if (self->priv->progress_reference_end_time <= timestamp) {
		result = 1.0;
		return result;
	}
	result = CLAMP (((gdouble) (timestamp - self->priv->progress_reference_start_time)) / ((gdouble) (self->priv->progress_reference_end_time - self->priv->progress_reference_start_time)), 0.0, 1.0);
	return result;
}

gint64
ft_cycle_calculate_progress_duration (FtCycle* self,
                                      gint64 timestamp)
{
	gint64 result;
	g_return_val_if_fail (self != NULL, -1LL);
	if (ft_timestamp_is_undefined (self->priv->progress_reference_start_time)) {
		ft_ensure_timestamp (&timestamp);
		ft_cycle_prepare_progress (self, timestamp);
	}
	result = ft_timestamp_subtract (self->priv->progress_reference_end_time, self->priv->progress_reference_start_time);
	return result;
}

/**
         * Invalidate cache. Cache helps avoiding re-iterating time-blocks for certain operations.
         *
         * You should call it on every time-block change or change of metadata.
         */
static void
ft_cycle_invalidate_cache (FtCycle* self)
{
	g_return_if_fail (self != NULL);
	self->priv->weight = ((double) NAN);
	self->priv->completion_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->progress_value = ((double) NAN);
	self->priv->progress_reference_start_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->progress_reference_end_time = FT_TIMESTAMP_UNDEFINED;
}

static void
ft_cycle_real_dispose (GObject* base)
{
	FtCycle * self;
	GList* link = NULL;
	self = (FtCycle*) base;
	while (TRUE) {
		GList* _tmp0_;
		GList* _tmp1_;
		GList* _tmp2_;
		GList* _tmp3_;
		gconstpointer _tmp4_;
		guint _tmp5_;
		GList* _tmp6_;
		GList* _tmp7_;
		_tmp0_ = self->priv->time_blocks;
		_tmp1_ = g_list_first (_tmp0_);
		link = _tmp1_;
		_tmp2_ = link;
		if (!(_tmp2_ != NULL)) {
			break;
		}
		_tmp3_ = link;
		_tmp4_ = _tmp3_->data;
		g_signal_parse_name ("changed", FT_TYPE_TIME_BLOCK, &_tmp5_, NULL, FALSE);
		g_signal_handlers_disconnect_matched ((FtTimeBlock*) _tmp4_, G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, _tmp5_, 0, NULL, (GCallback) _ft_cycle_on_time_block_changed_ft_time_block_changed, self);
		_tmp6_ = link;
		_g_object_unref0 (_tmp6_->data);
		_tmp6_->data = NULL;
		_tmp7_ = link;
		self->priv->time_blocks = g_list_delete_link (self->priv->time_blocks, _tmp7_);
	}
	G_OBJECT_CLASS (ft_cycle_parent_class)->dispose (G_TYPE_CHECK_INSTANCE_CAST (self, G_TYPE_OBJECT, GObject));
}

FtCycle*
ft_cycle_construct (GType object_type)
{
	FtCycle * self = NULL;
	self = (FtCycle*) g_object_new (object_type, NULL);
	return self;
}

FtCycle*
ft_cycle_new (void)
{
	return ft_cycle_construct (FT_TYPE_CYCLE);
}

FtSession*
ft_cycle_get_session (FtCycle* self)
{
	FtSession* result;
	GList* link = NULL;
	GList* _tmp0_;
	GList* _tmp1_;
	FtSession* _tmp2_ = NULL;
	GList* _tmp3_;
	g_return_val_if_fail (self != NULL, NULL);
	_tmp0_ = self->priv->time_blocks;
	_tmp1_ = g_list_first (_tmp0_);
	link = _tmp1_;
	_tmp3_ = link;
	if (_tmp3_ != NULL) {
		GList* _tmp4_;
		gconstpointer _tmp5_;
		FtSession* _tmp6_;
		FtSession* _tmp7_;
		_tmp4_ = link;
		_tmp5_ = _tmp4_->data;
		_tmp6_ = ft_time_block_get_session ((FtTimeBlock*) _tmp5_);
		_tmp7_ = _tmp6_;
		_tmp2_ = _tmp7_;
	} else {
		_tmp2_ = NULL;
	}
	result = _tmp2_;
	return result;
}

gint64
ft_cycle_get_start_time (FtCycle* self)
{
	gint64 result;
	g_return_val_if_fail (self != NULL, -1LL);
	result = self->priv->_start_time;
	return result;
}

gint64
ft_cycle_get_end_time (FtCycle* self)
{
	gint64 result;
	g_return_val_if_fail (self != NULL, -1LL);
	result = self->priv->_end_time;
	return result;
}

gint64
ft_cycle_get_duration (FtCycle* self)
{
	gint64 result;
	g_return_val_if_fail (self != NULL, -1LL);
	result = ft_timestamp_subtract (self->priv->_end_time, self->priv->_start_time);
	return result;
}

static void
ft_cycle_real_added (FtCycle* self,
                     FtTimeBlock* time_block)
{
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	ft_cycle_emit_changed (self);
}

static void
ft_cycle_real_removed (FtCycle* self,
                       FtTimeBlock* time_block)
{
	g_return_if_fail (self != NULL);
	g_return_if_fail (time_block != NULL);
	ft_cycle_emit_changed (self);
}

static void
ft_cycle_real_changed (FtCycle* self)
{
	g_return_if_fail (self != NULL);
	ft_cycle_invalidate_cache (self);
	ft_cycle_update_time_range (self);
}

static GObject *
ft_cycle_constructor (GType type,
                      guint n_construct_properties,
                      GObjectConstructParam * construct_properties)
{
	GObject * obj;
	GObjectClass * parent_class;
	FtCycle * self;
	parent_class = G_OBJECT_CLASS (ft_cycle_parent_class);
	obj = parent_class->constructor (type, n_construct_properties, construct_properties);
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, FT_TYPE_CYCLE, FtCycle);
	(self->priv->time_blocks == NULL) ? NULL : (self->priv->time_blocks = (_g_list_free__g_object_unref0_ (self->priv->time_blocks), NULL));
	self->priv->time_blocks = NULL;
	return obj;
}

static void
ft_cycle_class_init (FtCycleClass * klass,
                     gpointer klass_data)
{
	ft_cycle_parent_class = g_type_class_peek_parent (klass);
	g_type_class_adjust_private_offset (klass, &FtCycle_private_offset);
	((GObjectClass *) klass)->dispose = (void (*) (GObject*)) ft_cycle_real_dispose;
	G_OBJECT_CLASS (klass)->get_property = _vala_ft_cycle_get_property;
	G_OBJECT_CLASS (klass)->constructor = ft_cycle_constructor;
	G_OBJECT_CLASS (klass)->finalize = ft_cycle_finalize;
	g_object_class_install_property (G_OBJECT_CLASS (klass), FT_CYCLE_SESSION_PROPERTY, ft_cycle_properties[FT_CYCLE_SESSION_PROPERTY] = g_param_spec_object ("session", "session", "session", FT_TYPE_SESSION, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), FT_CYCLE_START_TIME_PROPERTY, ft_cycle_properties[FT_CYCLE_START_TIME_PROPERTY] = g_param_spec_int64 ("start-time", "start-time", "start-time", G_MININT64, G_MAXINT64, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), FT_CYCLE_END_TIME_PROPERTY, ft_cycle_properties[FT_CYCLE_END_TIME_PROPERTY] = g_param_spec_int64 ("end-time", "end-time", "end-time", G_MININT64, G_MAXINT64, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), FT_CYCLE_DURATION_PROPERTY, ft_cycle_properties[FT_CYCLE_DURATION_PROPERTY] = g_param_spec_int64 ("duration", "duration", "duration", G_MININT64, G_MAXINT64, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	ft_cycle_signals[FT_CYCLE_ADDED_SIGNAL] = g_signal_new_class_handler ("added", FT_TYPE_CYCLE, G_SIGNAL_RUN_LAST, (GCallback) ft_cycle_real_added, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, FT_TYPE_TIME_BLOCK);
	ft_cycle_signals[FT_CYCLE_REMOVED_SIGNAL] = g_signal_new_class_handler ("removed", FT_TYPE_CYCLE, G_SIGNAL_RUN_LAST, (GCallback) ft_cycle_real_removed, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, FT_TYPE_TIME_BLOCK);
	ft_cycle_signals[FT_CYCLE_CHANGED_SIGNAL] = g_signal_new_class_handler ("changed", FT_TYPE_CYCLE, G_SIGNAL_RUN_FIRST, (GCallback) ft_cycle_real_changed, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}

static void
ft_cycle_instance_init (FtCycle * self,
                        gpointer klass)
{
	self->priv = ft_cycle_get_instance_private (self);
	self->priv->_start_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->_end_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->changed_freeze_count = 0;
	self->priv->changed_is_pending = FALSE;
	self->priv->progress_value = ((double) NAN);
	self->priv->progress_reference_start_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->progress_reference_end_time = FT_TIMESTAMP_UNDEFINED;
	self->priv->weight = ((double) NAN);
	self->priv->completion_time = FT_TIMESTAMP_UNDEFINED;
}

static void
ft_cycle_finalize (GObject * obj)
{
	FtCycle * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, FT_TYPE_CYCLE, FtCycle);
	(self->priv->time_blocks == NULL) ? NULL : (self->priv->time_blocks = (_g_list_free__g_object_unref0_ (self->priv->time_blocks), NULL));
	G_OBJECT_CLASS (ft_cycle_parent_class)->finalize (obj);
}

/**
     * A convenience class describing a cycle.
     *
     * Cycle consists of a single pomodoro and breaks following it. In case pomodoro comes right after another pomodoro
     * we treat it as new cycle to make session indicator more readable, although it's not true to a definition of
     * a cycle. Uncompleted time-blocks are included, therefore some cycles may be considered as invalid.
     *
     * Cycle does not serve as a container for time-blocks, its more like an annotation.
     */
static GType
ft_cycle_get_type_once (void)
{
	static const GTypeInfo g_define_type_info = { sizeof (FtCycleClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) ft_cycle_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (FtCycle), 0, (GInstanceInitFunc) ft_cycle_instance_init, NULL };
	GType ft_cycle_type_id;
	ft_cycle_type_id = g_type_register_static (G_TYPE_OBJECT, "FtCycle", &g_define_type_info, 0);
	FtCycle_private_offset = g_type_add_instance_private (ft_cycle_type_id, sizeof (FtCyclePrivate));
	return ft_cycle_type_id;
}

GType
ft_cycle_get_type (void)
{
	static volatile gsize ft_cycle_type_id__once = 0;
	if (g_once_init_enter (&ft_cycle_type_id__once)) {
		GType ft_cycle_type_id;
		ft_cycle_type_id = ft_cycle_get_type_once ();
		g_once_init_leave (&ft_cycle_type_id__once, ft_cycle_type_id);
	}
	return ft_cycle_type_id__once;
}

static void
_vala_ft_cycle_get_property (GObject * object,
                             guint property_id,
                             GValue * value,
                             GParamSpec * pspec)
{
	FtCycle * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (object, FT_TYPE_CYCLE, FtCycle);
	switch (property_id) {
		case FT_CYCLE_SESSION_PROPERTY:
		g_value_set_object (value, ft_cycle_get_session (self));
		break;
		case FT_CYCLE_START_TIME_PROPERTY:
		g_value_set_int64 (value, ft_cycle_get_start_time (self));
		break;
		case FT_CYCLE_END_TIME_PROPERTY:
		g_value_set_int64 (value, ft_cycle_get_end_time (self));
		break;
		case FT_CYCLE_DURATION_PROPERTY:
		g_value_set_int64 (value, ft_cycle_get_duration (self));
		break;
		default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

