/* chess-clock.c generated by valac 0.56.3, the Vala compiler
 * generated from chess-clock.vala, do not modify */

/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2010-2013 Robert Ancell
 * Copyright (C) 2013-2014 Michael Catanzaro
 * Copyright (C) 2015-2016 Sahil Sareen
 *
 * 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 3 of the License, or (at your option) any later
 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
 * license.
 */

#include "libchess.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <glib-object.h>

enum  {
	CHESS_CLOCK_0_PROPERTY,
	CHESS_CLOCK_EXTRA_SECONDS_PROPERTY,
	CHESS_CLOCK_WHITE_REMAINING_SECONDS_PROPERTY,
	CHESS_CLOCK_BLACK_REMAINING_SECONDS_PROPERTY,
	CHESS_CLOCK_CLOCK_TYPE_PROPERTY,
	CHESS_CLOCK_ACTIVE_COLOR_PROPERTY,
	CHESS_CLOCK_NUM_PROPERTIES
};
static GParamSpec* chess_clock_properties[CHESS_CLOCK_NUM_PROPERTIES];
#define _g_timer_destroy0(var) ((var == NULL) ? NULL : (var = (g_timer_destroy (var), NULL)))
enum  {
	CHESS_CLOCK_TICK_SIGNAL,
	CHESS_CLOCK_EXPIRED_SIGNAL,
	CHESS_CLOCK_NUM_SIGNALS
};
static guint chess_clock_signals[CHESS_CLOCK_NUM_SIGNALS] = {0};

struct _ChessClockPrivate {
	gint white_initial_seconds;
	gint black_initial_seconds;
	gint white_seconds_used;
	gint black_seconds_used;
	gint white_prev_move_seconds;
	gint black_prev_move_seconds;
	gint white_extra_seconds;
	gint black_extra_seconds;
	gint _extra_seconds;
	ChessClockType _clock_type;
	Color _active_color;
	GTimer* timer;
	guint tick_timeout_id;
	gboolean is_active;
};

static gint ChessClock_private_offset;
static gpointer chess_clock_parent_class = NULL;

static void chess_clock_watch_timer (ChessClock* self);
static gboolean chess_clock_tick_cb (ChessClock* self);
static void chess_clock_stop_watching_timer (ChessClock* self);
static gboolean _chess_clock_tick_cb_gsource_func (gpointer self);
static void chess_clock_update_extra_seconds (ChessClock* self);
static void chess_clock_finalize (GObject * obj);
static GType chess_clock_get_type_once (void);
static void _vala_chess_clock_get_property (GObject * object,
                                     guint property_id,
                                     GValue * value,
                                     GParamSpec * pspec);
static void _vala_chess_clock_set_property (GObject * object,
                                     guint property_id,
                                     const GValue * value,
                                     GParamSpec * pspec);

gchar*
chess_clock_type_to_string (ChessClockType self)
{
	gchar* result;
	switch (self) {
		case CHESS_CLOCK_TYPE_SIMPLE:
		{
			gchar* _tmp0_;
			_tmp0_ = g_strdup ("simple");
			result = _tmp0_;
			return result;
		}
		case CHESS_CLOCK_TYPE_FISCHER:
		{
			gchar* _tmp1_;
			_tmp1_ = g_strdup ("fischer");
			result = _tmp1_;
			return result;
		}
		case CHESS_CLOCK_TYPE_BRONSTEIN:
		{
			gchar* _tmp2_;
			_tmp2_ = g_strdup ("bronstein");
			result = _tmp2_;
			return result;
		}
		default:
		{
			g_assert_not_reached ();
		}
	}
}

