1 /**
2 Copyright: Copyright (c) 2013-2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.gui.utils.timermanager;
8 
9 public import anchovy.gui.utils.timer;
10 
11 import core.time;
12 import std.array;
13 import std.algorithm;
14 import std.math: isNaN;
15 import std.stdio;
16 
17 import anchovy.core.types;
18 
19 class TimerManager
20 {
21 	this(double delegate() currentTimeCallback)
22 	in
23 	{
24 		assert(currentTimeCallback);
25 	}
26 	body
27 	{
28 		freeTimers.reserve(128);
29 		queue.reserve(128);
30 		currentTime = currentTimeCallback;
31 	}
32 
33 	void updateTimers(double currentTime)
34 	{
35 		while(!queue.empty)
36 		{
37 			foreach(i, t; queue)
38 			{
39 				if (currentTime < t.nextUpdateTime)
40 					return;
41 				t.tick(currentTime);
42 				if (t.nextUpdateTime <= 0 || isNaN(t.nextUpdateTime))
43 				{
44 					freeTimers ~= t;
45 					queue = queue[0..i] ~ queue[i + 1..$];
46 					break;
47 				}
48 				if (t.tickType == TimerTickType.PROCESS_ALL_ORDERED)
49 				{
50 					break;
51 				}
52 			}
53 			sortTimers();
54 		}
55 	}
56 
57 	/// 	initialDelay can be used to specify first delay to be different from following, that are set with delay parameter.
58 	/// 				Must be not NaN and > 0 to be used as first delay.
59 	Timer addTimer(double _delay, TimerHandler _handler, double _initialDelay = double.nan, TimerTickType _tickType = TimerTickType.init)
60 	{
61 		Timer timer = popFreeTimer();
62 
63 		double startTime = currentTime();
64 
65 		if (!isNaN(_initialDelay) || _initialDelay < 0)
66 		{
67 			startTime += _initialDelay;
68 		}
69 		else
70 			startTime += _delay;
71 
72 		timer.initialize(startTime, currentTime(), _delay, _handler, _tickType);
73 		addToQueue(timer);
74 
75 		return timer;
76 	}
77 
78 	/// Resets timer's delay to newDelay if > 0 or to timer.delay otherwise.
79 	///
80 	/// Timer.delay will not be changed. Timer.nextUpdate only chabges.
81 	/// If you wish change Timer.delay you can do this by returning new delay in timer callback or
82 	/// by setting it directly trough the reference returned by addTimer.
83 	void resetTimer(Timer timer, double newDelay = double.nan)
84 	{
85 		double _delay = newDelay;
86 		if (!(_delay > 0)) _delay = timer.delay;
87 		timer.nextUpdateTime = currentTime() + _delay;
88 
89 		sortTimers();
90 	}
91 
92 	void stopTimer(Timer timer)
93 	{
94 		foreach(i, ref t; queue)
95 		{
96 			if (t == timer)
97 			{
98 				freeTimers ~= t;
99 				queue = queue[0..i] ~ queue[i + 1..$];
100 				return;
101 			}
102 		}
103 
104 		assert("Tried to stop not running timer");
105 	}
106 
107 protected:
108 
109 	Timer popFreeTimer()
110 	{
111 		if (freeTimers.length > 0)
112 		{
113 			scope(exit) freeTimers.popBack;
114 			return freeTimers.back;
115 		}
116 		else
117 		{
118 			return new Timer();
119 		}
120 	}
121 
122 	/// timer must be previously removed from freeTimers.
123 	void addToQueue(Timer timer)
124 	{
125 		queue ~= timer;
126 		sortTimers();
127 	}
128 
129 	void sortTimers()
130 	{
131 		sort!(q{a.nextUpdateTime < b.nextUpdateTime})(queue);
132 	}
133 
134 	Timer[]	freeTimers;
135 	Timer[] queue;
136 
137 	double delegate()	currentTime;
138 }