#!/usr/bin/python3

# servicetrayicon.py
# 2021-04-18
# by Gernot Walzl

# System tray icon for systemd services

import sys
import subprocess
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QCursor
from PyQt5.QtGui import QIcon
from PyQt5.QtGui import QPainter
from PyQt5.QtGui import QPainterPath
from PyQt5.QtGui import QPen
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QMenu
from PyQt5.QtWidgets import QScrollArea
from PyQt5.QtWidgets import QSystemTrayIcon
from PyQt5.QtWidgets import QVBoxLayout


class SystemCtl():

    def __init__(self, service):
        self.service = service

    def start(self):
        cmd = ['sudo', 'systemctl', 'start', self.service]
        return subprocess.call(cmd) == 0

    def stop(self):
        cmd = ['sudo', 'systemctl', 'stop', self.service]
        return subprocess.call(cmd) == 0

    def is_active(self):
        cmd = ['systemctl', 'is-active', '--quiet', self.service]
        return subprocess.call(cmd) == 0


class JournalCtl():

    def __init__(self, service):
        self.service = service

    def get_log(self, num_lines=100):
        cmd = ['journalctl', '-u', self.service, '-n', str(num_lines)]
        return subprocess.check_output(cmd).decode(sys.stdout.encoding)


class LogDialog(QDialog):

    def __init__(self, service, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle("Log of {0}".format(service))

        self.journalctl = JournalCtl(service)

        layout = QVBoxLayout(self)
        self.scrollarea = QScrollArea(self)
        self.label_logview = QLabel(self.scrollarea)
        self.label_logview.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.scrollarea.setWidget(self.label_logview)
        self.scrollarea.setWidgetResizable(True)
        layout.addWidget(self.scrollarea)

        self.resize(600, 300)

        self.timer_refresh = QTimer(self)
        self.timer_refresh.setInterval(1000)
        self.timer_refresh.timeout.connect(self.update_logview)

    def update_logview(self):
        self.label_logview.setText(self.journalctl.get_log())

    def scroll_bottom(self):
        scrollbar = self.scrollarea.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())

    def showEvent(self, event):
        self.update_logview()
        QTimer.singleShot(100, self.scroll_bottom)
        self.timer_refresh.start()

    def hideEvent(self, event):
        self.timer_refresh.stop()

    def closeEvent(self, event):
        event.ignore()
        self.hide()


class SystemDServiceTrayIcon(QSystemTrayIcon):

    def __init__(self, service, text, parent=None):
        QSystemTrayIcon.__init__(self, parent)

        self.systemctl = SystemCtl(service)

        self.icon_inactive = self.paint_icon(Qt.lightGray, text)
        self.icon_active = self.paint_icon(Qt.green, text)

        self.menu = QMenu(parent)
        self.action_start = self.menu.addAction("Start")
        self.action_start.triggered.connect(self.on_action_start)
        self.action_stop = self.menu.addAction("Stop")
        self.action_stop.triggered.connect(self.on_action_stop)
        self.action_viewlog = self.menu.addAction("View Log")
        self.action_viewlog.triggered.connect(self.on_action_viewlog)
        self.action_exit = self.menu.addAction("Exit")
        self.action_exit.triggered.connect(self.on_action_exit)
        self.activated.connect(self.on_activated)

        self.logdialog = LogDialog(service, parent)

        self.update_status()
        timer = QTimer(self)
        timer.setInterval(3000)
        timer.timeout.connect(self.update_status)
        timer.start()

    def paint_icon(self, color, text):
        pixmap = QPixmap(32, 32)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        painter.setRenderHint(QPainter.Antialiasing)
        pen = QPen(Qt.darkGray)
        pen.setWidth(2)
        painter.setPen(pen)
        path = QPainterPath()
        path.addRect(2, 2, 28, 22)
        painter.fillPath(path, Qt.white)
        painter.drawPath(path)
        path = QPainterPath()
        path.addRect(8, 26, 16, 4)
        painter.fillPath(path, Qt.white)
        painter.drawPath(path)
        painter.fillRect(14, 25, 4, 2, Qt.white)
        path = QPainterPath()
        path.addRect(6, 6, 20, 14)
        painter.fillPath(path, color)
        painter.drawPath(path)
        font = painter.font()
        font.setPixelSize(10)
        painter.setFont(font)
        painter.drawText(8, 8, 16, 10, Qt.AlignCenter, text)
        painter.end()
        return QIcon(pixmap)

    def update_status(self):
        if self.systemctl.is_active():
            self.setIcon(self.icon_active)
            self.action_start.setEnabled(False)
            self.action_stop.setEnabled(True)
            self.setToolTip("Service {0} is active.".format(
                self.systemctl.service))
            self.logdialog.setWindowIcon(self.icon_active)
        else:
            self.setIcon(self.icon_inactive)
            self.action_start.setEnabled(True)
            self.action_stop.setEnabled(False)
            self.setToolTip("Service {0} is inactive.".format(
                self.systemctl.service))
            self.logdialog.setWindowIcon(self.icon_inactive)

    def on_activated(self, reason):
        if (reason == QSystemTrayIcon.Trigger or
                reason == QSystemTrayIcon.Context):
            self.menu.popup(QCursor.pos())

    def on_action_start(self):
        self.systemctl.start()

    def on_action_stop(self):
        self.systemctl.stop()

    def on_action_viewlog(self):
        self.logdialog.show()

    def on_action_exit(self):
        QApplication.quit()


if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Usage: ' + sys.argv[0] + ' service [text]')
        sys.exit(1)
    service = sys.argv[1]
    if len(sys.argv) > 2:
        text = sys.argv[2]
    else:
        text = service[0:3]
    app = QApplication(sys.argv)
    trayicon = SystemDServiceTrayIcon(service, text)
    QTimer.singleShot(1000, trayicon.show)
    sys.exit(app.exec_())