1 module godot.api.reference;
2 
3 import std.meta, std.traits; // std.typecons;
4 import std.algorithm : swap;
5 
6 import godot, godot.abi;
7 import godot.refcounted, godot.object;
8 import godot.api.traits, godot.api.script;
9 
10 /// Ref-counted container for Reference types
11 struct Ref(T) {
12     static assert(extends!(T, RefCounted), "Ref may only be used with Reference-derived classes. Other Godot classes are not reference-counted.");
13     static assert(!is(T == const), "Ref cannot contain a const Reference");
14     //@nogc nothrow:
15 
16     static if (isGodotBaseClass!T) {
17         package(godot) T _reference;
18         alias _self = _reference;
19     } else {
20         package(godot) T _self;
21         pragma(inline, true)
22         package(godot) GodotClass!T _reference() {
23             return (_self) ? _self.owner : GodotClass!T.init;
24         }
25     }
26 
27     /++
28 	Returns the reference without allowing it to escape the calling scope.
29 	
30 	TODO: dip1000
31 	+/
32     T refPayload() const {
33         return cast() _self;
34     }
35 
36     alias refPayload this;
37 
38     ref Ref opAssign(T other) {
39         if (_self.getGodotObject == other.getGodotObject)
40             return this;
41         unref();
42         _self = other;
43         if (_self)
44             _reference.reference();
45         return this;
46     }
47 
48     ref Ref opAssign(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) {
49         opAssign(other._self);
50         return this;
51     }
52 
53     ref Ref opAssign(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) {
54         swap(_self, other);
55         return this;
56     }
57 
58     void unref() {
59         if (_self && _reference.unreference()) {
60             _godot_api.object_destroy(&_reference._godot_object);
61         }
62         _self = T.init;
63     }
64 
65     Ref!U as(U)() if (isGodotClass!U && !is(U == GodotObject)) {
66         // the only non-Reference this can possibly be is Object, so no need to account for non-Refs
67         static assert(extends!(U, T) || extends!(T, U),
68             U.stringof ~ " is not polymorphic to " ~ T.stringof);
69         Ref!U ret = _self.as!U;
70         return ret;
71     }
72 
73     template as(R) if (is(R : Ref!U, U) && isGodotClass!(NonRef!R)) {
74         alias as = as!(NonRef!R);
75     }
76 
77     GodotObject as(R)() if (is(Unqual!R == GodotObject)) {
78         return _reference;
79     }
80 
81     template opCast(R) if (isGodotClass!(NonRef!R)) {
82         alias opCast = as!R;
83     }
84 
85     pragma(inline, true)
86     bool opEquals(R)(in auto ref R other) const {
87         return _self.getGDNativeObject!T == other.getGDNativeObject!T;
88     }
89 
90     pragma(inline, true)
91     bool isValid() const {
92         return _self.getGodotObject != GodotClass!T.init;
93     }
94 
95     alias opCast(T : bool) = isValid;
96     pragma(inline, true)
97     bool isNull() const {
98         return _self.getGodotObject == GodotClass!T.init;
99     }
100 
101     this(this) {
102         if (_self)
103             _reference.reference();
104     }
105 
106     /++
107 	Construct from other reference
108 	+/
109     this(T other) {
110         _self = other;
111         if (_self)
112             _reference.reference();
113     }
114 
115     this(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) {
116         _self = other._self;
117         if (_self)
118             _reference.reference();
119     }
120 
121     this(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) {
122         swap(_self, other);
123     }
124 
125     ~this() {
126         //unref();
127     }
128 }
129 
130 /++
131 Create a Ref from a pointer without incrementing refcount.
132 +/
133 package(godot) RefOrT!T refOrT(T)(T instance) {
134     static if (extends!(T, RefCounted)) {
135         Ref!T ret = void;
136         ret._self = instance;
137         return ret;
138     } else
139         return instance;
140 }
141 
142 /++
143 Create a Ref from a pointer and increment refcount.
144 +/
145 package(godot) RefOrT!T refOrTInc(T)(T instance) {
146     static if (extends!(T, RefCounted)) {
147         Ref!T ret = void;
148         ret._self = instance;
149         if (ret._self)
150             ret._reference.reference();
151         return ret;
152     } else
153         return instance;
154 }