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

class DeleteScreen
	
	def initialize(extlayers, button)
		@layers = Array.new
		fixed = Gtk::Fixed.new
		#### Was the deletion cancelled?
		@deletecancelled = false
		#### Table for deletion of single partitions
		@parttable = nil
		# list of drives
		@partdrives = Array.new
		# shown partitions
		@partparts = Array.new
		# headers
		@partheaders = Array.new
		# checkboxes
		@partchecks = Array.new
		# labels
		@partlabels = Array.new
		#### Table for deletion of complete drives
		@drivetable = nil
		@drivedrives = Array.new
		@drivechecks = Array.new
		@drivelabels = Array.new
		#### Progress bar for deletion
		@deleteprogress = Gtk::ProgressBar.new
		
		greentile = Gtk::EventBox.new.add Gtk::Image.new("deletelightgreen.png")
		orangetile = Gtk::EventBox.new.add Gtk::Image.new("deleteorange.png")
		reinsttext = TileHelpers.create_label("<b>Schnellreinigung</b>\n\nKlicken Sie hier, um die Festplatte für eine frische Windows-Installation optimal vorzubereiten. Gelöscht werden bei dieser Methode nur die Inhalte der Windows-Partitionen.", 380)
		secdeltext = TileHelpers.create_label("<b>Tiefenreinigung</b>\n\nKlicken Sie hier, falls Sie den Computer verkaufen oder ein Laufwerk entsorgen möchten. Bei dieser Methode werden alle Daten auf der Festplatte unwiederbringlich vernichtet.", 380)
		
		fixed.put(orangetile, 0, 0)
		fixed.put(greentile, 0, 130)
		fixed.put(secdeltext, 130, 0)
		fixed.put(reinsttext, 130, 130) 
		
		TileHelpers.place_back(fixed, extlayers)
		
		### Layer for deletion of single partitions
		
		green2tile = Gtk::EventBox.new.add Gtk::Image.new("deletegreen.png")
		fixeddelete = Gtk::Fixed.new
		TileHelpers.place_back(fixeddelete, extlayers)
		delscroll = Gtk::ScrolledWindow.new
		delscroll.height_request = 160
		delscroll.width_request = 760
		delscroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) 
		fixeddelete.put(green2tile, 0, 0)
		fixeddelete.put(delscroll, 130, 100)
		drivetext = TileHelpers.create_label("<b>Auswahl der Laufwerke</b>\n\nWählen Sie hier die Partitionen aus, auf denen sich Windows befindet. Die anderen Partitionen sollten Sie nicht auswählen, da sonst persönliche Dateien und Sicherungen verloren gehen könnten.", 510)
		fixeddelete.put(drivetext, 130, 0)
		# Table for drives
		@parttable = Gtk::Table.new(2, 11, false)
		align = Gtk::Alignment.new(0, 0, 0.1, 0.1)
		align.add @parttable
		delscroll.add_with_viewport align
		# delscroll.add drivetab
		anc = @parttable.get_ancestor(Gtk::Viewport)
		anc.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse('#005163') )
		anc.modify_fg(Gtk::STATE_NORMAL, Gdk::Color.parse('#005163') )
		anc.modify_text(Gtk::STATE_NORMAL, Gdk::Color.parse('#ffffff') )
		# puts anc.to_s
		# Forward button:
		text5 = Gtk::Label.new
		text5.width_request = 250
		text5.wrap = true
		text5.set_markup("<span color='white'>Klicken Sie hier, um die ausgewählten Laufwerke zu löschen.</span>")
		forw  = Gtk::EventBox.new.add Gtk::Image.new("forward.png")
		fixeddelete.put(forw, 853, 282)
		fixeddelete.put(text5, 605, 288)
		# End forward button
		forw.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				if TileHelpers.yes_no_dialog("Möchten Sie die ausgewählten Laufwerke wirklich komplett überschreiben?", "Überschreiben bestätigen")
					extlayers.each { |k,v|v.hide_all }
					extlayers["deleteprogress"].show_all
					# run dd to delete
					run_deletion(@deleteprogress, false)
					unless @deletecancelled == true
						TileHelpers.success_dialog("Die ausgewählten Laufwerke wurden erfolgreich gelöscht. Klicken Sie auf \"OK\" um zum Startbildschirm zurückzukehren.", "Laufwerke gelöscht")
					else
						TileHelpers.success_dialog("Der Löschvorgang wurde abgebrochen, die ausgewählten Laufwerke wurden nur teilweise gelöscht. Klicken Sie auf \"OK\" um zum Startbildschirm zurückzukehren.", "Laufwerke gelöscht")
					end
					extlayers.each { |k,v|v.hide_all }
					extlayers["start"].show_all
				end
			end
		}
		
		
		### Layer for progress of deletion
		
		fixedprogress = Gtk::Fixed.new
		winetile = Gtk::EventBox.new.add Gtk::Image.new("deletegreen.png")
		fixedprogress.put(winetile, 0, 0)
		cancelbutton = TileHelpers.place_back(fixedprogress, extlayers, false)
		@deleteprogress.width_request = 510
		@deleteprogress.height_request = 32
		fixedprogress.put(@deleteprogress, 130, 120)
		progresstext = TileHelpers.create_label("<b>Löschvorgang läuft</b>\n\nDie ausgewählten Laufwerke werden jetzt überschrieben. Das kann je nach Laufwerksgröße, Schreibgeschwindigkeit und gewählter Löschmethode wenige Minuten bis mehrere Stunden dauern. Der Fortschrittsbalken gibt Auskunft über die verbleibende Zeit.", 510)
		fixedprogress.put(progresstext, 130, 0)
		
		cancelbutton.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				@deletecancelled = true
			end
		}
		
		button.signal_connect('button-release-event') { |x, y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				extlayers["deletestart"].show_all
			end
		}
		greentile.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				@partdrives, @partparts, @partheaders, @partchecks, @partlabels = fill_part_tab(@parttable, @partheaders, @partchecks, @partlabels)
				extlayers["deletepartition"].show_all
			end
		}
		orangetile.signal_connect('button-release-event') { |x,y|
			if y.button == 1 
				extlayers.each { |k,v|v.hide_all }
				@drivedrives, @drivechecks, @drivelabels = fill_drive_tab(@drivetable, @drivechecks, @drivelabels)
				extlayers["deletedrive"].show_all
			end
		}
		
		extlayers["deletestart"] = fixed
		@layers[0] = fixed
		extlayers["deletepartition"] = fixeddelete
		@layers[1] = fixeddelete
		extlayers["deleteprogress"] = fixedprogress
		@layers[2] = fixedprogress
		
		drivelayer = create_drive_tab(extlayers)
		extlayers["deletedrive"] = drivelayer
		@layers[3] = drivelayer
		
	end
	attr_reader :layers
	
	def create_drive_tab(extlayers)
		orangetile = Gtk::EventBox.new.add Gtk::Image.new("deletegreen.png")
		fixed = Gtk::Fixed.new
		TileHelpers.place_back(fixed, extlayers)
		delscroll = Gtk::ScrolledWindow.new
		delscroll.height_request = 170
		delscroll.width_request = 760
		delscroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ) 
		fixed.put(orangetile, 0, 0)
		fixed.put(delscroll, 130, 90)
		drivetext = TileHelpers.create_label("<b>Auswahl der Laufwerke</b>\n\nWählen Sie hier die Festplatten aus, die Sie überschreiben möchten. Daten auf den gelöschten Festplatten können nach gegenwärtigem Stand der Technik nicht wiederhergestellt werden.", 510)
		fixed.put(drivetext, 130, 0)
		@drivetable = Gtk::Table.new(2, 2, false)
		align = Gtk::Alignment.new(0, 0, 0.1, 0.1)
		align.add @drivetable
		delscroll.add_with_viewport align
		anc = @drivetable.get_ancestor(Gtk::Viewport)
		anc.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse('#005163') )
		text5 = Gtk::Label.new
		text5.width_request = 250
		text5.wrap = true
		text5.set_markup("<span color='white'>Klicken Sie hier, um die ausgewählten Laufwerke zu löschen.</span>")
		forw  = Gtk::EventBox.new.add Gtk::Image.new("forward.png")
		forw.signal_connect('button-release-event') { |x,y|
			if y.button == 1
				if TileHelpers.yes_no_dialog("Möchten Sie die ausgewählten Laufwerke wirklich komplett überschreiben?", "Überschreiben bestätigen")
					extlayers.each { |k,v|v.hide_all }
					extlayers["deleteprogress"].show_all
					# run dd to delete
					run_deletion(@deleteprogress, true)
					unless @deletecancelled == true
						TileHelpers.success_dialog("Die ausgewählten Laufwerke wurden erfolgreich gelöscht. Klicken Sie auf \"OK\" um zum Startbildschirm zurückzukehren.", "Laufwerke gelöscht")
					else
						TileHelpers.success_dialog("Der Löschvorgang wurde abgebrochen, die ausgewählten Laufwerke wurden nur teilweise gelöscht. Klicken Sie auf \"OK\" um zum Startbildschirm zurückzukehren.", "Laufwerke gelöscht")
					end
					extlayers.each { |k,v|v.hide_all }
					extlayers["start"].show_all
				end
			end
		}
		fixed.put(forw, 853, 282)
		fixed.put(text5, 605, 288)
		return fixed
	end
	
	
	def fill_part_tab(table, oldheaders, oldchecks, oldlabels)
		TileHelpers.umount_all
		table.row_spacings = 4
		parts = Array.new
		headers = Array.new
		drives = Array.new
		checks = Array.new
		labels = Array.new
		Dir.entries("/sys/block").each { |l|
			drives.push(MfsDiskDrive.new(l, false)) if l =~ /[a-z]$/ 
		}
		oldheaders.each { |h| table.remove h }
		oldlabels.each{ |l| table.remove l } 
		oldchecks.each{ |c| table.remove c } 
		
		table.resize(drives.size * 100, 2)
		rowcount = 0
		drives.each { |d|
			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
			l = TileHelpers.create_label(label, 670)
			l.height_request = 17
			headers.push l
			table.attach(l, 0, 2, rowcount, rowcount+1)
			rowcount += 1
			partcount = 0
			d.partitions.each { |p|
				unless p.fs.to_s.strip == ""
					pltext = "#{p.device} - #{p.human_size} - #{p.fs}"
					mnt, opts = p.mount_point 
					pltext = pltext + " - wird benutzt!" unless mnt.nil? 
					pl = TileHelpers.create_label(pltext, 650)
					cb = Gtk::CheckButton.new
					cb.sensitive = false unless mnt.nil? 
					labels.push pl
					checks.push cb
					parts.push p.device
					table.attach(pl, 1, 2, rowcount, rowcount+1)
					table.attach(cb, 0, 1, rowcount, rowcount+1)
					rowcount += 1
					partcount += 1
				end
			}
		}
		table.resize(rowcount, 2)
		return drives, parts, headers, checks, labels
	end
	
	def fill_drive_tab(table, oldchecks, oldlabels)
		TileHelpers.umount_all
		table.row_spacings = 4
		checks = Array.new
		labels = Array.new
		drives = Array.new
		Dir.entries("/sys/block").each { |l|
			drives.push(MfsDiskDrive.new(l, false)) if l =~ /[a-z]$/ 
		}
		oldlabels.each{ |l| table.remove l } 
		oldchecks.each{ |c| table.remove c } 
		table.resize(drives.size, 2)
		rowcount = 0
		drives.each { |d|
			label = "<b>#{d.vendor} #{d.model} - #{d.human_size} - #{d.device}"
			if d.usb == true
				label = label + " (USB)"
			else
				label = label + " (SATA/eSATA/IDE)"
			end
			mnt = d.mounted 
			label = label + " - wird benutzt" if mnt == true
			label = label + "</b>\n"
			l = TileHelpers.create_label(label, 650)
			l.height_request = 17
			labels.push l
			table.attach(l, 1, 2, rowcount, rowcount+1)
			cb = Gtk::CheckButton.new
			cb.sensitive = false if mnt == true
			checks.push cb
			table.attach(cb, 0, 1, rowcount, rowcount+1)
			rowcount += 1
		}
		return drives, checks, labels
	end
	
	# fulldisks == true means to delete whole drives
	# fulldisks == false means to delete selected partitions only
	def run_deletion(progress, fulldisks)
		@deletecancelled = false
		delete_devices = Array.new
		# start a new dd process for every 16 chunks of 4 Megabyte = 64MB per process
		chunk_size = 16
		delete_chunks = Hash.new
		if fulldisks == true
			puts "Deletion of complete disks requested"
			0.upto(@drivedrives.size - 1) { |n|
				delete_devices.push(@drivedrives[n].device) if @drivechecks[n].active?
				delete_chunks[@drivedrives[n].device] = ( @drivedrives[n].size / 64 / 1024 ** 2 ) + 1  
			}
		else
			puts "Deletion of partitions requested"
			chunk_size = 2
			0.upto(@partparts.size - 1) { |n|
				delete_devices.push(@partparts[n]) if @partchecks[n].active?
				delete_chunks[@partparts[n]] = 16
			}
		end
		puts delete_devices.join(", ") 
		delete_devices.each { |d|
			starttime = Time.now.to_i 
			0.upto(delete_chunks[d] - 1) { |n|
				if @deletecancelled == false
					system "dd if=/dev/zero of=/dev/#{d} bs=4M count=#{chunk_size.to_s} seek=#{n.to_s}" 
					current_time = Time.now.to_i 
					t = "Lösche #{d} " 
					t = t + " - " + (n * 100 / delete_chunks[d]).to_s + "% abgeschlossen" 
					progress.fraction = n.to_f / delete_chunks[d].to_f 
					delta_time = current_time - starttime
					if fulldisks == true && delta_time > 20 && n > 2
						time_per_chunk = delta_time.to_f / n.to_f
						time_remaining = ( delete_chunks[d].to_f - n.to_f ) * time_per_chunk
						minutes_remaining = ( time_remaining + 30.0 ) / 60.0 
						nice_minutes = minutes_remaining.to_i % 60
						nice_hours = minutes_remaining.to_i / 60
						if nice_hours < 1 && nice_minutes < 3
							t = t + " - ca. eine Minute verbleibend"
						elsif nice_hours < 2
							t = t + " - ca. #{minutes_remaining.to_i.to_s} Minuten verbleibend"
						else
							t = t + " - ca. #{nice_hours.to_s} Stunden und #{nice_minutes.to_s} Minuten verbleibend"
						end
					end
					progress.text = t 
					while (Gtk.events_pending?)
						Gtk.main_iteration
					end
				end
			}
		}
		unless @deletecancelled == true
			progress.text = "Löschung abgeschlossen"
			progress.fraction = 1.0
		end
	end
	
end

