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 emulator.dcpu.updatequeue;
8 
9 import std.algorithm : swap, filter, sort;
10 import std.stdio;
11 
12 import emulator.dcpu.devices.idevice;
13 
14 struct UpdateQuery(Cpu)
15 {
16 	IDevice!Cpu device;
17 	ulong delay;
18 }
19 
20 struct UpdateQueue(Cpu)
21 {
22 	ulong ticksAccumulated;
23 	bool empty = true;
24 	UpdateQuery!Cpu*[] queries;
25 
26 	void onTick(ulong ticksElapsed)
27 	{
28 		ticksAccumulated += ticksElapsed;
29 
30 		if (!empty && ticksAccumulated > queries[0].delay)
31 		{
32 			queries_loop:
33 			foreach(ref query; queries)
34 			{
35 				ulong ticks = ticksAccumulated;
36 				while (ticks >= query.delay)
37 				{
38 					ticks -= query.delay;
39 					query.device.handleUpdateQuery(query.delay);
40 					//writefln("delay %s", query.delay);
41 					if (query.delay == 0) // remove query
42 					{
43 						destroy(query);
44 						continue queries_loop;
45 					}
46 				}
47 
48 				query.delay -= ticks;
49 			}
50 
51 			//writefln("queries %s", queries);
52 
53 			UpdateQuery!Cpu*[] tempQueries = queries;
54 			queries.length = 0;
55 
56 			foreach(ref query; tempQueries.filter!((a) => a !is null))
57 			{
58 				queries.assumeSafeAppend() ~= query;
59 			}
60 
61 			//writefln("queries %s", queries);
62 
63 			ticksAccumulated = 0;
64 
65 			if (queries.length == 0)
66 			{
67 				empty = true;
68 				return;
69 			}
70 
71 			//writefln("queries %s", queries);
72 
73 			sort!("a.delay < b.delay")(queries);
74 
75 			//writefln("queries %s", queries);
76 		}
77 
78 		//writefln("on tick %s", queries);
79 	}
80 
81 	/// Adds update query
82 	void addQuery(IDevice!Cpu device, ulong delay)
83 	{
84 		//writefln("begin add %s, %s", device, queries);
85 		ulong realDelay = delay + ticksAccumulated;
86 
87 		queries ~= new UpdateQuery!Cpu(device, realDelay);
88 
89 		foreach(i, query; queries)
90 		{
91 			if ((*query).delay > realDelay) // found place
92 			{
93 				foreach_reverse(j; i+1..queries.length)
94 				{
95 					swap(queries[j], queries[j-1]);
96 				}
97 
98 				break;
99 			}
100 		}
101 		
102 		empty = false;
103 
104 		//writefln("end add %s", queries);
105 	}
106 
107 	/// Removes all occurences of device in queries in place.
108 	void removeQueries(IDevice!Cpu device)
109 	{
110 		//writefln("begin remove %s, %s", device, queries);
111 		UpdateQuery!Cpu*[] tempQueries = queries;
112 		queries.length = 0;
113 
114 		foreach(ref query; tempQueries.filter!((a) => (*a).device != device))
115 		{
116 			queries.assumeSafeAppend() ~= query;
117 		}
118 
119 		if (queries.length == 0) empty = true;
120 
121 		//writefln("end remove %s", queries);
122 	}
123 }