ChessClockType
chess_clock_type_string_to_enum (const gchar* s)
{
	const gchar* _tmp0_;
	GQuark _tmp2_ = 0U;
	static GQuark _tmp1_label0 = 0;
	static GQuark _tmp1_label1 = 0;
	static GQuark _tmp1_label2 = 0;
	ChessClockType result;
	g_return_val_if_fail (s != NULL, 0);
	_tmp0_ = s;
	_tmp2_ = (NULL == _tmp0_) ? 0 : g_quark_from_string (_tmp0_);
	if (_tmp2_ == ((0 != _tmp1_label0) ? _tmp1_label0 : (_tmp1_label0 = g_quark_from_static_string ("simple")))) {
		switch (0) {
			default:
			{
				result = CHESS_CLOCK_TYPE_SIMPLE;
				return result;
			}
		}
	} else if (_tmp2_ == ((0 != _tmp1_label1) ? _tmp1_label1 : (_tmp1_label1 = g_quark_from_static_string ("fischer")))) {
		switch (0) {
			default:
			{
				result = CHESS_CLOCK_TYPE_FISCHER;
				return result;
			}
		}
	} else if (_tmp2_ == ((0 != _tmp1_label2) ? _tmp1_label2 : (_tmp1_label2 = g_quark_from_static_string ("bronstein")))) {
		switch (0) {
			default:
			{
				result = CHESS_CLOCK_TYPE_BRONSTEIN;
				return result;
			}
		}
	} else {
		switch (0) {
			default:
			{
				result = CHESS_CLOCK_TYPE_INVALID;
				return result;
			}
		}
	}
}

static GType
chess_clock_type_get_type_once (void)
{
	static const GEnumValue values[] = {{CHESS_CLOCK_TYPE_SIMPLE, "CHESS_CLOCK_TYPE_SIMPLE", "simple"}, {CHESS_CLOCK_TYPE_FISCHER, "CHESS_CLOCK_TYPE_FISCHER", "fischer"}, {CHESS_CLOCK_TYPE_BRONSTEIN, "CHESS_CLOCK_TYPE_BRONSTEIN", "bronstein"}, {CHESS_CLOCK_TYPE_INVALID, "CHESS_CLOCK_TYPE_INVALID", "invalid"}, {0, NULL, NULL}};
	GType chess_clock_type_type_id;
	chess_clock_type_type_id = g_enum_register_static ("ChessClockType", values);
	return chess_clock_type_type_id;
}

GType
chess_clock_type_get_type (void)
{
	static volatile gsize chess_clock_type_type_id__once = 0;
	if (g_once_init_enter (&chess_clock_type_type_id__once)) {
		GType chess_clock_type_type_id;
		chess_clock_type_type_id = chess_clock_type_get_type_once ();
		g_once_init_leave (&chess_clock_type_type_id__once, chess_clock_type_type_id);
	}
	return chess_clock_type_type_id__once;
}

static inline gpointer
chess_clock_get_instance_private (ChessClock* self)
{
	return G_STRUCT_MEMBER_P (self, ChessClock_private_offset);
}

ChessClock*
chess_clock_construct (GType object_type,
                       gint white_initial_seconds,
                       gint black_initial_seconds)
{
	ChessClock * self = NULL;
	self = (ChessClock*) g_object_new (object_type, NULL);
	self->priv->white_initial_seconds = white_initial_seconds;
	self->priv->black_initial_seconds = black_initial_seconds;
	return self;
}

ChessClock*
chess_clock_new (gint white_initial_seconds,
                 gint black_initial_seconds)
{
	return chess_clock_construct (TYPE_CHESS_CLOCK, white_initial_seconds, black_initial_seconds);
}

void
chess_clock_start (ChessClock* self)
{
	GTimer* _tmp0_;
	g_return_if_fail (self != NULL);
	if (self->priv->is_active) {
		return;
	}
	self->priv->is_active = TRUE;
	_tmp0_ = self->priv->timer;
	if (_tmp0_ == NULL) {
		GTimer* _tmp1_;
		_tmp1_ = g_timer_new ();
		_g_timer_destroy0 (self->priv->timer);
		self->priv->timer = _tmp1_;
	} else {
		GTimer* _tmp2_;
		_tmp2_ = self->priv->timer;
		g_timer_start (_tmp2_);
	}
	chess_clock_watch_timer (self);
}

