1 /**
2 Dynamic Variant array.
3 
4 Copyright:
5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017 Godot-D contributors
8 Copyright (c) 2022 Godot-DLang contributors
9 
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11 
12 
13 */
14 module godot.array;
15 
16 import godot.abi;
17 import godot.variant;
18 import godot.poolarrays;
19 
20 // generated raw bindings
21 import godot.builtins;
22 import godot.api.bind;
23 
24 import std.meta;
25 import std.traits, std.range;
26 import std.exception : assumeWontThrow;
27 
28 /**
29 Generic array, contains several elements of any type, accessible by numerical index starting at 0. Negative indices can be used to count from the right, like in Python. Arrays are always passed by reference.
30 */
31 struct Array {
32     int opApply(int delegate(size_t, ref Variant) dg) {
33         foreach (i; 0 .. length) {
34             Variant* v = cast(Variant*)&(this[i]);
35             int res = dg(cast(size_t) i, *v);
36             if (res)
37                 return res;
38         }
39         return 0;
40     }
41 
42     int opApply(int delegate(size_t, const(Variant)) dg) const {
43         foreach (i; 0 .. length) {
44             int res = dg(cast(size_t) i, this[i]);
45             if (res)
46                 return res;
47         }
48         return 0;
49     }
50 
51     int opApply(int delegate(ref Variant) dg) {
52         foreach (i; 0 .. length) {
53             Variant* v = cast(Variant*)&(this[i]);
54             int res = dg(*v);
55             if (res)
56                 return res;
57         }
58         return 0;
59     }
60 
61     int opApply(int delegate(const(Variant)) dg) const {
62         foreach (i; 0 .. length) {
63             int res = dg(this[i]);
64             if (res)
65                 return res;
66         }
67         return 0;
68     }
69 
70     /// Convert to a static array.
71     /// Excess elements are discarded if the Array is longer than `T`.
72     T as(T)() const 
73             if (isStaticArray!T && Variant.compatibleFromGodot!(ElementType!T)) {
74         import std.algorithm : min;
75 
76         T ret;
77         foreach (i; 0 .. min(T.length, length))
78             ret[i] = (this[i]).as!(ElementType!T);
79         return ret;
80     }
81 
82     /// Create an Array from any D range or static array with compatible elements.
83     static Array from(T)(T t)
84             if ((isForwardRange!T || isStaticArray!T) && Variant.compatibleToGodot!(
85                 ElementType!T)) {
86         import std.algorithm.iteration;
87 
88         Array ret = Array.make();
89         static if (hasLength!T) {
90             ret.resize(cast(int) t.length);
91             int ei = 0;
92             foreach (e; t)
93                 ret[ei++] = e;
94         } else
95             t.each!(e => ret ~= e);
96         return ret;
97     }
98 
99     //@nogc nothrow:
100 
101     package(godot) {
102         union array {
103             godot_array _godot_array;
104             Array_Bind _bind;
105         }
106 
107         array _array;
108     }
109 
110     alias _array this;
111 
112     package(godot) this(godot_array opaque) {
113         _godot_array = opaque;
114     }
115 
116     //@disable this(this);
117     //{
118     //	const godot_array tmp = _godot_array;
119     //	_godot_api.get_variant_from_type_constructor(GDEXTENSION_VARIANT_TYPE_ARRAY)(cast(GDExtensionTypePtr) &_godot_array, cast(GDExtensionTypePtr) &tmp);
120     //	this = _bind.duplicate(false);
121     //}
122 
123     this(const scope ref Array other) {
124         //if (&_godot_array)
125         //	_bind._destructor();
126         _godot_array = other._godot_array;
127     }
128 
129     Array opAssign(in Array other) {
130         //if (&_godot_array)
131         //	_bind._destructor();
132         this = _bind.new1(other); // do we actually need a copy here?
133         return this;
134         //_godot_api.variant_destroy(&_godot_array);
135         //_godot_api.variant_new_copy(&_godot_array, &other._godot_array);
136         //return this;
137     }
138 
139     /++
140 	Assigning null empties the Array variable, but unlike `clear`, does not
141 	destroy the original memory unless it was the only remaining reference.
142 	+/
143     Array opAssign(in typeof(null) n) {
144         return opAssign(Array.make());
145     }
146 
147     /++
148 	Create an array and add all $(PARAM args) to it.
149 	+/
150     static Array make(Args...)(Args args)
151             if (allSatisfy!(Variant.compatibleToGodot, Args)) {
152         Array ret = void;
153         ret._godot_array = godot_array.init;
154         auto ct = _godot_api.variant_get_ptr_constructor(GDEXTENSION_VARIANT_TYPE_ARRAY, 0);
155         ct(cast(GDExtensionTypePtr)&ret._godot_array, null);
156 
157         static if (args.length)
158             ret.resize(args.length);
159         static foreach (i, Arg; Args) {
160             ret[i] = args[i];
161         }
162         return ret;
163     }
164 
165     deprecated("Use Array.make() with 0 args instead.")
166     static Array empty_array() {
167         Array ret = void;
168         _godot_api.variant_new_nil(&ret._godot_array);
169         return ret;
170     }
171 
172     this(in typeof(null) n) {
173         //_godot_api.variant_new_nil(&_godot_array);
174         _bind.new0();
175     }
176 
177     // TODO: verify the following array constructors, since previous API's are gone
178     // it now uses overloads of Array.from() extension method
179     this(in PackedByteArray a) {
180         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
181     }
182 
183     this(in PackedInt32Array a) {
184         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
185     }
186 
187     this(in PackedInt64Array a) {
188         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
189     }
190 
191     this(in PackedFloat32Array a) {
192         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
193     }
194 
195     this(in PackedFloat64Array a) {
196         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
197     }
198 
199     this(in PackedStringArray a) {
200         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
201     }
202 
203     this(in PackedVector2Array a) {
204         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
205     }
206 
207     this(in PackedVector3Array a) {
208         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
209     }
210 
211     this(in PackedColorArray a) {
212         _godot_api.variant_new_copy(&_godot_array, &a._godot_array);
213     }
214 
215     auto ref inout(Variant) opIndex(size_t idx) inout {
216         godot_variant* v = cast(godot_variant*) _godot_api.array_operator_index(
217             cast(godot_array*)&_godot_array, cast(int) idx);
218         return *cast(inout(Variant)*) v;
219     }
220 
221     void opIndexAssign(T)(auto ref T value, in size_t idx)
222             if (is(T : Variant) || Variant.compatibleToGodot!T) {
223         Variant v = Variant(value);
224         godot_variant* val = cast(godot_variant*) _godot_api.array_operator_index(
225             &_godot_array, cast(int) idx);
226         *val = v._godot_variant;
227     }
228 
229     /// Append a single element.
230     ///
231     /// Note: an Array or range will be appended as one single element of type
232     /// Array, *not* concatenated to this Array. Use `appendRange` or
233     /// `appendArray` to concatenate/chain ranges or Arrays into one.
234     void append(T)(auto ref T t) if (is(T : Variant) || Variant.compatibleToGodot!T) {
235         import godot.string;
236 
237         Variant v = Variant(t);
238         //_godot_api.array_append(&_godot_array, &v._godot_variant);
239         _bind.append(v);
240     }
241 
242     void append(const(void)* nativeObjectPtr) {
243         import godot.string;
244 
245         Array a = void;
246         a._godot_array = _godot_array;
247         // TODO: implement me
248         //Variant v = Variant.from(a);
249         //_godot_api.array_append(&_godot_array, &v._godot_variant);
250         //v.as!Array.append(nativeObjectPtr);
251     }
252     /// ditto
253     template opOpAssign(string op) if (op == "~" || op == "+") {
254         alias opOpAssign = append;
255     }
256 
257     /// Concatenate a range or another Array to the end of this one.
258     void appendRange(R)(in auto ref R other)
259             if (
260                 !is(Unqual!R : Array) &&
261             isInputRange!R &&
262             (is(ElementType!R
263             : Variant) || Variant.compatible!(ElementType!R))) {
264         static if (hasLength!R) {
265             size_t l = length;
266             resize(l + other.length);
267             Variant[] slice = this[];
268             size_t i = l;
269             foreach (const v; other)
270                 slice[i++] = v;
271         } else
272             foreach (const v; other)
273                 append(v);
274     }
275     /// ditto
276     void appendArray(in ref Array other) {
277         appendRange(other[]);
278     }
279 
280     private static Array fromConcat(R, S)(in auto ref R r, in auto ref S s) {
281         Array ret = Array.make();
282         ret.resize(r.length + s.length);
283         Variant[] slice = ret[];
284         size_t i = 0;
285         foreach (const v; r)
286             slice[i++] = v;
287         foreach (const v; s)
288             slice[i++] = v;
289         return ret;
290     }
291     /// Concatenate two arrays into a new one. The originals are left unaffected
292     /// if there are still other references to them remaining.
293     Array opBinary(string op, R)(in auto ref R other)
294             if (
295                 (op == "~" || op == "+") && !is(Unqual!R : Array) &&
296             isInputRange!R && hasLength!R &&
297             (is(ElementType!R : Variant) || Variant.compatible!(ElementType!R))) {
298         return fromConcat(this[], other);
299     }
300     /// ditto
301     Array opBinary(string op)(in auto ref Array other) if (op == "~" || op == "+") {
302         return fromConcat(this[], other[]);
303     }
304     /// ditto
305     Array opBinaryRight(string op, R)(in auto ref R other)
306             if (
307                 (op == "~" || op == "+") && !is(Unqual!R : Array) &&
308             isInputRange!R && hasLength!R &&
309             (is(ElementType!R : Variant) || Variant.compatible!(ElementType!R))) {
310         return fromConcat(other, this[]);
311     }
312 
313     void clear() {
314         _bind.clear();
315     }
316 
317     size_t count(in Variant v) {
318         //return _godot_api.array_count(&_godot_array, &v._godot_variant);
319         return _bind.count(v);
320     }
321 
322     bool empty() const {
323         //return cast(bool)_godot_api.array_empty(&_godot_array);
324         return _bind.isEmpty();
325     }
326 
327     void erase(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) {
328         Variant vv = v;
329         //_godot_api.array_erase(&_godot_array, &vv._godot_variant);
330         _bind.erase(v);
331     }
332 
333     Variant front() const {
334         godot_variant v = void;
335         v = _bind.front()._godot_variant;
336         //godot_variant v = _godot_api.array_front(&_godot_array);
337         return cast(Variant) v;
338     }
339 
340     Variant back() const {
341         //godot_variant v = _godot_api.array_back(&_godot_array);
342         //return cast(Variant)v;
343         return _bind.back();
344     }
345 
346     int find(T)(in T what, size_t from) const 
347             if (is(T : Variant) || Variant.compatibleToGodot!T) {
348         const Variant vv = what;
349         //return _godot_api.array_find(&_godot_array, &vv._godot_variant, cast(int)from);
350         return _bind.find(vv, from);
351     }
352 
353     int findLast(T)(in T what) const 
354             if (is(T : Variant) || Variant.compatibleToGodot!T) {
355         const Variant vv = what;
356         //return _godot_api.array_find_last(&_godot_array, &vv._godot_variant);
357         return _bind.findLast(vv);
358     }
359 
360     bool has(T)(in T what) const if (is(T : Variant) || Variant.compatibleToGodot!T) {
361         const Variant vv = what;
362         //return cast(bool)_godot_api.array_has(&_godot_array, &vv._godot_variant);
363         return _bind.has(vv);
364     }
365 
366     @trusted
367     uint hash() const nothrow {
368         //return _godot_api.array_hash(&_godot_array);
369         return cast(uint) assumeWontThrow(_bind.hash());
370     }
371 
372     @trusted
373     hash_t toHash() const nothrow {
374         return cast(hash_t) hash();
375     }
376 
377     void insert(T)(const size_t pos, T value)
378             if (is(T : Variant) || Variant.compatibleToGodot!T) {
379         Variant vv = value;
380         //_godot_api.array_insert(&_godot_array, cast(int)pos, &vv._godot_variant);
381         _bind.insert(pos, vv);
382     }
383 
384     void invert() {
385         //_godot_api.array_invert(&_godot_array);
386         _bind.reverse();
387     }
388 
389     Variant popBack() {
390         //godot_variant v = _godot_api.array_pop_back(&_godot_array);
391         //return cast(Variant)v;
392         return _bind.popBack();
393     }
394 
395     Variant popFront() {
396         //godot_variant v = _godot_api.array_pop_front(&_godot_array);
397         //return cast(Variant)v;
398         return _bind.popFront();
399     }
400 
401     void pushBack(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) {
402         Variant vv = v;
403         //_godot_api.array_push_back(&_godot_array, &vv._godot_variant);
404         _bind.pushBack(vv);
405     }
406 
407     void pushFront(T)(T v) if (is(T : Variant) || Variant.compatibleToGodot!T) {
408         Variant vv = v;
409         //_godot_api.array_push_front(&_godot_array, &vv._godot_variant);
410         _bind.pushFront(vv);
411     }
412 
413     void remove(size_t idx) {
414         //_godot_api.array_remove(&_godot_array, cast(int)idx);
415         _bind.removeAt(idx);
416     }
417 
418     size_t size() const {
419         //return _godot_api.array_size(&_godot_array);
420         return _bind.size();
421     }
422 
423     alias length = size; // D-style `length`
424     alias opDollar = size;
425 
426     void resize(size_t size) {
427         //_godot_api.array_resize(&_godot_array, cast(int)size);
428         _bind.resize(size);
429     }
430 
431     int rfind(T)(in T what, size_t from) const 
432             if (is(T : Variant) || Variant.compatibleToGodot!T) {
433         const Variant vv = what;
434         //return _godot_api.array_rfind(&_godot_array, &vv._godot_variant, cast(int)from);
435         return _bind.rfind(vv, from);
436     }
437 
438     void sort() {
439         //_godot_api.array_sort(&_godot_array);
440         _bind.sort();
441     }
442 
443     /+void sort_custom(godot.Object obj, in ref String func)
444 	{
445 		_godot_api.array_sort_custom(&_godot_array, obj, &func._godot_string);
446 	}+/
447 
448     /// Allocate a new separate copy of the Array
449     Array dup() const {
450         Array ret = Array.make();
451         size_t l = size();
452         ret.resize(l);
453         foreach (vi; 0 .. l) {
454             ret[vi] = this[vi];
455         }
456         return ret;
457     }
458 
459     /// Returns: a new Array containing a slice of the original. It is a copy,
460     /// *not* a reference to the original Array's memory.
461     ///
462     /// Note: `end` is non-inclusive, as in D slice operations, not as in Godot.
463     Array slice(size_t start, size_t end, size_t stride = 1, bool deep = false) const {
464         //Array ret = void;
465         //ret._godot_array = _godot_api.array_slice(&_godot_array,
466         //	cast(int)start, cast(int)(end-1), cast(int)stride, deep);
467         return _bind.slice(start, end, stride, deep);
468     }
469 
470     /++
471 	Returns: a slice of the array memory. The slice does *not* have ownership of
472 	the reference-counted memory and is invalid after the original Array goes
473 	out of scope or is resized.
474 	+/
475     Variant[] opSlice(size_t start, size_t end) {
476         Variant* ret = cast(Variant*) _godot_api.array_operator_index(&_godot_array, 0);
477         return ret[start .. end];
478     }
479     /// ditto
480     const(Variant)[] opSlice(size_t start, size_t end) const {
481         const(Variant)* ret = cast(const(Variant)*) _godot_api.array_operator_index_const(
482             &_godot_array, 0);
483         return ret[start .. end];
484     }
485     /// ditto
486     Variant[] opSlice() {
487         return this[0 .. length];
488     }
489     /// ditto
490     const(Variant)[] opSlice() const {
491         return this[0 .. length];
492     }
493     // TODO: `scope` for the returned slices?
494 
495     ~this() {
496         //if (&_godot_array)
497         //	_bind._destructor();
498         _array = _array.init;
499         //_godot_api.variant_destroy(&_godot_array);
500     }
501 }
502 
503 struct TypedArray(T) {
504     Array _array;
505     alias _array this;
506 
507     this(this) {
508     }
509 
510     this(Array other) {
511         _array = other;
512     }
513 
514     this(T[] other) {
515         _array = Array.from(other);
516     }
517 }