1 using System;
2 using System.ComponentModel;
3 using System.Linq;
4 using System.Reactive.Disposables;
5 using System.Reactive.Linq;
6 using Reactive.Bindings;
7 using Reactive.Bindings.Extensions;
8 using TrainEditor2.Models.Panels;
9 using TrainEditor2.ViewModels.Others;
10 
11 namespace TrainEditor2.ViewModels.Panels
12 {
13 	internal class PanelViewModel : BaseViewModel
14 	{
15 		internal ReadOnlyReactivePropertySlim<ThisViewModel> This
16 		{
17 			get;
18 		}
19 
20 		internal ReactiveProperty<TreeViewItemViewModel> TreeItem
21 		{
22 			get;
23 		}
24 
25 		internal ReactiveProperty<TreeViewItemViewModel> SelectedTreeItem
26 		{
27 			get;
28 		}
29 
30 		internal ReadOnlyReactiveCollection<ListViewColumnHeaderViewModel> ListColumns
31 		{
32 			get;
33 		}
34 
35 		internal ReadOnlyReactiveCollection<ListViewItemViewModel> ListItems
36 		{
37 			get;
38 		}
39 
40 		internal ReactiveProperty<ListViewItemViewModel> SelectedListItem
41 		{
42 			get;
43 		}
44 
45 		internal ReadOnlyReactivePropertySlim<ScreenViewModel> SelectedScreen
46 		{
47 			get;
48 		}
49 
50 		internal ReadOnlyReactivePropertySlim<PilotLampElementViewModel> SelectedPilotLamp
51 		{
52 			get;
53 		}
54 
55 		internal ReadOnlyReactivePropertySlim<NeedleElementViewModel> SelectedNeedle
56 		{
57 			get;
58 		}
59 
60 		internal ReadOnlyReactivePropertySlim<DigitalNumberElementViewModel> SelectedDigitalNumber
61 		{
62 			get;
63 		}
64 
65 		internal ReadOnlyReactivePropertySlim<DigitalGaugeElementViewModel> SelectedDigitalGauge
66 		{
67 			get;
68 		}
69 
70 		internal ReadOnlyReactivePropertySlim<LinearGaugeElementViewModel> SelectedLinearGauge
71 		{
72 			get;
73 		}
74 
75 		internal ReadOnlyReactivePropertySlim<TimetableElementViewModel> SelectedTimetable
76 		{
77 			get;
78 		}
79 
80 		internal ReadOnlyReactivePropertySlim<TouchElementViewModel> SelectedTouch
81 		{
82 			get;
83 		}
84 
85 		internal ReactiveCommand AddScreen
86 		{
87 			get;
88 		}
89 
90 		internal ReactiveCommand AddPilotLamp
91 		{
92 			get;
93 		}
94 
95 		internal ReactiveCommand AddNeedle
96 		{
97 			get;
98 		}
99 
100 		internal ReactiveCommand AddDigitalNumber
101 		{
102 			get;
103 		}
104 
105 		internal ReactiveCommand AddDigitalGauge
106 		{
107 			get;
108 		}
109 
110 		internal ReactiveCommand AddLinearGauge
111 		{
112 			get;
113 		}
114 
115 		internal ReactiveCommand AddTimetable
116 		{
117 			get;
118 		}
119 
120 		internal ReactiveCommand AddTouch
121 		{
122 			get;
123 		}
124 
125 		internal ReactiveCommand CopyScreen
126 		{
127 			get;
128 		}
129 
130 		internal ReactiveCommand CopyPilotLamp
131 		{
132 			get;
133 		}
134 
135 		internal ReactiveCommand CopyNeedle
136 		{
137 			get;
138 		}
139 
140 		internal ReactiveCommand CopyDigitalNumber
141 		{
142 			get;
143 		}
144 
145 		internal ReactiveCommand CopyDigitalGauge
146 		{
147 			get;
148 		}
149 
150 		internal ReactiveCommand CopyLinearGauge
151 		{
152 			get;
153 		}
154 
155 		internal ReactiveCommand CopyTimetable
156 		{
157 			get;
158 		}
159 
160 		internal ReactiveCommand CopyTouch
161 		{
162 			get;
163 		}
164 
165 		internal ReactiveCommand RemoveScreen
166 		{
167 			get;
168 		}
169 
170 		internal ReactiveCommand RemovePilotLamp
171 		{
172 			get;
173 		}
174 
175 		internal ReactiveCommand RemoveNeedle
176 		{
177 			get;
178 		}
179 
180 		internal ReactiveCommand RemoveDigitalNumber
181 		{
182 			get;
183 		}
184 
185 		internal ReactiveCommand RemoveDigitalGauge
186 		{
187 			get;
188 		}
189 
190 		internal ReactiveCommand RemoveLinearGauge
191 		{
192 			get;
193 		}
194 
195 		internal ReactiveCommand RemoveTimetable
196 		{
197 			get;
198 		}
199 
200 		internal ReactiveCommand RemoveTouch
201 		{
202 			get;
203 		}
204 
PanelViewModel(Panel panel)205 		internal PanelViewModel(Panel panel)
206 		{
207 			CompositeDisposable treeItemDisposable = new CompositeDisposable();
208 			CompositeDisposable listItemDisposable = new CompositeDisposable();
209 
210 			This = panel
211 				.ObserveProperty(x => x.This)
212 				.Do(_ => This?.Value.Dispose())
213 				.Select(x => new ThisViewModel(x))
214 				.ToReadOnlyReactivePropertySlim()
215 				.AddTo(disposable);
216 
217 			TreeItem = panel
218 				.ObserveProperty(x => x.TreeItem)
219 				.Do(_ => TreeItem?.Value.Dispose())
220 				.Select(x => new TreeViewItemViewModel(x))
221 				.ToReactiveProperty()
222 				.AddTo(disposable);
223 
224 			TreeItem.Subscribe(x =>
225 				{
226 					treeItemDisposable.Dispose();
227 					treeItemDisposable = new CompositeDisposable();
228 
229 					x.PropertyChangedAsObservable()
230 						.ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.None)
231 						.Subscribe(_ => TreeItem.ForceNotify())
232 						.AddTo(treeItemDisposable);
233 				})
234 				.AddTo(disposable);
235 
236 			SelectedTreeItem = panel
237 				.ToReactivePropertyAsSynchronized(
238 					x => x.SelectedTreeItem,
239 					x => TreeItem.Value.SearchViewModel(x),
240 					x => x?.Model
241 				)
242 				.AddTo(disposable);
243 
244 			ListColumns = panel.ListColumns
245 				.ToReadOnlyReactiveCollection(x => new ListViewColumnHeaderViewModel(x))
246 				.AddTo(disposable);
247 
248 			ListItems = panel.ListItems
249 				.ToReadOnlyReactiveCollection(x => new ListViewItemViewModel(x))
250 				.AddTo(disposable);
251 
252 			SelectedListItem = panel
253 				.ToReactivePropertyAsSynchronized(
254 					x => x.SelectedListItem,
255 					x => ListItems.FirstOrDefault(y => y.Model == x),
256 					x => x?.Model
257 				)
258 				.AddTo(disposable);
259 
260 			SelectedTreeItem
261 				.Subscribe(_ =>
262 				{
263 					SelectedListItem.Value = null;
264 					panel.CreateListColumns();
265 					panel.CreateListItems();
266 				})
267 				.AddTo(disposable);
268 
269 			SelectedListItem
270 				.Where(x => x != null)
271 				.Subscribe(x =>
272 				{
273 					listItemDisposable.Dispose();
274 					listItemDisposable = new CompositeDisposable();
275 
276 					CompositeDisposable tagDisposable = new CompositeDisposable();
277 
278 					x.Tag
279 						.OfType<INotifyPropertyChanged>()
280 						.Subscribe(y =>
281 						{
282 							tagDisposable.Dispose();
283 							tagDisposable = new CompositeDisposable();
284 
285 							CompositeDisposable subjectDisposable = new CompositeDisposable();
286 
287 							y.PropertyChangedAsObservable()
288 								.Subscribe(_ => panel.UpdateListItem(x.Model))
289 								.AddTo(tagDisposable);
290 
291 							Screen screen = y as Screen;
292 							PilotLampElement pilotLamp = y as PilotLampElement;
293 							NeedleElement needle = y as NeedleElement;
294 							DigitalNumberElement digitalNumber = y as DigitalNumberElement;
295 							DigitalGaugeElement digitalGauge = y as DigitalGaugeElement;
296 							LinearGaugeElement linearGauge = y as LinearGaugeElement;
297 
298 							screen?.ObserveProperty(z => z.Number)
299 								.Select(_ => TreeItem.Value.Children[1].Children.FirstOrDefault(z => z.Tag.Value == screen))
300 								.Where(z => z != null)
301 								.Subscribe(z => panel.RenameScreenTreeItem(z.Model))
302 								.AddTo(tagDisposable);
303 
304 							pilotLamp?.ObserveProperty(z => z.Subject)
305 								.Subscribe(z =>
306 								{
307 									subjectDisposable.Dispose();
308 									subjectDisposable = new CompositeDisposable();
309 
310 									z.PropertyChangedAsObservable()
311 										.Subscribe(_ => panel.UpdateListItem(x.Model))
312 										.AddTo(subjectDisposable);
313 								})
314 								.AddTo(tagDisposable);
315 
316 							needle?.ObserveProperty(z => z.Subject)
317 								.Subscribe(z =>
318 								{
319 									subjectDisposable.Dispose();
320 									subjectDisposable = new CompositeDisposable();
321 
322 									z.PropertyChangedAsObservable()
323 										.Subscribe(_ => panel.UpdateListItem(x.Model))
324 										.AddTo(subjectDisposable);
325 								})
326 								.AddTo(tagDisposable);
327 
328 							digitalNumber?.ObserveProperty(z => z.Subject)
329 								.Subscribe(z =>
330 								{
331 									subjectDisposable.Dispose();
332 									subjectDisposable = new CompositeDisposable();
333 
334 									z.PropertyChangedAsObservable()
335 										.Subscribe(_ => panel.UpdateListItem(x.Model))
336 										.AddTo(subjectDisposable);
337 								})
338 								.AddTo(tagDisposable);
339 
340 							digitalGauge?.ObserveProperty(z => z.Subject)
341 								.Subscribe(z =>
342 								{
343 									subjectDisposable.Dispose();
344 									subjectDisposable = new CompositeDisposable();
345 
346 									z.PropertyChangedAsObservable()
347 										.Subscribe(_ => panel.UpdateListItem(x.Model))
348 										.AddTo(subjectDisposable);
349 								})
350 								.AddTo(tagDisposable);
351 
352 							linearGauge?.ObserveProperty(z => z.Subject)
353 								.Subscribe(z =>
354 								{
355 									subjectDisposable.Dispose();
356 									subjectDisposable = new CompositeDisposable();
357 
358 									z.PropertyChangedAsObservable()
359 										.Subscribe(_ => panel.UpdateListItem(x.Model))
360 										.AddTo(subjectDisposable);
361 								})
362 								.AddTo(tagDisposable);
363 
364 							subjectDisposable.AddTo(tagDisposable);
365 						})
366 						.AddTo(listItemDisposable);
367 
368 					tagDisposable.AddTo(listItemDisposable);
369 				})
370 				.AddTo(disposable);
371 
372 			SelectedScreen = SelectedListItem
373 				.Select(x => x?.Tag.Value as Screen)
374 				.Do(_ => SelectedScreen?.Value?.Dispose())
375 				.Select(x => x != null ? new ScreenViewModel(x, panel.Screens.Where(y => y != x)) : null)
376 				.ToReadOnlyReactivePropertySlim()
377 				.AddTo(disposable);
378 
379 			SelectedPilotLamp = SelectedListItem
380 				.Select(x => x?.Tag.Value as PilotLampElement)
381 				.Do(_ => SelectedPilotLamp?.Value?.Dispose())
382 				.Select(x => x != null ? new PilotLampElementViewModel(x) : null)
383 				.ToReadOnlyReactivePropertySlim()
384 				.AddTo(disposable);
385 
386 			SelectedNeedle = SelectedListItem
387 				.Select(x => x?.Tag.Value as NeedleElement)
388 				.Do(_ => SelectedNeedle?.Value?.Dispose())
389 				.Select(x => x != null ? new NeedleElementViewModel(x) : null)
390 				.ToReadOnlyReactivePropertySlim()
391 				.AddTo(disposable);
392 
393 			SelectedDigitalNumber = SelectedListItem
394 				.Select(x => x?.Tag.Value as DigitalNumberElement)
395 				.Do(_ => SelectedDigitalNumber?.Value?.Dispose())
396 				.Select(x => x != null ? new DigitalNumberElementViewModel(x) : null)
397 				.ToReadOnlyReactivePropertySlim()
398 				.AddTo(disposable);
399 
400 			SelectedDigitalGauge = SelectedListItem
401 				.Select(x => x?.Tag.Value as DigitalGaugeElement)
402 				.Do(_ => SelectedDigitalGauge?.Value?.Dispose())
403 				.Select(x => x != null ? new DigitalGaugeElementViewModel(x) : null)
404 				.ToReadOnlyReactivePropertySlim()
405 				.AddTo(disposable);
406 
407 			SelectedLinearGauge = SelectedListItem
408 				.Select(x => x?.Tag.Value as LinearGaugeElement)
409 				.Do(_ => SelectedLinearGauge?.Value?.Dispose())
410 				.Select(x => x != null ? new LinearGaugeElementViewModel(x) : null)
411 				.ToReadOnlyReactivePropertySlim()
412 				.AddTo(disposable);
413 
414 			SelectedTimetable = SelectedListItem
415 				.Select(x => x?.Tag.Value as TimetableElement)
416 				.Do(_ => SelectedTimetable?.Value?.Dispose())
417 				.Select(x => x != null ? new TimetableElementViewModel(x) : null)
418 				.ToReadOnlyReactivePropertySlim()
419 				.AddTo(disposable);
420 
421 			SelectedTouch = SelectedListItem
422 				.Select(x => x?.Tag.Value as TouchElement)
423 				.Do(_ => SelectedTouch?.Value?.Dispose())
424 				.Select(x => x != null ? new TouchElementViewModel(x) : null)
425 				.ToReadOnlyReactivePropertySlim()
426 				.AddTo(disposable);
427 
428 			AddScreen = SelectedTreeItem
429 				.Select(x => x == TreeItem.Value.Children[1])
430 				.ToReactiveCommand()
431 				.WithSubscribe(panel.AddScreen)
432 				.AddTo(disposable);
433 
434 			AddPilotLamp = SelectedTreeItem
435 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[0])
436 							 || x == TreeItem.Value.Children[2].Children[0])
437 				.ToReactiveCommand()
438 				.WithSubscribe(panel.AddPilotLamp)
439 				.AddTo(disposable);
440 
441 			AddNeedle = SelectedTreeItem
442 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[1])
443 							 || x == TreeItem.Value.Children[2].Children[1])
444 				.ToReactiveCommand()
445 				.WithSubscribe(panel.AddNeedle)
446 				.AddTo(disposable);
447 
448 			AddDigitalNumber = SelectedTreeItem
449 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[2])
450 							 || x == TreeItem.Value.Children[2].Children[2])
451 				.ToReactiveCommand()
452 				.WithSubscribe(panel.AddDigitalNumber)
453 				.AddTo(disposable);
454 
455 			AddDigitalGauge = SelectedTreeItem
456 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[3])
457 							 || x == TreeItem.Value.Children[2].Children[3])
458 				.ToReactiveCommand()
459 				.WithSubscribe(panel.AddDigitalGauge)
460 				.AddTo(disposable);
461 
462 			AddLinearGauge = SelectedTreeItem
463 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[4])
464 							 || x == TreeItem.Value.Children[2].Children[4])
465 				.ToReactiveCommand()
466 				.WithSubscribe(panel.AddLinearGauge)
467 				.AddTo(disposable);
468 
469 			AddTimetable = SelectedTreeItem
470 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[0].Children[5])
471 							 || x == TreeItem.Value.Children[2].Children[5])
472 				.ToReactiveCommand()
473 				.WithSubscribe(panel.AddTimetable)
474 				.AddTo(disposable);
475 
476 			AddTouch = SelectedTreeItem
477 				.Select(x => TreeItem.Value.Children[1].Children.Any(y => x == y.Children[1]))
478 				.ToReactiveCommand()
479 				.WithSubscribe(panel.AddTouch)
480 				.AddTo(disposable);
481 
482 			CopyScreen = SelectedScreen
483 				.Select(x => x != null)
484 				.ToReactiveCommand()
485 				.WithSubscribe(panel.CopyScreen)
486 				.AddTo(disposable);
487 
488 			CopyPilotLamp = SelectedPilotLamp
489 				.Select(x => x != null)
490 				.ToReactiveCommand()
491 				.WithSubscribe(panel.CopyPilotLamp)
492 				.AddTo(disposable);
493 
494 			CopyNeedle = SelectedNeedle
495 				.Select(x => x != null)
496 				.ToReactiveCommand()
497 				.WithSubscribe(panel.CopyNeedle)
498 				.AddTo(disposable);
499 
500 			CopyDigitalNumber = SelectedDigitalNumber
501 				.Select(x => x != null)
502 				.ToReactiveCommand()
503 				.WithSubscribe(panel.CopyDigitalNumber)
504 				.AddTo(disposable);
505 
506 			CopyDigitalGauge = SelectedDigitalGauge
507 				.Select(x => x != null)
508 				.ToReactiveCommand()
509 				.WithSubscribe(panel.CopyDigitalGauge)
510 				.AddTo(disposable);
511 
512 			CopyLinearGauge = SelectedLinearGauge
513 				.Select(x => x != null)
514 				.ToReactiveCommand()
515 				.WithSubscribe(panel.CopyLinearGauge)
516 				.AddTo(disposable);
517 
518 			CopyTimetable = SelectedTimetable
519 				.Select(x => x != null)
520 				.ToReactiveCommand()
521 				.WithSubscribe(panel.CopyTimetable)
522 				.AddTo(disposable);
523 
524 			CopyTouch = SelectedTouch
525 				.Select(x => x != null)
526 				.ToReactiveCommand()
527 				.WithSubscribe(panel.CopyTouch)
528 				.AddTo(disposable);
529 
530 			RemoveScreen = SelectedScreen
531 				.Select(x => x != null)
532 				.ToReactiveCommand()
533 				.WithSubscribe(panel.RemoveScreen)
534 				.AddTo(disposable);
535 
536 			RemovePilotLamp = SelectedPilotLamp
537 				.Select(x => x != null)
538 				.ToReactiveCommand()
539 				.WithSubscribe(panel.RemovePilotLamp)
540 				.AddTo(disposable);
541 
542 			RemoveNeedle = SelectedNeedle
543 				.Select(x => x != null)
544 				.ToReactiveCommand()
545 				.WithSubscribe(panel.RemoveNeedle)
546 				.AddTo(disposable);
547 
548 			RemoveDigitalNumber = SelectedDigitalNumber
549 				.Select(x => x != null)
550 				.ToReactiveCommand()
551 				.WithSubscribe(panel.RemoveDigitalNumber)
552 				.AddTo(disposable);
553 
554 			RemoveDigitalGauge = SelectedDigitalGauge
555 				.Select(x => x != null)
556 				.ToReactiveCommand()
557 				.WithSubscribe(panel.RemoveDigitalGauge)
558 				.AddTo(disposable);
559 
560 			RemoveLinearGauge = SelectedLinearGauge
561 				.Select(x => x != null)
562 				.ToReactiveCommand()
563 				.WithSubscribe(panel.RemoveLinearGauge)
564 				.AddTo(disposable);
565 
566 			RemoveTimetable = SelectedTimetable
567 				.Select(x => x != null)
568 				.ToReactiveCommand()
569 				.WithSubscribe(panel.RemoveTimetable)
570 				.AddTo(disposable);
571 
572 			RemoveTouch = SelectedTouch
573 				.Select(x => x != null)
574 				.ToReactiveCommand()
575 				.WithSubscribe(panel.RemoveTouch)
576 				.AddTo(disposable);
577 
578 			treeItemDisposable.AddTo(disposable);
579 			listItemDisposable.AddTo(disposable);
580 		}
581 	}
582 }
583