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 	ubyte[] mask;
20 	public this(ITileLayer target, Coordinate area, MappingElement me){
21 		this.target = target;
22 		this.area = area;
23 		this.me = me;
24 	}
25 	public void redo() {
26 		for(int y = area.top ; y < area.bottom ; y++){
27 			for(int x = area.left ; x < area.right ; x++){
28 				if(target.readMapping(x,y).tileID != 0xFFFF){
29 					mask[area.width * y + x] = 0xFF;
30 					target.writeMapping(x,y,me);
31 				}
32 			}
33 		}
34 	}
35 	public void undo() {
36 		for(int y = area.top ; y < area.bottom ; y++){
37 			for(int x = area.left ; x < area.right ; x++){
38 				if(mask[area.width * y + x] == 0xFF){
39 					target.writeMapping(x,y,MappingElement(0xFFFF));
40 				}
41 			}
42 		}
43 	}
44 }
45 
46 public class WriteToMapOverwrite : UndoableEvent {
47 	ITileLayer target;
48 	Coordinate area;
49 	MappingElement me;
50 	MappingElement[] original;
51 	public this(ITileLayer target, Coordinate area, MappingElement me){
52 		this.target = target;
53 		this.area = area;
54 		this.me = me;
55 		original.length = area.area;
56 	}
57 	public void redo() {
58 		size_t pos;
59 		for(int y = area.top ; y < area.bottom ; y++){
60 			for(int x = area.left ; x < area.right ; x++){
61 				original[pos] = target.readMapping(x,y);
62 				target.writeMapping(x,y,me);
63 				pos++;
64 			}
65 		}
66 	}
67 	public void undo() {
68 		size_t pos;
69 		for(int y = area.top ; y < area.bottom ; y++){
70 			for(int x = area.left ; x < area.right ; x++){
71 				target.writeMapping(x,y,original[pos]);
72 				pos++;
73 			}
74 		}
75 	}
76 }
77 
78 public class WriteToMapSingle : UndoableEvent {
79 	ITileLayer target;
80 	int x;
81 	int y;
82 	MappingElement me;
83 	MappingElement original;
84 	public this(ITileLayer target, int x, int y, MappingElement me) {
85 		this.target = target;
86 		this.x = x;
87 		this.y = y;
88 		this.me = me;
89 	}
90 	public void redo() {
91 		original = target.readMapping(x,y);
92 		target.writeMapping(x,y,me);
93 		debug {
94 			import std.stdio : writeln;
95 			writeln("Layer was written at position ", x, ";", y," with values ", me);
96 		}
97 	}
98 	public void undo() {
99 		target.writeMapping(x,y,original);
100 	}
101 }
102 
103 public class CreateTileLayerEvent : UndoableEvent {
104 	TileLayer creation;
105 	MapDocument target;
106 	int tX;
107 	int tY;
108 	int mX;
109 	int mY;
110 	int pri;
111 	string name;
112 	string file;
113 	string res;
114 	bool embed;
115 	Tag backup;
116 	Image tileSource;
117 
118 	public this(MapDocument target, int tX, int tY, int mX, int mY, dstring name, string file, string res,
119 			bool embed) {
120 		import std.utf : toUTF8;
121 		creation = new TileLayer(tX, tY);
122 		this.target = target;
123 		//this.md = md;
124 		this.tX = tX;
125 		this.tY = tY;
126 		this.mX = mX;
127 		this.mY = mY;
128 		this.name = toUTF8(name);
129 		this.file = file;
130 		this.res = res;
131 		this.embed = embed;
132 		//this.imageReturnFunc = imageReturnFunc;
133 	}
134 	public void redo() {
135 		import std.file : exists, isFile;
136 		import std.path : baseName;
137 		import std.utf : toUTF8;
138 		import PixelPerfectEngine.system.etc : intToHex;
139 		if (backup) {	//If a backup exists, then re-add that to the document, then return.
140 			target.mainDoc.addNewLayer(pri, backup, creation);
141 			target.outputWindow.addLayer(pri);
142 			return;
143 		}
144 		try {
145 			const int nextLayer = target.nextLayerNumber;
146 
147 			//handle the following instances for mapping:
148 			//file == null AND embed
149 			//file == existing file AND embed
150 			//file == existing file AND !embed
151 			//file == nonexisting file
152 			if ((!exists(file) || !isFile(file)) && embed) {	//create new instance for the map by embedding data into the SDLang file
153 				//selDoc.mainDoc.tld[nextLayer] = new
154 				MappingElement[] me;
155 				me.length = mX * mY;
156 				creation.loadMapping(mX, mY, me);
157 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
158 				target.mainDoc.addEmbeddedMapData(nextLayer, me);
159 			} else if (!exists(file)) {	//Create empty file
160 				File f = File(file, "wb");
161 				MappingElement[] me;
162 				me.length = mX * mY;
163 				creation.loadMapping(mX, mY, me);
164 				target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation);
165 				saveMapFile(MapDataHeader(mX, mY), me, f);
166 				target.mainDoc.addMapDataFile(nextLayer, res);
167 			} else {	//load mapping, embed data into current file if needed
168 				MapDataHeader mdh;
169 				MappingElement[] me = loadMapFile(File(file), mdh);
170 				creation.loadMapping(mdh.sizeX, mdh.sizeY, me);
171 				if (embed)
172 					target.mainDoc.addEmbeddedMapData(nextLayer, me);
173 				else
174 					target.mainDoc.addMapDataFile(nextLayer, res);
175 			}
176 
177 			//handle the following instances for materials:
178 			//res == image file
179 			//TODO: check if material resource file has any embedded resource data
180 			//TODO: enable importing from SDLang map files (*.ppm)
181 			//TODO: generate dummy tiles for nonexistent material
182 			if (exists(res)) {
183 				//load the resource file and test if it's the correct size (through an exception)
184 				tileSource = loadImage(File(res));
185 				ABitmap[] tilesheet;
186 				switch (tileSource.getBitdepth()) {
187 					case 4:
188 						Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(tileSource, tX, tY);
189 						foreach(p; output)
190 							tilesheet ~= p;
191 						break;
192 					case 8:
193 						Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(tileSource, tX, tY);
194 						foreach(p; output)
195 							tilesheet ~= p;
196 						break;
197 					case 16:
198 						Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(tileSource, tX, tY);
199 						foreach(p; output)
200 							tilesheet ~= p;
201 						break;
202 					case 32:
203 						Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(tileSource, tX, tY);
204 						foreach(p; output)
205 							tilesheet ~= p;
206 						break;
207 					default:
208 						throw new Exception("Unsupported bitdepth!");
209 
210 				}
211 				if (tilesheet.length == 0) throw new Exception("No tiles were imported!");
212 				target.addTileSet(nextLayer, tilesheet);
213 				target.mainDoc.addTileSourceFile(nextLayer, res);
214 				/+debug {
215 					TileLayer tl = cast(TileLayer)target;
216 					writeln(tl.displayList);
217 				}+/
218 				//writeln(tilesheet.length);
219 				//generate default names for the tiles
220 				{
221 					TileInfo[] idList;
222 					string nameBase = baseName(res);
223 					for (int id ; id < tilesheet.length ; id++) {
224 						idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4));
225 						//writeln(idList);
226 					}
227 					target.mainDoc.addTileInfo(nextLayer, idList, res);
228 				}
229 				if (tileSource.isIndexed) {
230 					Color[] palette;
231 					/*foreach (color ; tileSource.palette) {
232 						palette ~= Color(color.a, color.r, color.g, color.b);
233 						debug writeln(color);
234 					}*/
235 					auto sourcePalette = tileSource.palette;
236 					palette.reserve(sourcePalette.length);
237 					for (ushort i ; i < sourcePalette.length ; i++){
238 						const auto origC = sourcePalette[i];
239 						const Color c = Color(origC.a, origC.r, origC.g, origC.b);
240 						palette ~= c;
241 					}
242 					debug writeln(palette);
243 					target.mainDoc.addPaletteFile(file, "", cast(int)target.outputWindow.palette.length);
244 					target.outputWindow.palette = target.outputWindow.palette ~ palette;
245 				}
246 
247 			}
248 			target.outputWindow.addLayer(nextLayer);
249 			target.selectedLayer = nextLayer;
250 			pri = nextLayer;
251 			target.updateLayerList();
252 			target.updateMaterialList();
253 		} catch (Exception e) {
254 			debug writeln(e);
255 		}
256 	}
257 	public void undo() {
258 		//Just remove the added layer from the layerlists
259 		target.outputWindow.removeLayer(pri);
260 		backup = target.mainDoc.removeLayer(pri);
261 		target.updateLayerList();
262 		target.updateMaterialList();
263 	}
264 }