1 module editorevents;
2 
3 import document;
4 
5 public import PixelPerfectEngine.concrete.eventChainSystem;
6 public import PixelPerfectEngine.graphics.layers;
7 public import PixelPerfectEngine.map.mapformat;
8 
9 import PixelPerfectEngine.system.file;
10 
11 import std.stdio;
12 import std.conv : to;
13 import sdlang;
14 
15 public class WriteToMapVoidFill : UndoableEvent {
16 	ITileLayer target;
17 	Coordinate area;
18 	MappingElement me;
19 	MappingElement[] original;
20 	public this(ITileLayer target, Coordinate area, MappingElement me){
21 		this.target = target;
22 		this.area = area;
23 		this.me = me;
24 		//mask.length = area.area;
25 	}
26 	public void redo() {
27 		for(int y = area.top ; y <= area.bottom ; y++){
28 			for(int x = area.left ; x <= area.right ; x++){
29 				MappingElement o = target.readMapping(x,y);
30 				original ~= o;
31 				if (o.tileID == 0xFFFF)
32 					target.writeMapping(x,y,me);
33 			}
34 		}
35 	}
36 	public void undo() {
37 		size_t pos;
38 		for(int y = area.top ; y <= area.bottom ; y++){
39 			for(int x = area.left ; x <= area.right ; x++){
40 				target.writeMapping(x,y,original[pos]);
41 				pos++;
42 			}
43 		}
44 	}
45 }
46 
47 public class WriteToMapOverwrite : UndoableEvent {
48 	ITileLayer target;
49 	Coordinate area;
50 	MappingElement me;
51 	MappingElement[] original;
52 	public this(ITileLayer target, Coordinate area, MappingElement me){
53 		this.target = target;
54 		this.area = area;
55 		this.me = me;
56 		//original.length = (area.width + 1) * (area.height + 1);
57 	}
58 	public void redo() {
59 		for(int y = area.top ; y <= area.bottom ; y++){
60 			for(int x = area.left ; x <= area.right ; x++){
61 				original ~= target.readMapping(x,y);
62 				target.writeMapping(x,y,me);
63 			}
64 		}
65 	}
66 	public void undo() {
67 		size_t pos;
68 		for(int y = area.top ; y <= area.bottom ; y++){
69 			for(int x = area.left ; x <= area.right ; x++){
70 				target.writeMapping(x,y,original[pos]);
71 				pos++;
72 			}
73 		}
74 	}
75 }
76 
77 public class WriteToMapSingle : UndoableEvent {
78 	ITileLayer target;
79 	int x;
80 	int y;
81 	MappingElement me;
82 	MappingElement original;
83 	public this(ITileLayer target, int x, int y, MappingElement me) {
84 		this.target = target;
85 		this.x = x;
86 		this.y = y;
87 		this.me = me;
88 	}
89 	public void redo() {
90 		original = target.readMapping(x,y);
91 		target.writeMapping(x,y,me);
92 		/*debug {
93 			import std.stdio : writeln;
94 			writeln("Layer was written at position ", x, ";", y," with values ", target.readMapping(x,y));
95 		}*/
96 	}
97 	public void undo() {
98 		target.writeMapping(x,y,original);
99 	}
100 }
101 
102 public class CreateTileLayerEvent : UndoableEvent {
103 	TileLayer creation;
104 	MapDocument target;
105 	int tX;
106 	int tY;
107 	int mX;
108 	int mY;
109 	int pri;
110 	string name;
111 	string file;
112 	bool embed;
113 	Tag backup;
114 
115 	public this(MapDocument target, int tX, int tY, int mX, int mY, dstring name, string file, bool embed) {
116 		import std.utf : toUTF8;
117 		creation = new TileLayer(tX, tY);
118 		creation.setRenderingMode(RenderingMode.Copy);
119 		this.target = target;
120 		//this.md = md;
121 		this.tX = tX;
122 		this.tY = tY;
123 		this.mX = mX;
124 		this.mY = mY;
125 		this.name = toUTF8(name);
126 		this.file = file;
127 		this.embed = embed;
128 		//this.imageReturnFunc = imageReturnFunc;
129 	}
130 	public void redo() {
131 		import std.file : exists, isFile;
132 		import std.path : baseName;
133 		import std.utf : toUTF8;
134 		import PixelPerfectEngine.system.etc : intToHex;
135 		if (backup) {	//If a backup exists, then re-add that to the document, then return.
136 			target.mainDoc.addNewLayer(pri, backup, creation);
137 			target.outputWindow.addLayer(pri);
138 			return;
139 		}
140 		try {
141 			const int nextLayer = target.nextLayerNumber;
142 
143 			//handle the following instances for mapping:
144 			//file == null AND embed
145 			//file == existing file AND embed
146 			//file == existing file AND !embed
147 			//file == nonexisting file
148 			if ((!exists(file) || !isFile(file)) && embed) {	//create new instance for the map by embedding data into the SDLang file
149 				//selDoc.mainDoc.tld[nextLayer] = new
150 				MappingElement[] me;
151 				me.length = mX * mY;
152 				creation.loadMapping(mX, mY, me);
153 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
154 				target.mainDoc.addEmbeddedMapData(nextLayer, me);
155 			} else if (!exists(file)) {	//Create empty file
156 				File f = File(file, "wb");
157 				MappingElement[] me;
158 				me.length = mX * mY;
159 				creation.loadMapping(mX, mY, me);
160 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
161 				saveMapFile(MapDataHeader(mX, mY), me, f);
162 				target.mainDoc.addMapDataFile(nextLayer, file);
163 			} else {	//load mapping, embed data into current file if needed
164 				MapDataHeader mdh;
165 				MappingElement[] me = loadMapFile(File(file), mdh);
166 				creation.loadMapping(mdh.sizeX, mdh.sizeY, me);
167 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
168 				if (embed)
169 					target.mainDoc.addEmbeddedMapData(nextLayer, me);
170 				else
171 					target.mainDoc.addMapDataFile(nextLayer, file);
172 			}
173 
174 			//handle the following instances for materials:
175 			//res == image file
176 			//TODO: check if material resource file has any embedded resource data
177 			//TODO: enable importing from SDLang map files (*.xmf)
178 			//TODO: generate dummy tiles for nonexistent material
179 			/+if (exists(res)) {
180 				//load the resource file and test if it's the correct size (through an exception)
181 				source = loadImage(File(res));
182 				ABitmap[] tilesheet;
183 				switch (source.getBitdepth()) {
184 					case 4:
185 						Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY);
186 						foreach(p; output)
187 							tilesheet ~= p;
188 						break;
189 					case 8:
190 						Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY);
191 						foreach(p; output)
192 							tilesheet ~= p;
193 						break;
194 					case 16:
195 						Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY);
196 						foreach(p; output)
197 							tilesheet ~= p;
198 						break;
199 					case 32:
200 						Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY);
201 						foreach(p; output)
202 							tilesheet ~= p;
203 						break;
204 					default:
205 						throw new Exception("Unsupported bitdepth!");
206 
207 				}
208 				if (tilesheet.length == 0) throw new Exception("No tiles were imported!");
209 				target.addTileSet(nextLayer, tilesheet);
210 				target.mainDoc.addsourceFile(nextLayer, res);
211 				{
212 					TileInfo[] idList;
213 					string nameBase = baseName(res);
214 					for (int id ; id < tilesheet.length ; id++) {
215 						idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4));
216 						//writeln(idList);
217 					}
218 					target.mainDoc.addTileInfo(nextLayer, idList, res);
219 				}
220 				if (source.isIndexed) {
221 					Color[] palette;
222 					/*foreach (color ; source.palette) {
223 						palette ~= Color(color.a, color.r, color.g, color.b);
224 						debug writeln(color);
225 					}*/
226 					auto sourcePalette = source.palette;
227 					palette.reserve(sourcePalette.length);
228 					for (ushort i ; i < sourcePalette.length ; i++){
229 						const auto origC = sourcePalette[i];
230 						const Color c = Color(origC.a, origC.r, origC.g, origC.b);
231 						palette ~= c;
232 					}
233 					target.mainDoc.addPaletteFile(res, "", cast(int)target.outputWindow.palette.length);
234 					target.outputWindow.palette = target.outputWindow.palette ~ palette;
235 					//debug writeln(target.outputWindow.palette);
236 				}
237 
238 			}+/
239 			target.outputWindow.addLayer(nextLayer);
240 			target.selectedLayer = nextLayer;
241 			pri = nextLayer;
242 			target.updateLayerList();
243 			target.updateMaterialList();
244 		} catch (Exception e) {
245 			writeln(e);
246 		}
247 	}
248 	public void undo() {
249 		//Just remove the added layer from the layerlists
250 		target.outputWindow.removeLayer(pri);
251 		backup = target.mainDoc.removeLayer(pri);
252 		target.updateLayerList();
253 		target.updateMaterialList();
254 	}
255 }
256 public class ResizeTileMapEvent : UndoableEvent {
257 	MappingElement[] backup, destMap;
258 	int mX, mY, offsetX, offsetY, newX, newY;
259 	MapDocument targetDoc;
260 	int layer;
261 	bool patternRepeat;
262 	this(int[6] params, MapDocument targetDoc, int layer, bool patternRepeat) {
263 		mX = params[0];
264 		mY = params[1];
265 		offsetX = params[2];
266 		offsetY = params[3];
267 		newX = params[4];
268 		newY = params[5];
269 		this.targetDoc = targetDoc;
270 		this.layer = layer;
271 		this.patternRepeat = patternRepeat;
272 	}
273 	public void redo() {
274 		//backup current layer data
275 		ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
276 		backup = targetLayer.getMapping();
277 		destMap.length = newX * newY;
278 		//writeln(destMap.length);
279 		if(patternRepeat) {
280 			int sX = offsetX % mX, sY = offsetY % mY;
281 			for (int iY ; iY < newY ; iY++) {
282 				for (int iX ; iX < newX ; iX++) {
283 					destMap[iX + (iY * newX)] = backup[sX + (sY * mX)];
284 					sX++;
285 					if (sX >= mX) sX = 0;
286 				}
287 				sY++;
288 				if (sY >= mY) sY = 0;
289 			}
290 		} else {
291 			for (int iY ; iY < mY ; iY++) {
292 				//Do a boundscheck, if falls outside of it do nothing. If inside, copy.
293 				if (iY + offsetY < newY && iY + offsetY >= 0) {
294 					for (int iX ; iX < mX ; iX++) {
295 						//Do a boundscheck, if falls outside of it do nothing. If inside, copy.
296 						if (iX + offsetX < newX && iX + offsetX >= 0) {
297 							destMap[iX + offsetX + ((iY + offsetY) * newY)] = backup[iX + (iY * mY)];
298 						} else if (iX + offsetX >= newX) break;
299 					}
300 				} else if (iY + offsetY >= newY) break;
301 			}
302 		}
303 		targetLayer.loadMapping(newX, newY, destMap);
304 		targetDoc.mainDoc.alterTileLayerInfo(layer, 4, newX);
305 		targetDoc.mainDoc.alterTileLayerInfo(layer, 5, newY);
306 	}
307 	public void undo() {
308 		ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
309 		targetLayer.loadMapping(mX, mY, backup);
310 		targetDoc.mainDoc.alterTileLayerInfo(layer, 4, mX);
311 		targetDoc.mainDoc.alterTileLayerInfo(layer, 5, mY);
312 	}
313 }
314 public class AddTileSheetEvent : UndoableEvent {
315 	Image source;
316 	MapDocument targetDoc;
317 	int layer;
318 	int paletteOffset;
319 	int paletteShift;
320 	string preName, afterName, fileName;
321 	int numFrom;
322 	uint numStyle;		///0: decimal, 1: hex, 2: octal
323 	Tag backup;
324 	this(Image source, MapDocument targetDoc, int layer, int paletteOffset, int paletteShift, string[3] name, int numFrom, 
325 			uint numStyle) {
326 		this.source = source;
327 		this.targetDoc = targetDoc;
328 		this.layer = layer;
329 		this.paletteOffset = paletteOffset;
330 		this.paletteShift = paletteShift;
331 		preName = name[0];
332 		afterName = name[1];
333 		fileName = name[2];
334 		this.numFrom = numFrom;
335 		this.numStyle = numStyle;
336 	}
337 	public void redo() {
338 		import PixelPerfectEngine.system.etc : intToHex, intToOct;
339 		//Copy palette if exists (-1 means no palette or do not import palette)
340 		if (paletteOffset >= 0) {
341 			//Color[] targetPalette = targetDoc.outputWindow.paletteLocal;
342 			Color[] importedPalette = loadPaletteFromImage(source);
343 			assert(importedPalette.length);
344 			const ushort offset = cast(ushort)(paletteOffset << paletteShift);
345 			targetDoc.outputWindow.loadPaletteChunk(importedPalette, offset);
346 			targetDoc.mainDoc.addPaletteFile(fileName, "", paletteOffset << paletteShift, paletteShift);
347 		}
348 		if (backup is null) {
349 			//TODO: check if material resource file has any embedded resource data
350 			//TODO: enable importing from SDLang map files (*.xmf)
351 			//TODO: generate dummy tiles for nonexistent material
352 			
353 			//load the resource file and test if it's the correct size (through an exception)
354 			//source = loadImage(File(res));
355 			ITileLayer itl = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer];
356 			const int tX = itl.getTileWidth, tY = itl.getTileHeight;
357 			ABitmap[] tilesheet;
358 			switch (source.getBitdepth()) {
359 				case 4:
360 					Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY);
361 					foreach(p; output)
362 						tilesheet ~= p;
363 					break;
364 				case 8:
365 					Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY);
366 					foreach(p; output)
367 						tilesheet ~= p;
368 					break;
369 				case 16:
370 					Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY);
371 					foreach(p; output)
372 						tilesheet ~= p;
373 					break;
374 				case 32:
375 					Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY);
376 					foreach(p; output)
377 						tilesheet ~= p;
378 					break;
379 				default:
380 					throw new Exception("Unsupported bitdepth!");
381 			}
382 			if (tilesheet.length == 0) throw new Exception("No tiles were imported!");
383 			targetDoc.addTileSet(layer, tilesheet);
384 			targetDoc.mainDoc.addTileSourceFile(layer, fileName, null, 0);
385 			{
386 				TileInfo[] idList;
387 				for (int id ; id < tilesheet.length ; id++) {
388 					//idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4));
389 					string tilename = preName;
390 					switch(numStyle & 0x3) {
391 						case 1:
392 							tilename ~= intToHex(id + numFrom, numStyle>>>8);
393 							break;
394 						case 2:
395 							tilename ~= intToOct(id + numFrom, numStyle>>>8);
396 							break;
397 						default:
398 							string num = to!string(id);
399 							for (int i ; i < (numStyle>>>8) - num.length ; i++) {
400 								tilename ~= '0';
401 							}
402 							tilename ~= num;
403 							break;
404 					}
405 					tilename ~= afterName;
406 					idList ~= TileInfo(cast(wchar)id, cast(ushort)paletteShift, id, tilename);
407 					//writeln(idList);
408 				}
409 				targetDoc.mainDoc.addTileInfo(layer, idList, fileName, null);
410 			}
411 			/+if (source.isIndexed) {
412 				Color[] palette;
413 				/*foreach (color ; source.palette) {
414 					palette ~= Color(color.a, color.r, color.g, color.b);
415 					debug writeln(color);
416 				}*/
417 				auto sourcePalette = source.palette;
418 				palette.reserve(sourcePalette.length);
419 				for (ushort i ; i < sourcePalette.length ; i++){
420 					const auto origC = sourcePalette[i];
421 					const Color c = Color(origC.a, origC.r, origC.g, origC.b);
422 					palette ~= c;
423 				}
424 				target.mainDoc.addPaletteFile(res, "", cast(int)target.outputWindow.palette.length);
425 				target.outputWindow.palette = target.outputWindow.palette ~ palette;
426 				//debug writeln(target.outputWindow.palette);
427 			}+/
428 
429 			
430 		} else {
431 			
432 		}
433 	}
434 	public void undo() {
435 
436 	}
437 }