#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Copyright 2011 michiamophil (openmamba user :-)
# Copyright 2011-2020 Silvan Calarco
# ChangeLog:
#	(michiamophil on 11/11/11) Rewrited all the graphic code and removed gui.py


from PyQt4 import QtGui, QtCore
from subprocess import Popen
import signal
import gettext	# To translate
from gi.repository import GLib
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
#import dbus.glib

#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
#1)-------------------------------(VARIABILI)----------------------------------------
#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# Icons
img_help    = "/usr/share/icons/gnome/24x24/status/dialog-question.png"
img_fOpen   = "/usr/share/icons/gnome/32x32/actions/document-open.png"
img_refresh = "/usr/share/icons/gnome/24x24/actions/view-refresh.png"
img_pach1   = "/usr/share/openmamba/usbinstall/img/pach1.png"
img_cloud   = "/usr/share/openmamba/usbinstall/img/nuvola.png"
img_exit    = "/usr/share/openmamba/usbinstall/img/exit.png"
window_icon = "/usr/share/icons/hicolor/32x32/apps/mamba.png"
# Global path
path_cpio   = ""
path_iso    = ""
path_dev    = ""
# General vars
dev_model   = ""
a_path_dev  = []	# Array of choosen devices
a_dev_model = []	# Their models.
step        = 1
choose_type = ""	# Type of dialog in choose_file()
#1)###############################(VARIABILI)########################################




