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

# Copyright 2011 michiamophil (openmamba user :-)
# Copyright 2011-2024 Silvan Calarco

from subprocess import Popen
import gettext
from PyQt6 import QtCore
from PyQt6.QtCore import QRect
from PyQt6.QtGui import QIcon, QPixmap
from PyQt6.QtWidgets import QApplication, QComboBox, QDialog,\
	QFileDialog, QFrame, QLabel,\
	QLineEdit, QMessageBox, QProgressBar, QPushButton
from gi.repository import GLib
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)

translator = gettext.translation('usbinstall', '/usr/share/locale')
_ = translator.gettext


# Class for dbus_start()
class set_dbus(dbus.service.Object):
	window = None

	def __init__(self, bus_name, window, object_path='/org/openmamba/usbinstall'):
		dbus.service.Object.__init__(self, bus_name, object_path)
		self.window = window

	@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 == "":
			window.end_with_success()
		else:
			window.msg = QMessageBox.critical(window, _("Script Error"), _(errMsg))
			window.end_forced()
		return 0

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


# Contains program's graphic
class MainWindow(QDialog):
	# 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"
	# Background images
	img_background1 = "/usr/share/openmamba/usbinstall/img/back1.png"
	img_background2 = "/usr/share/openmamba/usbinstall/img/back2.png"
	img_background3 = "/usr/share/openmamba/usbinstall/img/back3.png"
	# Global path
	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()

	# Call when is called create_window()
	def __init__(self):
		super().__init__()
		self.add_elements()
		self.add_events()

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

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

	# Events handler
	def add_events(self):
		self.b_next.clicked.connect(self.go_on)
		self.b_back.clicked.connect(self.go_back)
		self.b_open_file.clicked.connect(self.choose_file)
		self.b_help.clicked.connect(self.manage_cloud)
		self.b_cloud.clicked.connect(self.close_cloud)
		self.b_refresh.clicked.connect(self.get_list)
		self.c_usb.activated.connect(self.set_usb)

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

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

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

	# Event n°4
	def manage_cloud(self):
		if self.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 self.step == 2:
			aiuto = _("Choose an usb key for the installation.                    <span style=\"color:red;\">All data will be lost</span>")
		self.l_cloud.setText(aiuto)
		self.cloud_show()

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

	# Event n°6
	def get_list(self):
		# Clear array and comboBox
		self.a_path_dev.clear()
		self.a_dev_model.clear()
		self.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'):
				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:
					self.a_path_dev.append(device)
					self.a_dev_model.append(vendor + " " + model)
					# And add element to comboBox
					self.c_usb.addItem(self.a_dev_model[i] + " (" +
						"{0:.2f}".format(size / 1024.0 / 1024.0 / 1024.0) + " GiB)")
					i += 1

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

	# Event n°7
	def set_usb(self, i):
		self.path_dev  = self.a_path_dev[i]
		dev_model = self.a_dev_model[i]

	# Show all cloud objects
	def cloud_show(self):
		self.i_cloud.show()
		self.b_cloud.show()
		self.l_cloud.show()

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

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

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

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

	def step2(self):
		self.set_objs(self.img_background2,
				_("Step 2/3 - choice of USB key"), _("Choose the USB key")+":")
		self.b_back.setText(_("Back"))
		self.obj_chang_hide()
		self.list_obj2()
		self.get_list()	# Get available devices

	def step3(self):
		self.set_objs(self.img_background3,
			_("Step 3/3 - installation of openmamba to USB"),
			_("Installation in progress..."))
		# Hide all unnecessary objects
		self.obj_chang_hide()
		self.b_help.hide()
		self.line.hide()
		self.b_back.hide()
		self.b_next.hide()
		# Show the progressBar
		self.p_install.show()
		bus_name = dbus.service.BusName('org.openmamba.usbinstall', bus=dbus.SystemBus())
		self.dbus_loop = set_dbus(bus_name, self)
		self.bus = dbus.SystemBus(mainloop=self.dbus_loop)

		print("calling pkexec /usr/sbin/usbinstall " + self.path_dev + " " + self.path_iso + " -d")
		self.process_sh = Popen(["pkexec", "/usr/sbin/usbinstall",
				self.path_dev, self.path_iso, "-d"])

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

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

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

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

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


# Program start
if __name__ == "__main__":
	app = QApplication(['usbinstall'])
	window = MainWindow()
	mainloop = GLib.MainLoop()
	window.step1()
	app.exec()
