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             // TODO: check again when object casts will be fixed
24             //       for now it has to be this way or _self will point at random garbage
25             //       causing unexpected failures, see test project "RefTest t = n;" aroung line 356
26             return (_self) ? *cast(typeof(return)*) &_self : GodotClass!T.init;
27         }
28     }
29 
30     /++
31 	Returns the reference without allowing it to escape the calling scope.
32 	
33 	TODO: dip1000
34 	+/
35     T refPayload() const {
36         return cast() _self;
37     }
38 
39     alias refPayload this;
40 
41     ref Ref opAssign(T other) {
42         if (_self.getGodotObject == other.getGodotObject)
43             return this;
44         unref();
45         _self = other;
46         if (_self)
47             _reference.reference();
48         return this;
49     }
50 
51     ref Ref opAssign(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) {
52         opAssign(other._self);
53         return this;
54     }
55 
56     ref Ref opAssign(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) {
57         swap(_self, other);
58         return this;
59     }
60 
61     void unref() {
62         if (_self && _reference.unreference()) {
63             _godot_api.object_destroy(&_reference._godot_object);
64         }
65         _self = T.init;
66     }
67 
68     Ref!U as(U)() if (isGodotClass!U && !is(U == GodotObject)) {
69         // the only non-Reference this can possibly be is Object, so no need to account for non-Refs
70         static assert(extends!(U, T) || extends!(T, U),
71             U.stringof ~ " is not polymorphic to " ~ T.stringof);
72         Ref!U ret = _self.as!U;
73         return ret;
74     }
75 
76     template as(R) if (is(R : Ref!U, U) && isGodotClass!(NonRef!R)) {
77         alias as = as!(NonRef!R);
78     }
79 
80     GodotObject as(R)() if (is(Unqual!R == GodotObject)) {
81         return _reference;
82     }
83 
84     template opCast(R) if (isGodotClass!(NonRef!R)) {
85         alias opCast = as!R;
86     }
87 
88     pragma(inline, true)
89     bool opEquals(R)(in auto ref R other) const {
90         return _self.getGDExtensionObject!T == other.getGDExtensionObject!T;
91     }
92 
93     pragma(inline, true)
94     bool isValid() const {
95         return _self.getGodotObject != GodotClass!T.init;
96     }
97 
98     alias opCast(T : bool) = isValid;
99     pragma(inline, true)
100     bool isNull() const {
101         return _self.getGodotObject == GodotClass!T.init;
102     }
103 
104     this(this) {
105         if (_self)
106             _reference.reference();
107     }
108 
109     /++
110 	Construct from other reference
111 	+/
112     this(T other) {
113         _self = other;
114         if (_self)
115             _reference.reference();
116     }
117 
118     this(R)(ref R other) if (is(R : Ref!U, U) && extends!(T, U)) {
119         _self = other._self;
120         if (_self)
121             _reference.reference();
122     }
123 
124     this(R)(R other) if (is(R : Ref!U, U) && extends!(T, U)) {
125         swap(_self, other);
126     }
127 
128     ~this() {
129         //unref();
130     }
131 }
132 
133 /++
134 Create a Ref from a pointer without incrementing refcount.
135 +/
136 package(godot) RefOrT!T refOrT(T)(T instance) {
137     static if (extends!(T, RefCounted)) {
138         Ref!T ret = void;
139         ret._self = instance;
140         return ret;
141     } else
142         return instance;
143 }
144 
145 /++
146 Create a Ref from a pointer and increment refcount.
147 +/
148 package(godot) RefOrT!T refOrTInc(T)(T instance) {
149     static if (extends!(T, RefCounted)) {
150         Ref!T ret = void;
151         ret._self = instance;
152         if (ret._self)
153             ret._reference.reference();
154         return ret;
155     } else
156         return instance;
157 }