public class ChessClock : Object
{
    private uint _white_duration;
    public uint white_duration
    {
        get { return _white_duration; }
    }
    private uint _black_duration;
    public uint black_duration
    {
        get { return _black_duration; }
    }

    private uint _white_used;
    public uint white_used
    {
        get
        {
            if (active_color == Color.WHITE)
                return _white_used + (uint) (timer.elapsed () * 1000);
            else
                return _white_used;
        }
    }

    public uint white_used_in_seconds
    {
        get { return (white_used + 500) / 1000; }
    }

    private uint _black_used;
    public uint black_used
    {
        get
        {
            if (active_color == Color.WHITE)
                return _black_used;
            else
                return _black_used + (uint) (timer.elapsed () * 1000);
        }
    }

    public uint black_used_in_seconds
    {
        get { return (black_used + 500) / 1000; }
    }

    public Color _active_color = Color.WHITE;
    public Color active_color
    {
        get { return _active_color; }
        set
        {
            if (value == active_color)
                return;

            stop ();
            _active_color = value;
            start ();
        }
    }

    private Timer timer;
    private uint expire_timeout = 0;
    private uint tick_timeout = 0;

    public signal void tick ();
    public signal void expired ();

    public ChessClock (uint white_duration, uint black_duration, uint white_used = 0, uint black_used = 0)
    {
        _white_duration = white_duration * 1000;
        _black_duration = black_duration * 1000;
        _white_used = white_used;
        _black_used = black_used;
        timer = new Timer ();
    }
    
    private bool is_started
    {
        get { return expire_timeout != 0; }
    }
    
    public void start ()
    {
        if (is_started)
            return;

        /* Start stopwatch */
        timer.start ();

        /* Notify when this timer has expired */
        if (active_color == Color.WHITE)
            expire_timeout = Timeout.add (white_duration - _white_used, timer_expired_cb);
        else
            expire_timeout = Timeout.add (black_duration - _black_used, timer_expired_cb);

        /* Wake up each second */
        tick_cb ();
    }

    private bool timer_expired_cb ()
    {
        stop ();
        expired ();
        return false;
    }

    private bool tick_cb ()
    {
        if (tick_timeout != 0)
            tick ();

        uint elapsed = (uint) (timer.elapsed () * 1000);
        uint used;
        if (active_color == Color.WHITE)
            used = _white_used + elapsed;
        else
            used = _black_used + elapsed;
        var next_tick_time = ((used / 1000) + 1) * 1000;
        tick_timeout = Timeout.add (next_tick_time - used, tick_cb);

        return false;
    }

    public void stop ()
    {
        if (!is_started)
            return;

        timer.stop ();
        Source.remove (expire_timeout);
        expire_timeout = 0;
        Source.remove (tick_timeout);
        tick_timeout = 0;

        var elapsed = (uint) (timer.elapsed () * 1000);
        if (active_color == Color.WHITE)
        {
            _white_used += elapsed;
            if (_white_used > white_duration)
                _white_used = white_duration;
        }
        else
        {
            _black_used += elapsed;
            if (_black_used > black_duration)
                _black_used = black_duration;
        }

        timer.reset ();
    }
}
