1 module serializer;
2 
3 import sdlang;
4 import editor;
5 import types;
6 import pixelperfectengine.graphics.common;
7 import pixelperfectengine.concrete.elements;
8 import std.utf;
9 import std.stdio;
10 import conv = std.conv;
11 
12 public class WindowSerializer {
13 	Tag root;
14 	string filename;
15 	public this(){
16 		root = new Tag(null, null);
17 		Tag window = new Tag(root, null, "Window", [Value("window")]);
18 		new Tag(window, null, "title", [Value("New Window")]);
19 		new Tag(window, "size", "x", [Value(640)]);
20 		new Tag(window, "size", "y", [Value(480)]);
21 		new Tag(window, null, "extraButtons");
22 	}
23 	public this(string filename) {
24 		this.filename = filename;
25 	}
26 	public void store(string filename) {
27 		this.filename = filename;
28 		store();
29 	}
30 	public @property @nogc string getFilename() {
31 		return filename;
32 	}
33 	public void store() {
34 		import std.string : toStringz;
35 		string fileout = root.toSDLDocument("\t",1);
36 		debug writeln(fileout);
37 		File filestream = File(filename, "w");
38 		//filestream.open();
39 		filestream.write(fileout);
40 		filestream.close();
41 	}
42 	private Coordinate parseCoordinate(Tag t){
43 		return Coordinate(t.values[0].get!int,t.values[1].get!int,t.values[2].get!int,t.values[3].get!int);
44 	}
45 	private string parseCoordinateIntoString(Tag t){
46 		return conv.to!string(t.values[0].get!int) ~ ", " ~ conv.to!string(t.values[1].get!int) ~ ", " ~
47 				conv.to!string(t.values[2].get!int) ~ ", " ~ conv.to!string(t.values[3].get!int);
48 	}
49 	public void deserialize(DummyWindow dw, Editor e) {
50 		root = parseFile(filename);
51 		foreach(t0; root.all.tags){
52 			string name = t0.expectValue!string(), type;
53 			WindowElement we;
54 			switch(t0.getFullName.toString){
55 				case "Label":
56 					we = new Label(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
57 							parseCoordinate(t0.expectTag("position")));
58 					dw.addElement(we);
59 					type = "Label";
60 					break;
61 				case "Button":
62 					we = new Button(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
63 							parseCoordinate(t0.expectTag("position")));
64 					dw.addElement(we);
65 					type = "Button";
66 					break;
67 				case "SmallButton":
68 					break;
69 				case "TextBox":
70 					we = new TextBox(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
71 							parseCoordinate(t0.expectTag("position")));
72 					dw.addElement(we);
73 					type = "TextBox";
74 					break;
75 				case "SmallCheckBox":
76 					break;
77 				case "CheckBox":
78 					we = new CheckBox(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
79 							parseCoordinate(t0.expectTag("position")));
80 					dw.addElement(we);
81 					type = "CheckBox";
82 					break;
83 				case "SmallRadioButton":
84 					break;
85 				case "RadioButton":
86 					we = new RadioButton(toUTF32(t0.expectTagValue!string("text")), t0.expectTagValue!string("source"),
87 							parseCoordinate(t0.expectTag("position")));
88 					dw.addElement(we);
89 					type = "RadioButton";
90 					break;
91 				case "ListView":
92 					int[] columnWidths;
93 					dstring[] columnTexts;
94 					const int headerHeight = t0.expectTag("header").expectValue!int();
95 					foreach(t1; t0.expectTag("header").tags){
96 						columnTexts ~= toUTF32(t1.values[0].get!string);
97 						columnWidths ~= t1.values[1].get!int;
98 					}
99 					we = new ListView(new ListViewHeader(headerHeight, columnWidths, columnTexts), [], 
100 							t0.expectTagValue!string("source"), parseCoordinate(t0.expectTag("position")));
101 					dw.addElement(we);
102 					type = "ListView";
103 					break;
104 				case "Window":
105 					dw.setTitle(toUTF32(t0.expectTagValue!string("title")));
106 					dw.setSize(t0.expectTagValue!int("size:x"),t0.expectTagValue!int("size:y"));
107 					type = "Window";
108 					break;
109 				case "HorizScrollBar":
110 					we = new HorizScrollBar(t0.expectTagValue!int("maxValue"), t0.expectTagValue!string("source"),
111 							parseCoordinate(t0.expectTag("position")));
112 					dw.addElement(we);
113 					type = "HorizScrollBar";
114 					break;
115 				case "VertScrollBar":
116 					we = new VertScrollBar(t0.expectTagValue!int("maxValue"), t0.expectTagValue!string("source"),
117 							parseCoordinate(t0.expectTag("position")));
118 					dw.addElement(we);
119 					type = "VertScrollBar";
120 					break;
121 				default:
122 					break;
123 			}
124 			if (type != "Window") {
125 				e.elements[name] = ElementInfo(we, name, type);
126 				//e.elementTypes[name] = type;
127 			}
128 		}
129 		e.updateElementList;
130 	}
131 	public void generateDCode(string outputFile){
132 		string outputCode = "import pixelperfectengine.concrete.window; \n\n", windowCtor, elementCtors, typeDefs;
133 		foreach(t0; root.all.tags){
134 			string typeName = t0.name;
135 			switch (typeName) {
136 				case "Window": break;
137 				case "SmallRadioButton":
138 					elementCtors ~= "\t\t" ~ t0.getValue!string() ~ " = ";
139 					typeDefs ~= "\t" ~ "RadioButton" ~ " " ~ t0.getValue!string() ~ ";\n";
140 					break;
141 				case "SmallCheckBox":
142 					elementCtors ~= "\t\t" ~ t0.getValue!string() ~ " = ";
143 					typeDefs ~= "\t" ~ "CheckBox" ~ " " ~ t0.getValue!string() ~ ";\n";
144 					break;
145 				default:
146 					elementCtors ~= "\t\t" ~ t0.getValue!string() ~ " = ";
147 					typeDefs ~= "\t" ~ typeName ~ " " ~ t0.getValue!string() ~ ";\n";
148 					break;
149 			}
150 			switch(typeName){
151 				case "Button", "Label", "TextBox", "CheckBox", "RadioButton":
152 					elementCtors ~= "new " ~ typeName ~ "(\"" ~ t0.getTagValue!string("text") ~ "\"d, \"" ~
153 							t0.getTagValue!string("source") ~ "\", Box(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "));\n";
154 					break;
155 				case "HorizScrollBar", "VertScrollBar":
156 					elementCtors ~= "new " ~ typeName ~ "(\"" ~ conv.to!string(t0.getTagValue!int("maxValue")) ~ ", \"" ~ 
157 							t0.getTagValue!string("source") ~ "\", Box(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "));\n";
158 					break;
159 				case "ListView":
160 					elementCtors ~= "new ListView(new ListViewHeader(" ~ conv.to!string(t0.getTagValue!int("header")) ~ ", ";
161 					string intArr = "[", strArr = "[";
162 					foreach (t1 ; t0.expectTag("header").tags) {
163 						intArr ~= conv.to!string(t1.getValue!int()) ~ " ,";
164 						strArr ~= "\"" ~ t1.getValue!string() ~ "\" ,";
165 					}
166 					intArr = intArr[0..$-2] ~ "]";
167 					strArr = strArr[0..$-2] ~ "]";
168 					elementCtors ~= intArr ~ ", " ~ strArr ~ "), null, \"" ~ 
169 							t0.getTagValue!string("source") ~ "\", Box(" ~ parseCoordinateIntoString(t0.getTag("position")) ~ "));\n";
170 					break;
171 				case "Window":
172 					outputCode ~= "public class " ~ t0.getValue!string() ~ " : Window {\n";
173 					//string extraButtons;
174 					/+Tag t1 = t0.getTag("extraButtons", null);
175 					if(t1 !is null){
176 						if(t1.values.length){
177 							extraButtons = ", [";
178 							foreach(Value v; t1.values){
179 								extraButtons ~= v.get!string() ~ ", ";
180 							}
181 							extraButtons.length -= 2;
182 							extraButtons = "]";
183 						}
184 					}+/
185 					/+windowCtor = "super(\"" ~ t0.getTagValue!string("title") ~ "\"d, Box(0, 0, " ~
186 							conv.to!string(t0.getTagValue!int("size:x")) ~ ", " ~ conv.to!string(t0.getTagValue!int("size:y")) ~ " )" ~
187 							extraButtons ~ ");\n";+/
188 					windowCtor = "super(Box(0, 0, " ~ conv.to!string(t0.getTagValue!int("size:x")) ~ ", " ~ 
189 							conv.to!string(t0.getTagValue!int("size:y")) ~ "), \"" ~ t0.getTagValue!string("title") ~ "\");\n";
190 					break;
191 				default:
192 					break;
193 			}
194 		}
195 		outputCode ~= typeDefs ~ "\tpublic this(){\n\t\t" ~ windowCtor ~ elementCtors ~ "\t}\n}\n";
196 		debug writeln(outputCode);
197 		File filestream = File(outputFile, "w");
198 		//filestream.open();
199 		filestream.write(outputCode);
200 		filestream.close();
201 	}
202 	/**
203 	 * Returns a complete tag for editing a tree, etc.
204 	 */
205 	public Tag getTag(string target, string property){
206 		foreach(t0; root.all.tags){
207 			if(t0.getValue!string() == target){
208 				return t0.getTag(property);
209 			}
210 		}
211 		return null;
212 	}
213 	/** 
214 	 * Replaces an existing tag with a new one.
215 	 * Params:
216 	 *   target = The name of the window element, which tag must be replaced.
217 	 *   property = The name of the property Tag.
218 	 *   newTag = The new tag to be used in the place of the old
219 	 * Returns: The old tag as a backup.
220 	 */
221 	public Tag replaceTag(string target, string property, Tag newTag) {
222 		foreach(Tag t0; root.all.tags){
223 			if(t0.getValue!string() == target){
224 				Tag oldTag = t0.getTag(property);
225 				oldTag.remove();
226 				t0.add(newTag);
227 				return oldTag;
228 			}
229 		}
230 		return null;
231 	}
232 	/**
233 	 * Edits the value of an element.
234 	 * For MenuBar PopUpMenu trees, use the getTag function instead.
235 	 */
236 	public Value[] editValue(string target, string property, Value[] val){
237 		Value[] result;
238 		foreach(t0; root.all.tags){
239 			if(t0.getValue!string() == target){
240 				result = t0.getTagValues(property);
241 				t0.getTag(property).values = val;
242 				return result;
243 			}
244 		}
245 		return result;
246 	}
247 	public Value[] getValue(string target, string property){
248 		foreach(t0; root.all.tags){
249 			if(t0.getValue!string() == target){
250 				return t0.getTagValues(property);
251 			}
252 		}
253 		return null;
254 	}
255 	public string renameWindow(string name){
256 		string oldname = root.getTag("Window").getValue!string();
257 		root.getTag("Window").values[0] = Value(name);
258 		return oldname;
259 	}
260 	public string getWindowName(){
261 		return root.getTag("Window").getValue!string();
262 	}
263 	public Value[] editWindowValue(string property, Value[] val){
264 		Value[] result = root.getTag("Window").getTag(property).values;
265 		root.getTag("Window").getTag(property).values = val;
266 		return result;
267 	}
268 	public Value[] getWindowValue(string property){
269 		return root.getTag("Window").getTag(property).values;
270 	}
271 	public void renameElement(string oldName, string newName){
272 		foreach(t; root.tags){
273 			if(t.getValue!string() == oldName){
274 				t.values[0] = Value(newName);
275 				return;
276 			}
277 		}
278 	}
279 	public void addElement(string type, string name, Coordinate initPos){
280 		foreach(t; root.tags){
281 			if(t.getValue!string() == name)
282 				throw new ElementCollisionException("Similarly named element already exists!");
283 		}
284 		Tag t1 = new Tag(root, null, type, [Value(name)]);
285 		switch(type){
286 			case "Label", "TextBox", "RadioButton", "CheckBox":
287 				new Tag(t1, null, "text", [Value(name)]);
288 				break;
289 			case "Button":
290 				new Tag(t1, null, "icon", [Value("null")]);
291 				goto case "Label";
292 			case "ListView":
293 				Tag t2 = new Tag(t1, null, "header", [Value(16)]);
294 				new Tag(t2, null, null, [Value("col0"), Value(40)]);
295 				new Tag(t2, null, null, [Value("col1"), Value(40)]);
296 				break;
297 			case "HorizScrollBar", "VertScrollBar":
298 				//new Tag(t1, null, "barLength", [Value(1)]);
299 				new Tag(t1, null, "maxValue", [Value(16)]);
300 				break;
301 			case "MenuBar":
302 				Tag t2 = new Tag(t1, null, "options");
303 				Tag t3 = new Tag(t2, null, null, [Value("opt0")]);
304 				new Tag(t3, null, null, [Value("opt0_0")]);
305 				new Tag(t3, null, null, [Value("opt0_1")]);
306 				Tag t4 = new Tag(t2, null, null, [Value("opt1")]);
307 				new Tag(t4, null, null, [Value("opt1_0")]);
308 				new Tag(t4, null, null, [Value("opt1_1")]);
309 				break;
310 			default:
311 				break;
312 		}
313 		new Tag(t1, null, "source", [Value(name)]);
314 		new Tag(t1, null, "position", [Value(initPos.left), Value(initPos.top), Value(initPos.right), Value(initPos.bottom)]);
315 		//debug writeln(t1);
316 	}
317 	public void addElement(Tag tag){
318 		foreach(t; root.tags){
319 			if(t.values[0].get!string() == tag.values[0].get!string())
320 				throw new ElementCollisionException("Similarly named element already exists!");
321 		}
322 		root.add(tag);
323 	}
324 	public Tag removeElement(string name){
325 		foreach(t; root.tags){
326 			if(t.values[0].get!string() == name){
327 				t.remove;
328 				return t;
329 			}
330 		}
331 		return null;
332 	}
333 }
334 class ElementCollisionException : Exception{
335 	@nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
336     {
337         super(msg, file, line, nextInChain);
338     }
339 
340     @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
341     {
342         super(msg, file, line, nextInChain);
343     }
344 }