#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
#2)-------------------------------(WINDOW CODE)--------------------------------------
#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# Contains program's graphic
class create_window(QtGui.QDialog):
	# Call when is called create_window()
	def __init__(self):
		super(create_window, self).__init__()	# Register eventClose()
		self.add_elements()

	# Write window and objects
	def add_elements(self):
		# Window proprieties
		self.resize(500, 405)
		self.setWindowIcon(QtGui.QIcon(window_icon))
		# Background image
		self.i_back = QtGui.QLabel(self)
		self.i_back.setGeometry(QtCore.QRect(10, 10, 480, 300))
		# Description label
		self.l_descr = QtGui.QLabel(self)
		self.l_descr.setGeometry(QtCore.QRect(10, 317, 351, 31))
		# Orizontal line
		self.line = QtGui.QFrame(self)
		self.line.setGeometry(QtCore.QRect(10, 350, 481, 20))
		self.line.setFrameShape(QtGui.QFrame.HLine)
		self.line.setFrameShadow(QtGui.QFrame.Sunken)
		# "Next" button
		self.b_next = QtGui.QPushButton(self)
		self.b_next.setGeometry(QtCore.QRect(400, 370, 90, 29))
		self.b_next.setText(_("Go on"))
		# "Go Back" button
		self.b_back = QtGui.QPushButton(self)
		self.b_back.setGeometry(QtCore.QRect(300, 370, 90, 29))
		# "Help" button
		self.b_help = QtGui.QPushButton(QtGui.QIcon(img_help), "", self)
		self.b_help.setGeometry(QtCore.QRect(464, 320, 26, 26))
		self.b_help.setIconSize(QtCore.QSize(24, 24))
		self.b_help.setFlat(True)
		# Path line edit
		self.t_file = QtGui.QLineEdit(self)
		self.t_file.setGeometry(QtCore.QRect(212, 321, 222, 25))
		# Pach line edit
		self.pach1 = QtGui.QLabel(self)
		self.pach1.setGeometry(QtCore.QRect(430, 320, 32, 27))
		self.pach1.setPixmap(QtGui.QPixmap(img_pach1))
		# Open file button
		self.b_open_file = QtGui.QPushButton(QtGui.QIcon(img_fOpen), "", self)
		self.b_open_file.setGeometry(QtCore.QRect(431, 320, 31, 27))
		self.b_open_file.setFlat(True)
		# Help dialog image
		self.i_cloud = QtGui.QLabel(self)
		self.i_cloud.setGeometry(QtCore.QRect(289, 200, 211, 121))
		self.i_cloud.setPixmap(QtGui.QPixmap(img_cloud))
		self.i_cloud.hide()
		# Help dialog text
		self.l_cloud = QtGui.QLabel(self)
		self.l_cloud.setGeometry(QtCore.QRect(298, 200, 181, 101))
		self.l_cloud.setOpenExternalLinks(True)
		self.l_cloud.setWordWrap(True)
		self.l_cloud.hide()
		# Help dialog exit
		self.b_cloud = QtGui.QPushButton(QtGui.QIcon(img_exit), "", self)
		self.b_cloud.setGeometry(QtCore.QRect(471, 202, 14, 14))
		self.b_cloud.setFlat(True)
		self.b_cloud.hide()
		# Refresh button
		self.b_refresh = QtGui.QPushButton(QtGui.QIcon(img_refresh), "", self)
		self.b_refresh.setGeometry(QtCore.QRect(435, 320, 26, 26))
		self.b_refresh.setIconSize(QtCore.QSize(24, 24))
		self.b_refresh.setFlat(True)
		# List of usb (combobox)
		self.c_usb = QtGui.QComboBox(self)
		self.c_usb.setGeometry(QtCore.QRect(158, 320, 271, 25))
		# Progressbar
		self.p_install = QtGui.QProgressBar(self)
		self.p_install.setGeometry(QtCore.QRect(10, 354, 480, 36))
		self.p_install.hide()
		# Show the window
		self.show()

	# Intercept close event
	def closeEvent(self, event):
		msg = QtGui.QMessageBox.question(window, _("Attention"), _("Are you sure to abort?"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
		# Exit if was pressed yes button
		if msg == QtGui.QMessageBox.Yes:
			end_forced()
		else:
			event.ignore()
		
#2)###############################(WINDOW CODE)######################################





#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
#3)-----------------------------(EVENTS HANDLER)-------------------------------------
#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# Add SIGNINT event
#signal.signal(signal.SIGINT, zz)

def add_events():
	app.connect(window.b_next,      QtCore.SIGNAL('clicked()'), go_on)			# Event n°1
	app.connect(window.b_back,      QtCore.SIGNAL('clicked()'), go_back)		# Event n°2
	app.connect(window.b_open_file, QtCore.SIGNAL('clicked()'), choose_file)	# Event n°3
	app.connect(window.b_help,      QtCore.SIGNAL('clicked()'), manage_cloud)	# Event n°4
	app.connect(window.b_cloud,     QtCore.SIGNAL('clicked()'), close_cloud)	# Event n°5
	app.connect(window.b_refresh,   QtCore.SIGNAL('clicked()'), get_list)		# Event n°6
	app.connect(window.c_usb,       QtCore.SIGNAL("activated(int)"), set_usb)	# Event n°7

# Event n°1
def go_on():
	global step, path_cpio, path_iso, path_dev, dev_model
	cloud_hide()
	# Step 1 to 2
	if step == 1:
		if path_iso == "":
			msg_warn(_("An openmamba's iso is required"))
			return
		else:
			step += 1
			step2()
	# Step 2 to 3
	elif step == 2:
		if path_dev == "":
			msg_warn(_("You must choose a usb key"))
			return
		else:
			msg = QtGui.QMessageBox.question(window, _("Attention"), _("All data on the key ") + dev_model + _(" will be lost. \n")+ _("Are you sure to continue?"), 
				QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
			# Go on only if was pressed yes button
			if msg == QtGui.QMessageBox.Yes:
				step += 1
				step3()
			else:
				return

# Event n°2
def go_back():
	global step
	step -= 1
	cloud_hide()
	obj_chang_hide()
	# Step 1 to 0 -> exit
	if step == 0:
		app.exit(1)
		print (_("Application terminated by pressing \"cancel\" button"))
	# Step 2 to 1
	elif step == 1:
		step1()
	# Step 3 to 2
	elif step == 2:
		step2()

# Event n°3
def choose_file():
	global path_cpio, path_iso, choose_type
	# if step is 1
	if choose_type == "iso":
		nfileName = QtGui.QFileDialog.getOpenFileName(window, _("Choose the iso file"), "/home", "File .iso (*.iso)")
		path_iso = nfileName
		window.t_file.setText(nfileName)

# Event n°4
def manage_cloud():
	global step
	if step == 1:
		aiuto = _("The file .iso contains the operating system. You can download it from <a style=\"color: green; \"href=\"http://www.openmamba.org/distribution/download.html?lang=en\">here</a>")
	elif step == 2:
		aiuto = _("Choose an usb key for the installation.                    <span style=\"color:red;\">All data will be lost</span>")
	window.l_cloud.setText(aiuto)
	cloud_show()

# Event n°5
def close_cloud():
	cloud_hide()

# Event n°6 
def get_list():
	global a_path_dev, a_dev_model
	
	# Clear array and comboBox
	a_path_dev  = []
	a_dev_model = []
	window.c_usb.clear()
	i = 0
	
	# Get list form dbus
	bus = dbus.SystemBus()
	ud_manager_obj = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2')
	ud_manager = dbus.Interface(ud_manager_obj, 'org.freedesktop.DBus.ObjectManager')
	for k,v in ud_manager.GetManagedObjects().items():
		device_block = v.get('org.freedesktop.UDisks2.Block', {})
		device_partitiontable = v.get('org.freedesktop.UDisks2.PartitionTable', {})
		device = device_block.get('Device')
		drive = device_block.get('Drive')
		if not device or not drive or not device_partitiontable:
		    continue
		device = bytearray(device).replace(b'\x00', b'').decode('utf-8')
		device_drive = bus.get_object('org.freedesktop.UDisks2', drive)
		try:
			removable = device_drive.Get('org.freedesktop.UDisks2.Drive', 'Removable', dbus_interface='org.freedesktop.DBus.Properties')
		except:
			continue
		size = device_drive.Get('org.freedesktop.UDisks2.Drive', 'Size', dbus_interface='org.freedesktop.DBus.Properties')
		if removable and size > 891289600 and not device_block.get('ReadOnly') and \
				not device_block.get('HintSystem') and device_block.get('HintPartitionable'):
			connectionbus = device_drive.Get('org.freedesktop.UDisks2.Drive', 'ConnectionBus', dbus_interface='org.freedesktop.DBus.Properties')
			model = device_drive.Get('org.freedesktop.UDisks2.Drive', 'Model', dbus_interface='org.freedesktop.DBus.Properties')
			vendor = device_drive.Get('org.freedesktop.UDisks2.Drive', 'Vendor', dbus_interface='org.freedesktop.DBus.Properties')
			if removable:
				a_path_dev.append(device)
				a_dev_model.append(vendor + " " + model)
				# And add element to comboBox
				window.c_usb.addItem(a_dev_model[i] + " (" + "{0:.2f}".format(size / 1024.0 / 1024.0 / 1024.0) + " GiB)")
				i += 1

	# First dev is default
	if len(a_path_dev) != 0:
		set_usb(0)
	else:
		print (_("No usb available > 1 Gb"))

# Event n°7
def set_usb(i):
	global path_dev, dev_model, a_path_dev, a_dev_model
	path_dev  = a_path_dev[i]
	dev_model = a_dev_model[i]
#3)#############################(EVENTS HANDLER)#####################################



#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
#4)-------------------------------(DBUS CODE)----------------------------------------
#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# Class for dbus_start()
class set_dbus(dbus.service.Object):
	def __init__(self, bus_name, object_path='/org/openmamba/usbinstall'):
		dbus.service.Object.__init__(self, bus_name, object_path)

	@dbus.service.method('org.openmamba.usbinstall')
	def Set_progressBar(self, p):
		window.p_install.setProperty("value", int(p))
		return 0

	@dbus.service.method('org.openmamba.usbinstall')
	def Exit(self, errMsg):
		if errMsg == "":
			end_with_success()
		else:
			msg = QtGui.QMessageBox.critical(window, _("Script Error"), _(errMsg))
			end_forced()
		return 0

	@dbus.service.method('org.openmamba.usbinstall')
	def Set_description(self, descr):
		window.l_descr.setText(descr)
		return 0

def dbus_start():
	bus_name = dbus.service.BusName('org.openmamba.usbinstall', bus=dbus.SystemBus())
	dbus_loop = set_dbus(bus_name)
	bus = dbus.SystemBus(mainloop=dbus_loop)
#4)###############################(DBUS CODE)########################################




#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
#5)------------------------------(PROGRAM CODE)--------------------------------------
#=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
# Show all cloud objects
def cloud_show():
	window.i_cloud.show()
	window.b_cloud.show()
	window.l_cloud.show()

# Hide all cloud objects
def cloud_hide():
	window.i_cloud.hide()
	window.b_cloud.hide()
	window.l_cloud.hide()

#  Warning dialog abbreviation
def msg_warn(txt):
	msg = QtGui.QMessageBox.warning(window, _("Attention"), txt)

# Called when b_next was pressed
def set_objs(i_back, title, descr):
	
	window.i_back.setPixmap(QtGui.QPixmap(i_back))
	window.setWindowTitle(title)
	window.l_descr.setText(descr)


def step1():
	global choose_type, path_iso
	choose_type = "iso"
	path_iso    = ""
	window.t_file.setText(path_iso)	# Clean path box
	window.b_back.setText(_("Cancel"))
	set_objs("/usr/share/openmamba/usbinstall/img/back1.png", _("Step 1/3 - choice of ISO file"), _("Choose the openmamba ISO file")+":")
	obj_chang_hide()
	list_obj1()	# Show a set of object

def step2():
	set_objs("/usr/share/openmamba/usbinstall/img/back2.png", _("Step 2/3 - choice of USB key"), _("Choose the USB key")+":")
	obj_chang_hide()
	list_obj2()
	get_list()	# Get available devices

def step3():
	global process_sh
	set_objs("/usr/share/openmamba/usbinstall/img/back3.png", _("Step 3/3 - installation of openmamba to USB"), _("Installation in progress..."))
	# Hide all unnecessary objects
	obj_chang_hide()
	window.b_help.hide()
	window.line.hide()
	window.b_back.hide()
	window.b_next.hide()
	# Show the progressBar
	window.p_install.show()
	dbus_start()
	process_sh = Popen(["pkexec", "/usr/sbin/usbinstall", path_dev, path_iso, "-d"])

# Show a list of object (step 1)
def list_obj1():
	window.t_file.show()
	window.pach1.show()
	window.b_open_file.show()

# Show a list of object (step 2)
def list_obj2():
	window.c_usb.show()
	window.b_refresh.show()

# Hide all object that when b_next or b_back was pressed are changing
def obj_chang_hide():
	window.t_file.hide()
	window.pach1.hide()
	window.b_open_file.hide()
	window.c_usb.hide()
	window.b_refresh.hide()

def end_with_success():
	global process_sh, mainloop
	msg = QtGui.QMessageBox.information(window, _("Information"), _("Installation process completed"))
	mainloop.quit()
	app.exit(0)
	print (_("Installation process completed"))

def end_forced():
	global process_sh, mainloop
	mainloop.quit()
	#print _("Installation aborted")
	app.exit(1)

# Program start
app = QtGui.QApplication(['usbinstall'])
gettext.install('usbinstall', '/usr/share/locale', 1)
window = create_window()
mainloop = GLib.MainLoop()

def run():
	add_events()
	step1()
	return app.exec_()
if __name__ == "__main__":
	run()
#5)##############################(PROGRAM CODE)######################################
