1 module api.methods;
2 
3 import godotutil..string;
4 import api.classes, api.enums, api.util;
5 
6 import asdf;
7 
8 import std.range;
9 import std.algorithm.searching, std.algorithm.iteration;
10 import std.path;
11 import std.conv : text;
12 import std..string;
13 
14 
15 
16 
17 
18 class GodotMethod
19 {
20 	string name;
21 	Type return_type;
22 	bool is_editor;
23 	bool is_noscript;
24 	bool is_const;
25 	bool is_virtual;
26 	bool has_varargs;
27 	bool is_from_script;
28 	GodotArgument[] arguments;
29 	
30 	void finalizeDeserialization(Asdf data)
31 	{
32 		foreach(i, ref a; arguments)
33 		{
34 			a.index = i;
35 			a.parent = this;
36 		}
37 	}
38 	
39 	@serializationIgnore:
40 	GodotClass parent;
41 	
42 	string ddoc;
43 	
44 	bool same(in GodotMethod other) const
45 	{
46 		return name == other.name && is_const == other.is_const;
47 	}
48 	
49 	string templateArgsString() const
50 	{
51 		string ret = "";
52 		bool first = true; // track first arg to skip comma
53 		foreach(i, ref a; arguments)
54 		{
55 			if(a.type.acceptImplicit)
56 			{
57 				if(first) first = false;
58 				else ret ~= ", ";
59 				ret ~= text(a.type.godot, "Arg", i);
60 			}
61 		}
62 		if(has_varargs)
63 		{
64 			if(!first) ret ~= ", ";
65 			ret ~= "VarArgs...";
66 		}
67 		// template parens only if it actually is a template
68 		if(ret.length != 0) ret = text("(", ret, ")");
69 		return ret;
70 	}
71 	
72 	string argsString() const
73 	{
74 		string ret = "(";
75 		bool hasDefault = false;
76 		foreach(i, ref a; arguments)
77 		{
78 			if(i != 0) ret ~= ", ";
79 			if(a.type.acceptImplicit) ret ~= text(a.type.dCallParamPrefix,
80 				a.type.godot, "Arg", i);
81 			else ret ~= text(a.type.dCallParamPrefix, a.type.d);
82 			
83 			ret ~= " " ~ a.name.escapeD;
84 			if(a.has_default_value || hasDefault)
85 			{
86 				ret ~= " = " ~ escapeDefault(a.type, a.default_value);
87 			}
88 		}
89 		if(has_varargs)
90 		{
91 			if(arguments.length != 0) ret ~= ", ";
92 			ret ~= "VarArgs varArgs";
93 		}
94 		ret ~= ")";
95 		return ret;
96 	}
97 	
98 	string binding() const
99 	{
100 		string ret;
101 		
102 		ret ~= "\t\t@GodotName(\""~name~"\") GodotMethod!("~return_type.d;
103 		foreach(ai, const a; arguments)
104 		{
105 			ret ~= ", " ~ a.type.d;
106 		}
107 		if(has_varargs) ret ~= ", GodotVarArgs";
108 		ret ~= ") " ~ name.snakeToCamel.escapeD ~ ";\n";
109 		
110 		return ret;
111 	}
112 	
113 	string source() const
114 	{
115 		string ret;
116 		
117 		ret ~= "\t/**\n\t"~ddoc.replace("\n", "\n\t")~"\n\t*/\n";
118 		ret ~= "\t";
119 		ret ~= return_type.dRef~" ";
120 		// none of the types (Classes/Core/Primitive) are pointers in D
121 		// Classes are reference types; the others are passed by value.
122 		ret ~= name.snakeToCamel.escapeD;
123 		
124 		ret ~= templateArgsString;
125 		ret ~= argsString;
126 		
127 		if(is_const) ret ~= " const";
128 		else if(name == "callv" && parent.name.godot == "Object") ret ~= " const"; /// HACK
129 		ret ~= "\n\t{\n";
130 		
131 		// implementation
132 		if(is_virtual || has_varargs)
133 		{
134 			ret ~= "\t\tArray _GODOT_args = Array.make();\n";
135 			foreach(const a; arguments)
136 			{
137 				ret ~= "\t\t_GODOT_args.append("~escapeD(a.name)~");\n";
138 			}
139 			
140 			if(has_varargs)
141 			{
142 				ret ~= "\t\tforeach(vai, VA; VarArgs)\n\t\t{\n";
143 				// TODO: check that VA can convert to Variant
144 				ret ~= "\t\t\t_GODOT_args.append(varArgs[vai]);\n";
145 				ret ~= "\t\t}\n";
146 			}
147 			
148 			ret ~= "\t\tString _GODOT_method_name = String(\""~name~"\");\n";
149 			
150 			ret ~= "\t\t";
151 			if(return_type.d != "void") ret ~= "return ";
152 			ret ~= "this.callv(_GODOT_method_name, _GODOT_args)";
153 			if(return_type.d != "void" && return_type.d != "Variant")
154 				ret ~= ".as!(RefOrT!"~return_type.d~")";
155 			ret ~= ";\n";
156 		} // end varargs/virtual impl
157 		else
158 		{
159 			ret ~= "\t\tcheckClassBinding!(typeof(this))();\n";
160 			/// ".bind(\"" parent.name.godot ~ "\", \"" ~ name ~ "\");\n";
161 			ret ~= "\t\t";
162 			if(return_type.d != "void") ret ~= "return ";
163 			ret ~= "ptrcall!(" ~ return_type.d ~ ")(GDNativeClassBinding."
164 				~ name.snakeToCamel.escapeD ~ ", _godot_object";
165 			foreach(ai, const a; arguments)
166 			{
167 				ret ~= ", "~a.name.escapeD;
168 			}
169 			ret ~= ");\n";
170 		} // end normal method impl
171 		
172 		ret ~= "\t}\n";
173 		
174 		return ret;
175 	}
176 }
177 
178 struct GodotArgument
179 {
180 	string name;
181 	Type type;
182 	bool has_default_value;
183 	string default_value;
184 	
185 	@serializationIgnore:
186 	
187 	size_t index;
188 	GodotMethod parent;
189 }
190 
191 class GodotProperty
192 {
193 	string name;
194 	Type type;
195 	string getter, setter;
196 	int index;
197 	
198 	@serializationIgnore:
199 	
200 	string ddoc;
201 	
202 	string getterSource(in GodotMethod m) const
203 	{
204 		string ret;
205 		ret ~= "\t/**\n\t" ~ ddoc.replace("\n", "\n\t") ~ "\n\t*/\n";
206 		ret ~= "\t@property " ~ m.return_type.d ~ " " ~ name.replace("/","_").snakeToCamel.escapeD ~ "()\n\t{\n"; /// TODO: const?
207 		ret ~= "\t\treturn " ~ getter.snakeToCamel.escapeD ~ "(";
208 		if(index != -1) ret ~= text(index);
209 		ret ~= ");\n";
210 		ret ~= "\t}\n";
211 		return ret;
212 	}
213 	string setterSource(in GodotMethod m) const
214 	{
215 		string ret;
216 		ret ~= "\t/// ditto\n";
217 		ret ~= "\t@property void " ~ name.replace("/","_").snakeToCamel.escapeD ~ "(" ~ m.arguments[$-1].type.d ~ " v)\n\t{\n";
218 		ret ~= "\t\t" ~ setter.snakeToCamel.escapeD ~ "(";
219 		if(index != -1) ret ~= text(index) ~ ", ";
220 		ret ~= "v);\n";
221 		ret ~= "\t}\n";
222 		return ret;
223 	}
224 }
225 
226