#!/usr/bin/ruby
# encoding: utf-8

class WindowsScreen	
	def initialize(extlayers, button)
		@extlayers = extlayers
		@layers = Array.new
		## table for first SMART read out
		@smarttab = nil
		@smart_quick_icons = Array.new
		@smart_quick_descriptions = Array.new
		## progressbar for SMART self test routine
		@smart_progress = nil
		@break_smart_test = nil
		## table for second SMART readout
		@smartsumtab = nil
		@smart_detail_icons = Array.new
		@smart_detail_descriptions = Array.new
		## Button and description to jump to save function
		@save_button = nil
		@save_text = nil
		
		button1 = Gtk::EventBox.new.add Gtk::Image.new("reparierenwine.png")
		button2 = Gtk::EventBox.new.add Gtk::Image.new("reparierenorange.png")
		button3 = Gtk::EventBox.new.add Gtk::Image.new("reparierenpurple.png")
		back = Gtk::EventBox.new.add Gtk::Image.new("cancel.png")
		
		text1 = Gtk::Label.new
		text1.width_request = 380
		text1.wrap = true
		text1.set_markup("<span foreground='white'>Windows startet zwar, stürzt aber nach einer Weile ab oder friert ein.</span>")
		
		text2 = Gtk::Label.new
		text2.width_request = 380
		text2.wrap = true
		text2.set_markup("<span foreground='white'>Windows startet nicht, es erscheint stattdessen die Fehlermeldung <b>\"NTLDR fehlt\"</b></span>")
		
		text3 = Gtk::Label.new
		text3.width_request = 380
		text3.wrap = true
		text3.set_markup("<span foreground='white'>Windows startet nicht, es erscheinen Fehlermeldungen wie <b>\"No operating system found\"</b>, <b>\"Kein System gefunden\"</b> oder <b>\"Please insert a bootable media\"</b>.</span>")
		
		text4 = Gtk::Label.new
		text4.width_request = 250
		text4.wrap = true
		text4.set_markup("<span color='white'>Klicken Sie hier, um abzubrechen und zum Startbildschirm zurückzukehren.</span>")
		
		fixed = Gtk::Fixed.new
		fixed.put(button1, 0, 0)
		fixed.put(button2, 0, 130)
		fixed.put(button3, 0, 260)
		fixed.put(back, 852, 332)
		
		fixed.put(text1, 130, 50)
		fixed.put(text2, 130, 180)
		fixed.put(text3, 130, 290)
		fixed.put(text4, 605, 338)
		
		@layers[0] = fixed
		extlayers["winfixstart"] = fixed 
		
		button.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["winfixstart"].show_all
			end
		}
		back.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["start"].show_all
			end
		}
		button1.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				@smart_quick_icons, @smart_quick_descriptions, @smarttab = fill_smart_tab(@smart_quick_icons, @smart_quick_descriptions, @smarttab)
				extlayers["winfixsmart"].show_all
			end
		}
		button2.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["winfixntldr"].show_all
			end
		}
		button3.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["winfixmssys"].show_all
			end
		}
		
		#### Panel for NTLDR ...
		
		ntldrpane = Gtk::Fixed.new
		ntldrtext = Gtk::Label.new
		ntldrtext.width_request = 640
		ntldrtext.wrap = true
		ntldrtext.set_markup("<span foreground='white'>Die Meldung <b>\"NTLDR fehlt\"</b> kann verschiedene Ursachen haben. Meist lässt sich das Problem aber leicht beheben. Da die Notfall-DVD dies nicht automatisch erledigen kann, müssen Sie selbst tätig werden. Befolgen Sie dazu einfach die entsprechende Anleitung für Ihre Windows-Version. Diese können Sie sich hier anzeigen lassen oder auf einem anderen Computer ausdrucken. In diesem Fall finden Sie die Hilfetexte im Ordner \"Anleitungen\" auf der CD. Bevor Sie die Anleitung durchführen, starten Sie Ihren Computer neu.</span>")
		textxp = Gtk::Label.new
		textxp.width_request = 120
		textxp.wrap = true
		textxp.set_markup("<span foreground='white'>Anleitung für Windows XP anzeigen</span>")
		text7 = Gtk::Label.new
		text7.width_request = 120
		text7.wrap = true
		text7.set_markup("<span foreground='white'>Anleitung für Windows Vista und höher anzeigen</span>")
		manualxp = Gtk::EventBox.new.add Gtk::Image.new("pdfpurple.png")
		manual7 = Gtk::EventBox.new.add Gtk::Image.new("pdforange.png")
		checkhd = Gtk::EventBox.new.add Gtk::Image.new("reparierenwine.png")
		checktext = Gtk::Label.new
		checktext.width_request = 510
		checktext.wrap = true
		checktext.set_markup("<span foreground='white'>Falls die Anleitung bei Ihnen nicht hilft und die Probleme weiterhin bestehen, kann auch ein Festplattenproblem vorliegen. Klicken Sie hier zum Überprüfen aller Festplatten.</span>")
		back2 = Gtk::EventBox.new.add Gtk::Image.new("cancel.png")
		text5 = Gtk::Label.new
		text5.width_request = 250
		text5.wrap = true
		text5.set_markup("<span color='white'>Klicken Sie hier, um abzubrechen und zum Startbildschirm zurückzukehren.</span>")
		
		ntldrpane.put(ntldrtext, 0, 0)
		ntldrpane.put(manualxp, 0, 130)
		ntldrpane.put(manual7, 260, 130)
		ntldrpane.put(textxp, 130, 162)
		ntldrpane.put(text7, 390, 162)
		ntldrpane.put(checkhd, 0, 260)
		ntldrpane.put(checktext, 130, 292)
		ntldrpane.put(back2, 852, 332)
		ntldrpane.put(text5, 605, 338)
		
		@layers[1] = ntldrpane
		extlayers["winfixntldr"] = ntldrpane 
		
		back2.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["start"].show_all
			end
		}
		manualxp.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				TileHelpers.open_pdf("Anleitungen/XP-Startprobleme.pdf")
			end
		}
		manual7.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				TileHelpers.open_pdf("Anleitungen/Vista- und 7-Reparatur.pdf")
			end
		}
		checkhd.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				@smart_quick_icons, @smart_quick_descriptions, @smarttab = fill_smart_tab(@smart_quick_icons, @smart_quick_descriptions, @smarttab)
				extlayers["winfixsmart"].show_all
			end
		}
		
		#### Panel for SMART check
		
		smartpanel = create_smart_panel(extlayers)
		extlayers["winfixsmart"] = smartpanel 
		@layers[2] = smartpanel 
		
		#### Panel for running SMART check
		smartcheck = create_smart_running(extlayers)
		extlayers["winfixcheck"] = smartcheck 
		@layers[3] = smartcheck
		
		#### Panel for SMART summary
		smartsummary = create_smart_summary(extlayers)
		extlayers["winfixsummary"] = smartsummary 
		@layers[4] = smartsummary
		
		#### Panel for ms-sys
		mssyspanel = create_mssys_panel(extlayers)
		extlayers["winfixmssys"] = mssyspanel 
		@layers[5] = mssyspanel
		
		#### Panel for ms-sys done
		mbrdonepanel = create_mbrdone_panel(extlayers)
		extlayers["winfixmbrdone"] = mbrdonepanel 
		@layers[6] = mbrdonepanel
		
	end
	
	attr_reader :layers
	
	def create_ntldr_panel
		
	end
	
	def create_smart_panel(extlayers)
		fixed = Gtk::Fixed.new
		TileHelpers.place_back(fixed, extlayers)
		button1 = Gtk::EventBox.new.add Gtk::Image.new("reparierenneutral.png")
		fixed.put(button1, 0, 0)
		smarttext = TileHelpers.create_label("<b>SMART-Prüfung - Festplattentest</b>\n\nOft ist eine defekte Festplatte Ursache des Problems. Hier sehen Sie die von den Festplatten Ihres Computers mitgeteilten Zustände. Um präzisere Informationen zu erhalten, empfehlen wir Ihnen einen vollständigen Test aller Festplatten (SMART-Selbsttest) zu starten. Dieser kann mehrere Stunden dauern.", 760)
		fixed.put(smarttext, 130, 0)
		text5 = Gtk::Label.new
		text5.width_request = 230
		text5.wrap = true
		text5.set_markup("<span color='white'>Klicken Sie hier, um einen vollständigen SMART-Test zu starten.</span>")
		forw  = Gtk::EventBox.new.add Gtk::Image.new("forward.png")
		fixed.put(forw, 853, 282)
		fixed.put(text5, 605, 288)
		smartscroll = Gtk::ScrolledWindow.new
		smartscroll.height_request = 175
		smartscroll.width_request = 760
		@smarttab = Gtk::Table.new(1, 2, false)
		@smarttab.column_spacings = 10 
		@smarttab.row_spacings = 10 
		smartalign = Gtk::Alignment.new(0, 0, 0.1, 0.1)
		smartalign.add @smarttab
		smartscroll.add_with_viewport smartalign
		smartscroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) 
		fixed.put(smartscroll, 130, 96)
		anc = @smarttab.get_ancestor(Gtk::Viewport)
		anc.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse('#005163') )
		forw.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["winfixcheck"].show_all
				# TileHelpers.success_dialog("DUMMY: In der finalen Version läuft jetzt der Fortschrittsbalken durch, dann springt der Assistent auf den nächsten Bildschirm.", "DUMMY-Hinweis")
				res = run_smart_test(@smart_progress, @break_smart_test)
				extlayers["winfixcheck"].hide_all
				if res == true
					@smart_detail_icons, @smart_detail_descriptions, @smartsumtab, errors = fill_smart_tab(@smart_detail_icons, @smart_detail_descriptions, @smartsumtab, true)
					extlayers["winfixsummary"].show_all
					[ @save_button, @save_text ].each { |w| w.hide_all }
					if errors == true
						[ @save_button, @save_text ].each { |w| w.show_all }
					end
				end
			end
		}
		return fixed
	end
	
	def create_smart_running(extlayers)
		fixed = Gtk::Fixed.new
		@break_smart_test = TileHelpers.place_back(fixed, extlayers, false)
		button1 = Gtk::EventBox.new.add Gtk::Image.new("reparierenneutral.png")
		fixed.put(button1, 0, 0)
		@smart_progress = Gtk::ProgressBar.new
		@smart_progress.width_request = 510
		@smart_progress.height_request = 32
		fixed.put(@smart_progress, 130, 98)
		progresstext = TileHelpers.create_label("<b>SMART-Prüfung läuft</b>\n\nDie ausgewählten Laufwerke werden jetzt untersucht. Das kann je nach Laufwerksgröße mehrere Stunden dauern. Der Fortschrittsbalken gibt Auskunft über die verbleibende Zeit.", 510)
		fixed.put(progresstext, 130, 0)
		@smart_progress.text = "SMART-Selbsttest wird gestartet"
		return fixed
	end
	
	def create_smart_summary(extlayers)
		fixed = Gtk::Fixed.new
		TileHelpers.place_back(fixed, extlayers)
		button1 = Gtk::EventBox.new.add Gtk::Image.new("reparierenneutral.png")
		fixed.put(button1, 0, 0)
		smarttext = TileHelpers.create_label("<b>Ergebnis der SMART-Prüfung</b>\n\nOft ist eine defekte Festplatte Ursache des Problems. Hier sehen Sie die von den Festplatten Ihres Computers mitgeteilten Zustände nach dem soeben durchgeführten Selbsttest.", 760)
		fixed.put(smarttext, 130, 0)
		@save_text = TileHelpers.create_label("Klicken Sie hier, um jetzt eine Datensicherung durchzuführen.", 230)
		@save_button  = Gtk::EventBox.new.add Gtk::Image.new("forward.png")
		fixed.put(@save_button, 853, 282)
		fixed.put(@save_text, 605, 288)
		smartscroll = Gtk::ScrolledWindow.new
		smartscroll.height_request = 175
		smartscroll.width_request = 760
		# Now some dummy content: 
		# vbox = Gtk::VBox.new
		# 
		@smartsumtab = Gtk::Table.new(2, 2, false)
		@smartsumtab.column_spacings = 10 
		@smartsumtab.row_spacings = 10 
		smartalign = Gtk::Alignment.new(0, 0, 0.1, 0.1)
		smartalign.add @smartsumtab
		smartscroll.add_with_viewport smartalign
		smartscroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) 
		fixed.put(smartscroll, 130, 88)
		anc = @smartsumtab.get_ancestor(Gtk::Viewport)
		anc.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse('#005163') )
		@save_button.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["rescuestart"].show_all
			end
		}
		return fixed
		
	end
	
	def create_mssys_panel(extlayers)
		fixed = Gtk::Fixed.new
		TileHelpers.place_back(fixed, extlayers)
		button1 = Gtk::EventBox.new.add Gtk::Image.new("reparierenneutral.png")
		fixed.put(button1, 0, 0)
		mssystext = TileHelpers.create_label("<b>Fehlender Bootsektor</b>\n\nWindows startet nicht, es erscheinen Fehlermeldungen wie <b>\"No operating system found\"</b>, <b>\"Kein System gefunden\"</b> oder <b>\"Please insert a bootable media\"</b>. Fehlermeldungen wie diese erscheinen, wenn der Startbereich der Festplatte beschädigt ist. Dies kann die Notfall-DVD beheben.", 510)
		fixed.put(mssystext, 130, 0)
		text5 = Gtk::Label.new
		text5.width_request = 230
		text5.wrap = true
		text5.set_markup("<span color='white'>Klicken Sie hier, um den Startbereich der Festplatte zu reparieren.</span>")
		forw  = Gtk::EventBox.new.add Gtk::Image.new("forward.png")
		fixed.put(forw, 853, 282)
		fixed.put(text5, 605, 288)
		forw.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				fix_mbr
				extlayers["winfixmbrdone"].show_all
			end
		}
		
		return fixed
	end
	
	def create_mbrdone_panel(extlayers)
		fixed = Gtk::Fixed.new
		ntldrtext = TileHelpers.create_label("<b>Die Startinformationen wurden wiederhergestellt</b>\n\n<b>Achtung:</b> Sollte Ihr Windows noch immer nicht starten, müssen Sie es neu installieren. Befolgen Sie dafür die entsprechende Anleitung für Ihre Windows-Version. Diese können Sie sich hier anzeigen lassen oder auf einem anderen Computer ausdrucken. Für diesen Fall finden Sie die Hilfe-Texte im Ordner \"Anleitungen\" auf der CD.", 640)
		
		textxp = Gtk::Label.new
		textxp.width_request = 120
		textxp.wrap = true
		textxp.set_markup("<span foreground='white'>Anleitung für Windows XP anzeigen</span>")
		text7 = Gtk::Label.new
		text7.width_request = 120
		text7.wrap = true
		text7.set_markup("<span foreground='white'>Anleitung für Windows Vista und 7 anzeigen</span>")
		text8 = Gtk::Label.new
		text8.width_request = 120
		text8.wrap = true
		text8.set_markup("<span foreground='white'>Anleitung für Windows 8 anzeigen</span>")
		manualxp = Gtk::EventBox.new.add Gtk::Image.new("pdfpurple.png")
		manual7 = Gtk::EventBox.new.add Gtk::Image.new("pdforange.png")
		manual8 = Gtk::EventBox.new.add Gtk::Image.new("pdfturq.png")
		
		TileHelpers.place_back(fixed, extlayers)
		fixed.put(ntldrtext, 0, 0)
		fixed.put(manualxp, 0, 130)
		fixed.put(manual7, 260, 130)
		fixed.put(textxp, 130, 162)
		fixed.put(text7, 390, 162)
		fixed.put(manual8, 520, 130)
		fixed.put(text8, 650, 162)
		
		manualxp.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				TileHelpers.open_pdf("Anleitungen/XP-Neuinstallation.pdf")
			end
		}
		manual7.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				TileHelpers.open_pdf("Anleitungen/Neuinstallation-Vista-7.pdf")
			end
		}
		manual8.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				TileHelpers.open_pdf("Anleitungen/Windows8-Installation.pdf")
			end
		}
		return fixed
		
	end
	
	##### Fill the first table for smart quick readouts
	
	def fill_smart_tab(icons, descriptions, table, ntfsfix=false)
		icons.each { |i| table.remove i }
		descriptions.each { |i| table.remove i }
		drives = Array.new
		Dir.entries("/sys/block").each { |l|
			drives.push(MfsDiskDrive.new(l, false)) if l =~ /[a-z]$/ 
		}
		# drives.each {|d| puts "#{d.vendor} #{d.model} #{d.human_size} #{d.device}" }
		table.resize(drives.size, 2)
		icons = Array.new
		descriptions = Array.new
		errors = false
		0.upto(drives.size - 1) { |n|
			drive_errors = false
			icon = "smiley_green.png"
			# Create a label for each drive
			d = drives[n]
			label = "<b>#{d.vendor} #{d.model} #{d.human_size} #{d.device}"
			if d.usb == true
				label = label + " (USB)</b>\n"
			else
				label = label + " (SATA/eSATA/IDE)</b>\n"
			end
			smart_support, smart_info, smart_errors = d.error_log
			smart_test_types, smart_test_results, smart_bad_sectors, smart_reallocated, smart_seek_error = d.smart_details
			if smart_support == true 
				label = label + "Dieses Laufwerk verfügt über SMART-Funktionalität. "
			else
				label = label + "Dieses Laufwerk verfügt über keine SMART-Funktionalität. "
			end
			if smart_test_types == "short" 
				label = label + "Es wurden in letzter Zeit nur kurze Selbsttests durchgeführt. "
			elsif smart_test_types == "long" 
				label = label + "Es wurde bereits ein langer Selbsttest durchgeführt. "
			else
				label = label + "Es wurde noch nie ein Selbsttest durchgeführt. "
			end
			if smart_bad_sectors.to_i > 0 || smart_reallocated.to_i > 0 || smart_seek_error.to_i > 0
				label = label + "Das Protokoll des Laufwerkes enthält Hinweise auf defekte/realloziierte Sektoren. "
				errors = true
				drive_errors = true
				icon = "smiley_red.png"
			end
			if smart_test_results =~ /error/ 
				label = label + "Beim Selbsttest wurden Probleme festgestellt, die auf einen baldigen Totalausfall der Festplatte hindeuten. "
				icon = "smiley_red.png"
				errors = true
				drive_errors = true
			end
			if drive_errors == true && ntfsfix == true
				d.partitions.each { |p|
					system("ntfsfix /dev/#{p.device}") if p.fs =~ /ntfs/ 
				}
			end
 			l = TileHelpers.create_label(label, 670)
			descriptions.push l
			table.attach(l, 1, 2, n, n+1)
			img = Gtk::Image.new(icon)
			icons.push img
			table.attach(img, 0, 1, n, n+1)
		}
		return icons, descriptions, table, errors
	end
	
	##### Run the SMART test
	
	def run_smart_test(progbar, breakbutton)
		drives = Array.new
		breaked = false
		Dir.entries("/sys/block").each { |l|
			drives.push(MfsDiskDrive.new(l, true)) if l =~ /[a-z]$/ 
		}
		breakbutton.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				drives.each { |d|
					puts "Cancel test for: #{d.device}"
					system("smartctl -X /dev/#{d.device}")
				}
				breaked = true
			end
			@extlayers.each { |k,v|v.hide_all }
			@extlayers["start"].show_all
		}
		max_duration = -1
		durations = Hash.new
		remaining = Hash.new
		finished = Hash.new
		drives.each { |d|
			system("smartctl -s on /dev/#{d.device}")
			system("smartctl -X /dev/#{d.device}")	
			inside = false
			IO.popen("smartctl -c /dev/#{d.device}") { |l|
				while l.gets
					line = $_.strip
					if line =~ /recommended polling time.*\((.*?)\)/ && inside == true
						durations[d] = $1.to_i
						max_duration = $1.to_i if $1.to_i > max_duration
						break
					end
					inside = true if line =~ /Extended self-test routine/
				end
			}
			finished[d] = false
			system("smartctl -t long /dev/#{d.device}")
		}
		sleepcount = 0
		while finished.has_value?(false) && breaked == false
			remtime = -1
			progbar.pulse
			sleep 0.2
			progbar.pulse
			while (Gtk.events_pending?)
				Gtk.main_iteration
			end
			if sleepcount % 100 == 0
				drives.each { |d|
					test_running = false
					IO.popen("smartctl -c /dev/#{d.device}") { |l|
						while l.gets
							line = $_.strip
							if line =~ /Self-test execution status/ && line =~ /Self-test routine in progress/ 
								test_running = true
							end
							if line =~ /(\d*)% of test remaining/
								remaining[d] = $1.to_i
							end
						end
					}
					finished[d] = true if test_running == false
					this_remaining = remaining[d].to_i * durations[d].to_i / 100 
					remtime = this_remaining if this_remaining > remtime
				}
				progbar.text = "Verbleibende Testdauer: ca. %COUNT% Minuten".gsub("%COUNT%",  remtime.to_s)
			end
			sleepcount += 1
		end
		return false if breaked == true
		return true
	end
	
	def fix_mbr
		drives = Array.new
		Dir.entries("/sys/block").each { |l|
			drives.push(MfsDiskDrive.new(l, false)) if l =~ /[a-z]$/ 
		}
		drives.each { |d|
			# puts d.device unless d.usb 
			gpt = false
			IO.popen("parted -sm /dev/#{d.device} unit B print ") { |l|
				while l.gets
					line = $_.strip
					if line =~ Regexp.new("^/dev/#{d.device}") 
						ltoks = line.split(":") 
						puts ltoks[5]
						gpt = true unless ltoks[5] =~ /msdos/
					end
				end
			}
			has_win_parts = false
			d.partitions.each { |p|
				winpart, winvers = p.is_windows
				has_winparts = true unless winpart.nil? 
			}
			if (d.usb == true)
				puts "Ignore USB drive"
			elsif (gpt == true)
				TileHelpers.error_dialog("Die Platte #{d.device} (#{d.vendor} #{d.model} #{d.human_size}) verwendet eine GUID Partition Table oder ein BSD Disklabel. Bootsektoren auf derart partitionierten Festplatten können mit diesem Werkzeug nicht geschrieben werden.")
			elsif has_win_parts == false
				puts "No windows found on..."
			else
				puts "Write new boot record."
				system("dd if=/usr/share/syslinux/mbr.bin of=" + v.device)
			end
		}
		
		
	end
	
end