static gboolean
chess_clock_tick_cb (ChessClock* self)
{
	Color _tmp0_;
	Color _tmp1_;
	gboolean _tmp4_ = FALSE;
	gboolean result;
	g_return_val_if_fail (self != NULL, FALSE);
	_tmp0_ = chess_clock_get_active_color (self);
	_tmp1_ = _tmp0_;
	if (_tmp1_ == COLOR_WHITE) {
		gint _tmp2_;
		_tmp2_ = self->priv->white_seconds_used;
		self->priv->white_seconds_used = _tmp2_ + 1;
	} else {
		gint _tmp3_;
		_tmp3_ = self->priv->black_seconds_used;
		self->priv->black_seconds_used = _tmp3_ + 1;
	}
	g_signal_emit (self, chess_clock_signals[CHESS_CLOCK_TICK_SIGNAL], 0);
	if (self->priv->white_seconds_used >= (self->priv->white_initial_seconds + self->priv->white_extra_seconds)) {
		_tmp4_ = TRUE;
	} else {
		_tmp4_ = self->priv->black_seconds_used >= (self->priv->black_initial_seconds + self->priv->black_extra_seconds);
	}
	if (_tmp4_) {
		chess_clock_stop (self);
		g_signal_emit (self, chess_clock_signals[CHESS_CLOCK_EXPIRED_SIGNAL], 0);
	}
	result = G_SOURCE_CONTINUE;
	return result;
}

void
chess_clock_stop (ChessClock* self)
{
	GTimer* _tmp0_;
	g_return_if_fail (self != NULL);
	if (!self->priv->is_active) {
		return;
	}
	_tmp0_ = self->priv->timer;
	g_timer_stop (_tmp0_);
	chess_clock_stop_watching_timer (self);
	self->priv->is_active = FALSE;
}

void
chess_clock_pause (ChessClock* self)
{
	GTimer* _tmp0_;
	g_return_if_fail (self != NULL);
	if (!self->priv->is_active) {
		return;
	}
	_tmp0_ = self->priv->timer;
	g_timer_stop (_tmp0_);
	chess_clock_stop_watching_timer (self);
	self->priv->is_active = FALSE;
}

void
chess_clock_unpause (ChessClock* self)
{
	gboolean _tmp0_ = FALSE;
	GTimer* _tmp1_;
	GTimer* _tmp2_;
	g_return_if_fail (self != NULL);
	_tmp1_ = self->priv->timer;
	if (_tmp1_ == NULL) {
		_tmp0_ = TRUE;
	} else {
		_tmp0_ = self->priv->is_active;
	}
	if (_tmp0_) {
		return;
	}
	_tmp2_ = self->priv->timer;
	g_timer_continue (_tmp2_);
	chess_clock_watch_timer (self);
	self->priv->is_active = TRUE;
}

static gboolean
_chess_clock_tick_cb_gsource_func (gpointer self)
{
	gboolean result;
	result = chess_clock_tick_cb ((ChessClock*) self);
	return result;
}

static void
chess_clock_watch_timer (ChessClock* self)
{
	g_return_if_fail (self != NULL);
	self->priv->tick_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) 1000, _chess_clock_tick_cb_gsource_func, g_object_ref (self), g_object_unref);
}

static void
chess_clock_stop_watching_timer (ChessClock* self)
{
	g_return_if_fail (self != NULL);
	g_source_remove (self->priv->tick_timeout_id);
	self->priv->tick_timeout_id = (guint) 0;
}

