1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2019 Aurora Free Open Source Software.
10 Copyright (C) 2019 João Lourenço <joao@aurorafoss.org>
11 
12 This file is part of the Aurora Free Open Source Software. This
13 organization promote free and open source software that you can
14 redistribute and/or modify under the terms of the GNU Lesser General
15 Public License Version 3 as published by the Free Software Foundation or
16 (at your option) any later version approved by the Aurora Free Open Source
17 Software Organization. The license is available in the package root path
18 as 'LICENSE' file. Please review the following information to ensure the
19 GNU Lesser General Public License version 3 requirements will be met:
20 https://www.gnu.org/licenses/lgpl.html .
21 
22 Alternatively, this file may be used under the terms of the GNU General
23 Public License version 3 or later as published by the Free Software
24 Foundation. Please review the following information to ensure the GNU
25 General Public License requirements will be met:
26 https://www.gnu.org/licenses/gpl-3.0.html.
27 
28 NOTE: All products, services or anything associated to trademarks and
29 service marks used or referenced on this file are the property of their
30 respective companies/owners or its subsidiaries. Other names and brands
31 may be claimed as the property of others.
32 
33 For more info about intellectual property visit: aurorafoss.org or
34 directly send an email to: contact (at) aurorafoss.org .
35 */
36 
37 module aurorafw.entity.systemmanager;
38 
39 import aurorafw.entity.system;
40 import aurorafw.entity.entitymanager;
41 
42 version(unittest) import aurorafw.unit.assertion;
43 
44 import std.traits : fullyQualifiedName;
45 import std.exception;
46 
47 
48 final class SystemHandlingException : Exception
49 {
50 	mixin basicExceptionCtors;
51 }
52 
53 
54 class SystemManager
55 {
56 	// unittesting only
57 	@safe pure
58 	private this()
59 	{}
60 
61 	@safe pure
62 	public this(EntityManager entity)
63 	{
64 		_entity = entity;
65 	}
66 
67 
68 	/**
69 	 * Create a new system
70 	 *
71 	 * Examples:
72 	 * --------------------
73 	 * world.system.create(new FooSystem());
74 	 * --------------------
75 	 *
76 	 * SeeAlso: create(S : System)()
77 	 */
78 	@safe pure
79 	public void create(S : System)(S s)
80 	{
81 		enum id = fullyQualifiedName!S;
82 
83 		if (id in this.systems)
84 			throw new SystemHandlingException(
85 				"Cannot add system. " ~ __traits(identifier, S) ~ " is already created!"
86 			);
87 
88 		s.manager = this;
89 		this.systems[id] = s;
90 	}
91 
92 
93 	/**
94 	 * Create a new System
95 	 *
96 	 * Examples:
97 	 * --------------------
98 	 * world.system.create!FooSystem;
99 	 * --------------------
100 	 */
101 	@safe pure
102 	public void create(S : System)()
103 	{
104 		create(new S());
105 	}
106 
107 
108 	/**
109 	 * Get a system
110 	 *
111 	 * Returns: the type you're passing
112 	 *
113 	 * Examples:
114 	 * --------------------
115 	 * world.system.get!FooSystem
116 	 * --------------------
117 	 */
118 	@safe pure
119 	public S get(S : System)()
120 	{
121 		enum id = fullyQualifiedName!S;
122 		System* p;
123 		p = id in this.systems;
124 
125 		return p !is null ? cast(S)(*p) : null;
126 	}
127 
128 
129 	/**
130 	 * Remove
131 	 *
132 	 * Deletes a system from the world's scope
133 	 *
134 	 * Examples:
135 	 * --------------------
136 	 * world.system.remove!FooSystem
137 	 * --------------------
138 	 */
139 	@safe pure
140 	public void remove(S : System)()
141 	{
142 		enum id = fullyQualifiedName!S;
143 
144 		if (id in this.systems)
145 			this.systems.remove(id);
146 
147 		else
148 			throw new SystemHandlingException(
149 				"Cannot remove system. " ~ __traits(identifier, S) ~ " was already removed or it wasn't created!"
150 			);
151 	}
152 
153 
154 	/**
155 	 * Clear
156 	 *
157 	 * Deletes every system in the world's scope
158 	 *
159 	 * Examples:
160 	 * --------------------
161 	 * world.system.clear();
162 	 * --------------------
163 	 */
164 	@safe pure
165 	public void clear()
166 	{
167 		foreach(key, value; this.systems)
168 			this.systems.remove(key);
169 	}
170 
171 
172 	/**
173 	 * Update
174 	 *
175 	 * Updates every component with the Automatic Update Policy
176 	 *
177 	 * Examples:
178 	 * --------------------
179 	 * world.system.update();
180 	 * --------------------
181 	 */
182 	@safe pure
183 	public void update()
184 	{
185 		foreach(System s; this.systems)
186 			if (s.updatePolicy == s.UpdatePolicy.Automatic)
187 				s.update();
188 	}
189 
190 
191 	/**
192 	 * Update
193 	 *
194 	 * Updates any system passed
195 	 * It's useful when you only want to update systems manualy
196 	 *
197 	 * Examples:
198 	 * --------------------
199 	 * world.system.update!FooSystem;
200 	 * --------------------
201 	 */
202 	@safe pure
203 	public void update(S : System)()
204 	{
205 		this.systems[fullyQualifiedName!S].update();
206 	}
207 
208 
209 	@safe pure
210 	public EntityManager entity() { return _entity; } @property
211 
212 
213 	private EntityManager _entity;
214 	private System[string] systems;
215 }
216 
217 
218 version(unittest)
219 {
220 	private class unittest_FooSystem : System
221 	{
222 		override public void update() { this.updatePolicy = UpdatePolicy.Manual; }
223 	}
224 
225 	private class unittest_BarSystem : System
226 	{
227 		@safe pure
228 		public this () { super(UpdatePolicy.Manual); }
229 
230 		override public void update() { this.updatePolicy = UpdatePolicy.Automatic; }
231 	}
232 }
233 
234 
235 ///
236 @safe pure
237 @("System Manager: System creation")
238 unittest
239 {
240 	SystemManager system = new SystemManager();
241 
242 	system.create(new unittest_FooSystem());
243 
244 	import std.traits : ReturnType;
245 	assertTrue(is(ReturnType!(system.get!unittest_FooSystem) == unittest_FooSystem));
246 
247 	assertThrown!SystemHandlingException(system.create(new unittest_FooSystem())); // System created twice
248 }
249 
250 
251 ///
252 @safe pure
253 @("System Manager: Accessing system properties")
254 unittest
255 {
256 	SystemManager system = new SystemManager();
257 
258 	system.create(new unittest_FooSystem());
259 
260 	import std.traits : ReturnType;
261 	assertTrue((system.get!(unittest_FooSystem)).manager is system);
262 	assertTrue(is(ReturnType!(system.get!unittest_FooSystem) == unittest_FooSystem));
263 	assertTrue(system.get!(unittest_BarSystem) is null); // System wasn't created
264 }
265 
266 
267 ///
268 @safe pure
269 @("System Manager: System update")
270 unittest
271 {
272 	SystemManager system = new SystemManager();
273 
274 	unittest_FooSystem fooSys = new unittest_FooSystem();
275 	unittest_BarSystem barSys = new unittest_BarSystem();
276 	system.create(fooSys);
277 	system.create(barSys);
278 
279 	assertEquals(fooSys.updatePolicy, fooSys.UpdatePolicy.Automatic);
280 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Manual);
281 
282 	system.update(); // Only updates systems with Automatic Update Policy
283 
284 	assertEquals(fooSys.updatePolicy, fooSys.UpdatePolicy.Manual); // Got updated
285 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Manual); // Didn't get updated
286 
287 	system.update!unittest_BarSystem; // Systems can be updated manualy
288 
289 	assertEquals(barSys.updatePolicy, barSys.UpdatePolicy.Automatic); // Got updated
290 }
291 
292 
293 ///
294 @safe pure
295 @("System Manager: System clear")
296 unittest
297 {
298 	SystemManager system = new SystemManager();
299 
300 	system.create!unittest_FooSystem;
301 	system.clear();
302 
303 	assertEquals(0, system.systems.length);
304 }