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