void
chess_clock_update_prev_move_time (ChessClock* self)
{
	Color _tmp0_;
	Color _tmp1_;
	g_return_if_fail (self != NULL);
	_tmp0_ = chess_clock_get_active_color (self);
	_tmp1_ = _tmp0_;
	if (_tmp1_ == COLOR_WHITE) {
		self->priv->black_prev_move_seconds = self->priv->black_seconds_used;
	} else {
		self->priv->white_prev_move_seconds = self->priv->white_seconds_used;
	}
}

static void
chess_clock_update_extra_seconds (ChessClock* self)
{
	gint white_move_used = 0;
	gint black_move_used = 0;
	ChessClockType _tmp0_;
	g_return_if_fail (self != NULL);
	white_move_used = 0;
	black_move_used = 0;
	_tmp0_ = self->priv->_clock_type;
	switch (_tmp0_) {
		case CHESS_CLOCK_TYPE_FISCHER:
		{
			Color _tmp1_;
			Color _tmp2_;
			_tmp1_ = chess_clock_get_active_color (self);
			_tmp2_ = _tmp1_;
			if (_tmp2_ == COLOR_WHITE) {
				gint _tmp3_;
				_tmp3_ = self->priv->_extra_seconds;
				self->priv->white_extra_seconds = self->priv->white_extra_seconds + _tmp3_;
			} else {
				gint _tmp4_;
				_tmp4_ = self->priv->_extra_seconds;
				self->priv->black_extra_seconds = self->priv->black_extra_seconds + _tmp4_;
			}
			break;
		}
		case CHESS_CLOCK_TYPE_BRONSTEIN:
		{
			Color _tmp5_;
			Color _tmp6_;
			white_move_used = self->priv->white_seconds_used - self->priv->white_prev_move_seconds;
			black_move_used = self->priv->black_seconds_used - self->priv->black_prev_move_seconds;
			_tmp5_ = chess_clock_get_active_color (self);
			_tmp6_ = _tmp5_;
			if (_tmp6_ != COLOR_WHITE) {
				gint _tmp7_;
				_tmp7_ = self->priv->_extra_seconds;
				self->priv->white_extra_seconds = self->priv->white_extra_seconds + MIN (_tmp7_, white_move_used);
			} else {
				gint _tmp8_;
				_tmp8_ = self->priv->_extra_seconds;
				self->priv->black_extra_seconds = self->priv->black_extra_seconds + MIN (_tmp8_, black_move_used);
			}
			break;
		}
		default:
		{
			break;
		}
	}
}

gint
chess_clock_get_extra_seconds (ChessClock* self)
{
	gint result;
	g_return_val_if_fail (self != NULL, 0);
	result = self->priv->_extra_seconds;
	return result;
}

void
chess_clock_set_extra_seconds (ChessClock* self,
                               gint value)
{
	gint old_value;
	g_return_if_fail (self != NULL);
	old_value = chess_clock_get_extra_seconds (self);
	if (old_value != value) {
		self->priv->_extra_seconds = value;
		g_object_notify_by_pspec ((GObject *) self, chess_clock_properties[CHESS_CLOCK_EXTRA_SECONDS_PROPERTY]);
	}
}

gint
chess_clock_get_white_remaining_seconds (ChessClock* self)
{
	gint result;
	g_return_val_if_fail (self != NULL, 0);
	result = (self->priv->white_initial_seconds + self->priv->white_extra_seconds) - self->priv->white_seconds_used;
	return result;
}

gint
chess_clock_get_black_remaining_seconds (ChessClock* self)
{
	gint result;
	g_return_val_if_fail (self != NULL, 0);
	result = (self->priv->black_initial_seconds + self->priv->black_extra_seconds) - self->priv->black_seconds_used;
	return result;
}

ChessClockType
chess_clock_get_clock_type (ChessClock* self)
{
	ChessClockType result;
	g_return_val_if_fail (self != NULL, 0);
	result = self->priv->_clock_type;
	return result;
}

