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