1 module qui.lists;
2 
3 import qui.qui;//used for Position & Cell in Matrix
4 import qui.misc;
5 import std.file;
6 import std.stdio;
7 
8 class List(T){
9 private:
10 	T[] list;
11 	uinteger taken=0;
12 public:
13 	void add(T dat){
14 		if (taken==list.length){
15 			list.length+=10;
16 		}
17 		taken++;
18 		list[taken-1] = dat;
19 	}
20 	void addArray(T[] dat){
21 		list.length = taken;
22 		list ~= dat;
23 		taken += dat.length;
24 	}
25 	void set(uinteger index, T dat){
26 		list[index]=dat;
27 	}
28 	void remove(uinteger index, uinteger count=1){
29 		integer i;
30 		integer till=taken-count;
31 		for (i=index;i<till;i++){
32 			list[i] = list[i+count];
33 		}
34 		list.length-=count;
35 		taken-=count;
36 	}
37 	void removeLast(uinteger count = 1){
38 		taken -= count;
39 		if (list.length-taken>10){
40 			list.length=taken;
41 		}
42 	}
43 	void shrink(uinteger newSize){
44 		list.length=newSize;
45 		taken = list.length;
46 	}
47 	void insert(uinteger index, T[] dat){
48 		integer i;
49 		T[] ar,ar2;
50 		ar=list[0..index];
51 		ar2=list[index..taken];
52 		list.length=0;
53 		list=ar~dat~ar2;
54 		taken+=dat.length;
55 	}
56 	void insert(uinteger index, T dat){
57 		integer i;
58 		T[] ar,ar2;
59 		ar=list[0..index];
60 		ar2=list[index..taken];
61 		list.length=0;
62 		list=ar~[dat]~ar2;
63 		taken++;
64 	}
65 	void saveFile(string s, T sp){
66 		File f = File(s,"w");
67 		uinteger i;
68 		for (i=0;i<taken;i++){
69 			f.write(list[i],sp);
70 		}
71 		f.close;
72 	}
73 	T read(uinteger index){
74 		return list[index];
75 	}
76 	T[] readRange(uinteger index,uinteger i2){
77 		T[] r;
78 		r.length = (i2-index);
79 		r[0 .. r.length] = list[index .. i2];
80 		return r;
81 	}
82 	T readLast(){
83 		return list[taken-1];
84 	}
85 	T[] readLast(uinteger count){
86 		T[] r;
87 		r.length = count;
88 		r[0 .. r.length] = list[taken-count..taken];
89 		return r;
90 	}
91 	integer length(){
92 		return taken;
93 	}
94 	T[] toArray(){
95 		uinteger i;
96 		T[] r;
97 		if (taken!=-1){
98 			r.length=taken;
99 			for (i=0;i<taken;i++){//using a loop cuz simple '=' will just copy the pionter
100 				r[i]=list[i];
101 			}
102 		}
103 		return r;
104 	}
105 	void loadArray(T[] dats){
106 		uinteger i;
107 		list.length=dats.length;
108 		taken=list.length;
109 		for (i=0;i<dats.length;i++){
110 			list[i]=dats[i];
111 		}
112 	}
113 	void clear(){
114 		list.length=0;
115 		taken=0;
116 	}
117 	integer indexOf(T dat, integer i=0, bool forward=true){
118 		if (forward){
119 			for (;i<taken;i++){
120 				if (list[i]==dat){break;}
121 				if (i==taken-1){i=-1;break;}
122 			}
123 		}else{
124 			for (;i>=0;i--){
125 				if (list[i]==dat){break;}
126 				if (i==0){i=-1;break;}
127 			}
128 		}
129 		if (taken==0){
130 			i=-1;
131 		}
132 		return i;
133 	}
134 }
135 ///represents an item in a linked list. contains the item, and pointer to the next item's container
136 private struct LinkedItem(T){
137 	T data;
138 	LinkedItem!(T)* next = null;//mark it null to show the list has ended
139 }
140 ///a linked list, used where only reading in the forward direction is required
141 class LinkedList(T){
142 private:
143 	LinkedItem!(T)* firstItemPtr;
144 	LinkedItem!(T)* lastItemPtr;//the pointer of the last item, used for appending new items
145 	LinkedItem!(T)* nextReadPtr;//the pointer of the next item to be read
146 
147 	uinteger itemCount;//stores the total number of items
148 public:
149 	this(){
150 		firstItemPtr = null;
151 		lastItemPtr = null;
152 		nextReadPtr = null;
153 		itemCount = 0;
154 	}
155 	~this(){
156 		//free all the memory occupied
157 		clear();
158 	}
159 	///clears/resets the list. Frees all the occupied memory, & removes all items
160 	void clear(){
161 		//make sure that the list is populated
162 		if (firstItemPtr !is null){
163 			LinkedItem!(T)* nextPtr;
164 			for (nextReadPtr = firstItemPtr; nextReadPtr !is null; nextReadPtr = nextPtr){
165 				nextPtr = (*nextReadPtr).next;
166 				destroy(*nextReadPtr);
167 			}
168 			//reset all variables
169 			firstItemPtr = null;
170 			lastItemPtr = null;
171 			nextReadPtr = null;
172 			itemCount = 0;
173 		}
174 	}
175 	///adds a new item at the end of the list
176 	void append(T item){
177 		LinkedItem!(T)* ptr = new LinkedItem!(T);
178 		(*ptr).data = item;
179 		(*ptr).next = null;
180 		//add it to the list
181 		if (firstItemPtr is null){
182 			firstItemPtr = ptr;
183 			nextReadPtr = firstItemPtr;
184 		}else{
185 			(*lastItemPtr).next = ptr;
186 		}
187 		//mark this item as last
188 		lastItemPtr = ptr;
189 		//increase item count
190 		itemCount ++;
191 	}
192 	///removes the first item in list
193 	void removeFirst(){
194 		//make sure list is populated
195 		if (firstItemPtr !is null){
196 			LinkedItem!(T)* first;
197 			first = firstItemPtr;
198 			//mark the second item as first, if there isn't a second item, it'll automatically be marked null
199 			firstItemPtr = (*firstItemPtr).next;
200 			//if nextReadPtr is firstItemPtr, move it to next as well
201 			if (nextReadPtr is first){
202 				nextReadPtr = firstItemPtr;
203 			}
204 			//free memory occupied by first
205 			destroy(*first);
206 			//decrease count
207 			itemCount --;
208 		}
209 	}
210 	///number of items that the list is holding
211 	@property uinteger count(){
212 		return itemCount;
213 	}
214 	///resets the read position, i.e: set reading position to first item
215 	void resetRead(){
216 		nextReadPtr = firstItemPtr;
217 	}
218 	///returns pointer of next item to be read, null if there are no more items
219 	T* read(){
220 		T* r;
221 		if (nextReadPtr !is null){
222 			r = &((*nextReadPtr).data);
223 			//move read position
224 			nextReadPtr = (*nextReadPtr).next;
225 		}else{
226 			r = null;
227 		}
228 		return r;
229 	}
230 }
231 
232 ///Used in logging widgets. Holds upto certain number, after which older items are removed
233 class LogList(T){
234 private:
235 	List!T list;
236 	uinteger readFrom, maxLen;
237 public:
238 	this(uinteger maxLength=100){
239 		list = new List!T;
240 		readFrom = 0;
241 		maxLen = maxLength;
242 	}
243 	~this(){
244 		delete list;
245 	}
246 	///adds an item to the log
247 	void add(T dat){
248 		if (list.length>=maxLen){
249 			list.set(readFrom,dat);
250 			readFrom++;
251 		}else{
252 			list.add(dat);
253 		}
254 	}
255 	///Returns array containing items, in first-added-last order
256 	T[] read(uinteger count=0){
257 		T[] r;
258 		if (count>list.length){
259 			count = list.length;
260 		}
261 		if (count > 0){
262 			uinteger i;
263 			if (count>list.length){
264 				count = list.length;
265 			}
266 			r.length = count;
267 			for (i = readFrom; i < count; i++){
268 				r[i] = list.read((readFrom+i)%count);
269 			}
270 		}else{
271 			r = null;
272 		}
273 		return r;
274 	}
275 	///resets and clears the log
276 	void reset(){
277 		list.clear;
278 		readFrom = 0;
279 	}
280 	///returns the max number of items that can be stored
281 	@property uinteger maxCapacity(){
282 		return maxLen;
283 	}
284 }
285 
286 ///Used to store the widget's/terminal's display in a matrix
287 class Matrix{
288 private:
289 	Cell[][] matrix;//read as: matrix[y][x];
290 	//used to write by widgets
291 	uinteger xPosition, yPosition;
292 
293 	//stores which part was updated
294 	struct UpdateLocation{
295 		uinteger x, y;
296 		uinteger length;
297 	}
298 	LinkedList!UpdateLocation updateAt;
299 
300 	Cell readAsStream(uinteger pos){
301 		return matrix[pos/width][pos%width];
302 	}
303 
304 	char[] cellToChar(Cell[] c){
305 		char[] r;
306 		r.length = c.length;
307 		for (uinteger i = 0; i < c.length; i++){
308 			r[i] = c[i].c;
309 		}
310 		return r;
311 	}
312 
313 	void updateChars(QTerminal terminal, uinteger x, uinteger y, uinteger length){
314 		uinteger readPos = (y*width)+x, i;
315 		Cell[] line;
316 		line.length = length;
317 		//read all content into line
318 		for (i = 0; i < length; i++){
319 			line[i] = readAsStream(readPos);
320 			readPos ++;
321 		}
322 		//start writing it to terminal
323 		terminal.moveTo(cast(int)x, cast(int)y);
324 		//set origin colors
325 		RGBColor originBgColor, originTextColor;
326 		originBgColor = line[0].bgColor;
327 		originTextColor = line[0].textColor;
328 		terminal.setColors(originTextColor, originBgColor);
329 
330 		length --;
331 		uinteger writeFrom = 0;
332 		for (i = 0; i <= length; i++){
333 			//check if colors have changed, or is it end
334 			if (line[i].bgColor != originBgColor || line[i].textColor != originTextColor || i == length){
335 				if (writeFrom < i){
336 					terminal.setColors(originTextColor, originBgColor);
337 					terminal.writeChars(cellToChar(line[writeFrom .. i]));
338 				}
339 				//if is at end, write the 'last-encountered' char too
340 				if (i == length){
341 					terminal.setColors(line[i].textColor, line[i].bgColor);
342 					terminal.writeChars(line[i].c);
343 				}
344 			}
345 		}
346 	}
347 public:
348 	this(uinteger matrixWidth, uinteger matrixHeight, Cell fill){
349 		//set matrix size
350 		matrix.length = matrixHeight;
351 		//set width:
352 		for (uinteger i = 0; i < matrix.length; i++){
353 			matrix[i].length = matrixWidth;
354 			matrix[i][0 .. matrixWidth] = fill;
355 		}
356 		updateAt = new LinkedList!UpdateLocation;
357 		//set updateAt to whole Matrix
358 		UpdateLocation loc;
359 		loc.x, loc.y = 0;
360 		loc.length = width*height;
361 		updateAt.append(loc);
362 	}
363 	~this(){
364 		delete updateAt;
365 	}
366 	///Clear the matrix, and put fill in every cell
367 	void clear(Cell fill){
368 		for (uinteger row = 0; row < matrix.length; row++){
369 			matrix[row][0 .. matrix[row].length] = fill;
370 		}
371 		//set updateAt to whole Matrix
372 		UpdateLocation loc;
373 		loc.x, loc.y = 0;
374 		loc.length = width*height;
375 		updateAt.append(loc);
376 	}
377 	///Change size of the matrix, width and height
378 	bool changeSize(uinteger matrixWidth, uinteger matrixHeight, Cell fill){
379 		//make sure width & size are at least 1
380 		bool r = true;
381 		if (matrixWidth == 0 || matrixHeight == 0){
382 			r = false;
383 		}
384 		if (r){
385 			matrix.length = matrixHeight;
386 			for (uinteger i = 0; i < matrix.length; i++){
387 				matrix[i].length = matrixWidth;
388 				matrix[i][0 .. matrixWidth] = fill;
389 			}
390 		}
391 		return r;
392 	}
393 	///sets write position to (0, 0)
394 	void resetWritePosition(){
395 		xPosition = 0;
396 		yPosition = 0;
397 	}
398 	///used to write to matrix, call Matrix.setWriteLimits before this
399 	void write(char[] c, RGBColor textColor, RGBColor bgColor){
400 		uinteger i, xEnd, yEnd;
401 		xEnd = width;
402 		yEnd = height;
403 		//add it to updateAt
404 		UpdateLocation loc;
405 		loc.x = xPosition;
406 		loc.y = yPosition;
407 		loc.length = c.length;
408 		updateAt.append(loc);
409 		for (i = 0; i < c.length; xPosition++){
410 			if (xPosition == xEnd){
411 				//move to next row
412 				yPosition++;
413 				xPosition = 0;
414 			}
415 			//check if no more space left
416 			if (yPosition >= yEnd){
417 				//no more space left
418 				break;
419 			}
420 			matrix[yPosition][xPosition].c = c[i];
421 			matrix[yPosition][xPosition].bgColor = bgColor;
422 			matrix[yPosition][xPosition].textColor = textColor;
423 			i++;
424 		}
425 	}
426 	///changes colors for whole matrix
427 	void setColors(RGBColor textColor, RGBColor bgColor){
428 		for (uinteger i = 0; i < matrix.length; i++){
429 			for(uinteger j = 0; j < matrix[i].length; j++){
430 				matrix[i][j].textColor = textColor;
431 				matrix[i][j].bgColor = bgColor;
432 			}
433 		}
434 		//set updateAt to whole Matrix
435 		UpdateLocation loc;
436 		loc.x, loc.y = 0;
437 		loc.length = width*height;
438 		updateAt.append(loc);
439 	}
440 	///move to a different position to write
441 	bool moveTo(uinteger x, uinteger y){
442 		bool r = true;
443 		if (x > matrix[0].length-1 || y > matrix.length-1){
444 			r = false;
445 		}
446 		if (r){
447 			xPosition = x;
448 			yPosition = y;
449 		}
450 		return r;
451 	}
452 	///returns number of rows/lines in matrix
453 	@property uinteger height(){
454 		return matrix.length;
455 	}
456 	///returns number of columns in matrix
457 	@property uinteger width(){
458 		return matrix[0].length;
459 	}
460 	///returns the point ox x-axis where next write will start from
461 	@property uinteger writePosX(){
462 		return xPosition;
463 	}
464 	///returns the point ox y-axis where next write will start from
465 	@property uinteger writePosY(){
466 		return yPosition;
467 	}
468 	///read a cell from the matrix
469 	Cell read(uinteger x, uinteger y){
470 		return matrix[y][x];
471 	}
472 	///read a complete row from matrix
473 	Cell[] readRow(uinteger y){
474 		return matrix[y][];
475 	}
476 	///insert a matrix into this one at a position
477 	bool insert(Matrix toInsert, uinteger x, uinteger y){
478 		uinteger matrixWidth, matrixHeight;
479 		matrixHeight = toInsert.height;
480 		matrixWidth = toInsert.width;
481 		bool r = true;
482 		if (matrixHeight + y > this.height || matrixWidth + x > this.width){
483 			r = false;
484 		}else{
485 			uinteger row = 0;
486 			uinteger endAtRow = matrixHeight;
487 			for (;row<endAtRow; row++){
488 				matrix[y + row][x .. x+matrixWidth] = toInsert.readRow(row);
489 				//add this row to updateAt
490 				UpdateLocation loc;
491 				loc.x = x;
492 				loc.y = y+row;
493 				loc.length = matrixWidth;
494 				updateAt.append(loc);
495 			}
496 		}
497 		//debug{toFile("/home/nafees/Desktop/a");}
498 		return r;
499 	}
500 	/*debug{
501 		void toFile(string fname){
502 			File f = File(fname, "w");
503 			for (uinteger i = 0; i < matrix.length; i++){
504 				for (uinteger j = 0; j < matrix[i].length; j++){
505 					f.write(matrix[i][j].c);
506 				}
507 				f.write('|','\n');
508 			}
509 			f.close;
510 		}
511 	}*/
512 	///Write contents of matrix to a QTerminal
513 	void flushToTerminal(QTerminal terminal){
514 		//make sure that there was some change that needs to be flushed
515 		if (updateAt.count > 0){
516 			//start going through all update-needy
517 			uinteger i, count;
518 			count = updateAt.count();
519 			UpdateLocation* ptr;
520 			for (i = 0; i < count; i++){
521 				ptr = updateAt.read();
522 				if (ptr is null){
523 					break;
524 				}
525 				if ((*ptr).length > 0){
526 					updateChars(terminal, (*ptr).x, (*ptr).y, (*ptr).length);
527 				}
528 				updateAt.removeFirst();
529 			}
530 			terminal.flush();
531 		}
532 	}
533 }