void
chess_clock_set_clock_type (ChessClock* self,
                            ChessClockType value)
{
	ChessClockType old_value;
	g_return_if_fail (self != NULL);
	old_value = chess_clock_get_clock_type (self);
	if (old_value != value) {
		self->priv->_clock_type = value;
		g_object_notify_by_pspec ((GObject *) self, chess_clock_properties[CHESS_CLOCK_CLOCK_TYPE_PROPERTY]);
	}
}

Color
chess_clock_get_active_color (ChessClock* self)
{
	Color result;
	g_return_val_if_fail (self != NULL, 0);
	result = self->priv->_active_color;
	return result;
}

void
chess_clock_set_active_color (ChessClock* self,
                              Color value)
{
	Color _tmp0_;
	Color _tmp1_;
	g_return_if_fail (self != NULL);
	_tmp0_ = chess_clock_get_active_color (self);
	_tmp1_ = _tmp0_;
	if (value == _tmp1_) {
		return;
	}
	chess_clock_stop (self);
	self->priv->_active_color = value;
	chess_clock_update_extra_seconds (self);
	chess_clock_update_prev_move_time (self);
	chess_clock_start (self);
	g_object_notify_by_pspec ((GObject *) self, chess_clock_properties[CHESS_CLOCK_ACTIVE_COLOR_PROPERTY]);
}

static void
chess_clock_class_init (ChessClockClass * klass,
                        gpointer klass_data)
{
	chess_clock_parent_class = g_type_class_peek_parent (klass);
	g_type_class_adjust_private_offset (klass, &ChessClock_private_offset);
	G_OBJECT_CLASS (klass)->get_property = _vala_chess_clock_get_property;
	G_OBJECT_CLASS (klass)->set_property = _vala_chess_clock_set_property;
	G_OBJECT_CLASS (klass)->finalize = chess_clock_finalize;
	g_object_class_install_property (G_OBJECT_CLASS (klass), CHESS_CLOCK_EXTRA_SECONDS_PROPERTY, chess_clock_properties[CHESS_CLOCK_EXTRA_SECONDS_PROPERTY] = g_param_spec_int ("extra-seconds", "extra-seconds", "extra-seconds", G_MININT, G_MAXINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_WRITABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), CHESS_CLOCK_WHITE_REMAINING_SECONDS_PROPERTY, chess_clock_properties[CHESS_CLOCK_WHITE_REMAINING_SECONDS_PROPERTY] = g_param_spec_int ("white-remaining-seconds", "white-remaining-seconds", "white-remaining-seconds", G_MININT, G_MAXINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), CHESS_CLOCK_BLACK_REMAINING_SECONDS_PROPERTY, chess_clock_properties[CHESS_CLOCK_BLACK_REMAINING_SECONDS_PROPERTY] = g_param_spec_int ("black-remaining-seconds", "black-remaining-seconds", "black-remaining-seconds", G_MININT, G_MAXINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), CHESS_CLOCK_CLOCK_TYPE_PROPERTY, chess_clock_properties[CHESS_CLOCK_CLOCK_TYPE_PROPERTY] = g_param_spec_enum ("clock-type", "clock-type", "clock-type", TYPE_CHESS_CLOCK_TYPE, CHESS_CLOCK_TYPE_SIMPLE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_WRITABLE));
	g_object_class_install_property (G_OBJECT_CLASS (klass), CHESS_CLOCK_ACTIVE_COLOR_PROPERTY, chess_clock_properties[CHESS_CLOCK_ACTIVE_COLOR_PROPERTY] = g_param_spec_enum ("active-color", "active-color", "active-color", TYPE_COLOR, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE | G_PARAM_WRITABLE));
	chess_clock_signals[CHESS_CLOCK_TICK_SIGNAL] = g_signal_new ("tick", TYPE_CHESS_CLOCK, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
	chess_clock_signals[CHESS_CLOCK_EXPIRED_SIGNAL] = g_signal_new ("expired", TYPE_CHESS_CLOCK, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}

static void
chess_clock_instance_init (ChessClock * self,
                           gpointer klass)
{
	self->priv = chess_clock_get_instance_private (self);
	self->priv->white_seconds_used = 0;
	self->priv->black_seconds_used = 0;
	self->priv->white_prev_move_seconds = 0;
	self->priv->black_prev_move_seconds = 0;
	self->priv->white_extra_seconds = 0;
	self->priv->black_extra_seconds = 0;
	self->priv->_extra_seconds = 0;
	self->priv->_clock_type = CHESS_CLOCK_TYPE_SIMPLE;
	self->priv->_active_color = COLOR_WHITE;
	self->priv->tick_timeout_id = (guint) 0;
	self->priv->is_active = FALSE;
}

static void
chess_clock_finalize (GObject * obj)
{
	ChessClock * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_CHESS_CLOCK, ChessClock);
	_g_timer_destroy0 (self->priv->timer);
	G_OBJECT_CLASS (chess_clock_parent_class)->finalize (obj);
}

static GType
chess_clock_get_type_once (void)
{
	static const GTypeInfo g_define_type_info = { sizeof (ChessClockClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) chess_clock_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (ChessClock), 0, (GInstanceInitFunc) chess_clock_instance_init, NULL };
	GType chess_clock_type_id;
	chess_clock_type_id = g_type_register_static (G_TYPE_OBJECT, "ChessClock", &g_define_type_info, 0);
	ChessClock_private_offset = g_type_add_instance_private (chess_clock_type_id, sizeof (ChessClockPrivate));
	return chess_clock_type_id;
}

GType
chess_clock_get_type (void)
{
	static volatile gsize chess_clock_type_id__once = 0;
	if (g_once_init_enter (&chess_clock_type_id__once)) {
		GType chess_clock_type_id;
		chess_clock_type_id = chess_clock_get_type_once ();
		g_once_init_leave (&chess_clock_type_id__once, chess_clock_type_id);
	}
	return chess_clock_type_id__once;
}

static void
_vala_chess_clock_get_property (GObject * object,
                                guint property_id,
                                GValue * value,
                                GParamSpec * pspec)
{
	ChessClock * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (object, TYPE_CHESS_CLOCK, ChessClock);
	switch (property_id) {
		case CHESS_CLOCK_EXTRA_SECONDS_PROPERTY:
		g_value_set_int (value, chess_clock_get_extra_seconds (self));
		break;
		case CHESS_CLOCK_WHITE_REMAINING_SECONDS_PROPERTY:
		g_value_set_int (value, chess_clock_get_white_remaining_seconds (self));
		break;
		case CHESS_CLOCK_BLACK_REMAINING_SECONDS_PROPERTY:
		g_value_set_int (value, chess_clock_get_black_remaining_seconds (self));
		break;
		case CHESS_CLOCK_CLOCK_TYPE_PROPERTY:
		g_value_set_enum (value, chess_clock_get_clock_type (self));
		break;
		case CHESS_CLOCK_ACTIVE_COLOR_PROPERTY:
		g_value_set_enum (value, chess_clock_get_active_color (self));
		break;
		default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
_vala_chess_clock_set_property (GObject * object,
                                guint property_id,
                                const GValue * value,
                                GParamSpec * pspec)
{
	ChessClock * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (object, TYPE_CHESS_CLOCK, ChessClock);
	switch (property_id) {
		case CHESS_CLOCK_EXTRA_SECONDS_PROPERTY:
		chess_clock_set_extra_seconds (self, g_value_get_int (value));
		break;
		case CHESS_CLOCK_CLOCK_TYPE_PROPERTY:
		chess_clock_set_clock_type (self, g_value_get_enum (value));
		break;
		case CHESS_CLOCK_ACTIVE_COLOR_PROPERTY:
		chess_clock_set_active_color (self, g_value_get_enum (value));
		break;
		default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

