#!/usr/bin/env python
# -*- coding: utf-8 -*-

__version__ = '1.0'

import signal
import sys
import atexit
import os
import time
import base64
import stat
import pwd
import argparse

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Notify', '0.7')
gi.require_version('AppIndicator3', '0.1')

from gi.repository import Notify
from gi.repository import Gtk
from gi.repository import GLib

"""
--------------------------------------------------------------------------------
    Config
--------------------------------------------------------------------------------
"""
APP_NAME = "Labsync Tray GTK"
APP_YEAR = "2019"
APP_VERSION = __version__ + '-' + APP_YEAR
APP_DESCR = "Notifica lo stato di aggiornamento del software nella cartella /opt"
APP_AUTHORS = ["""Sezione di Informatica
DiSIT
Università del Piemonte Orientale"""]
APP_ID = "labsync-tray-gtk"

# force using StatusIcon over AppIndicator
FORCE_TRAY = False

# not used in this version
#CURR_DIR = os.path.dirname(os.path.abspath(__file__))

# files do not get deleted on user logout, so better create them in a neutral
# location with 666 file permission (see function create_icons_files() below)
#TMP_DIR = "/var/tmp/" + pwd.getpwuid(os.getuid()).pw_name
TMP_DIR = "/var/tmp/labsync-tray"

# state names
APP_OFF = 'Off'
APP_SYNC = "Syncing"
APP_SYNCED = "Synced"

# icons: could be PNG or SVG as well
APP_ICONS = {
    APP_OFF: os.path.join(TMP_DIR, 'labsync-off.svg'),
    APP_SYNCED: os.path.join(TMP_DIR, 'labsync-synced.svg'),
    APP_SYNC: os.path.join(TMP_DIR, 'labsync-sync.svg')
}

# seconds between checks
CHECK_INTERVAL = 30
CHECK_INTERVAL_UNIT = 's' # seconds, according to previous value

# how long the notification stays on the screen (seconds; 0=until user clicks)
# WARNING: if NOTIFY_TIME != 0, the application waits anyway when the user close the notification before the time is expired
# This is not an issue if we don't care about the status icon menu (GNOME does not show it anymore...)
NOTIFY_TIME = 0

# messages
MSG_SYNC_TITLE = "labsync"
MSG_SYNC_TEXT = """Stiamo aggiornando il software nella cartella /opt; ci \
sarà un po' di attività sul disco.\
\r\rAlcuni dei programmi lì presenti (comprese le macchine virtuali) potrebbero \
non funzionare correttamente fino al termine degli aggiornamenti.\
\r\rIl termine della procedura di aggiornamento ti verrà notificato, ti \
preghiamo di attendere qualche minuto.\
\r\r<b>Se hai atteso più di mezz&apos;ora, non hai ricevuto la notifica e il \
software che vuoi usare continua a non funzionare, contatta i tecnici.</b>"""
MSG_SYNC_ICON="dialog-warning"
# 0 = normal, 1 = warning, 2 = critical
MSG_SYNC_URGENCY = 2

MSG_SYNCED_TITLE = 'labsync'
MSG_SYNCED_TEXT = "Il software nella cartella /opt è aggiornato."
MSG_SYNCED_ICON="info"
# 0 = normal, 1 = warning, 2 = critical
MSG_SYNCED_URGENCY = 1

# file to check for saying if we are syncing or not
FILE_TO_CHECK = "/opt/ver"

# strings
STR_MENU_STATUS = "Stato"
STR_MENU_ABOUT = "Info"
STR_MENU_EXIT = "Esci"
STR_BTN_CLOSE = "Chiudi"

"""
--------------------------------------------------------------------------------
    Icons
--------------------------------------------------------------------------------
"""

APP_ICONS_DATA = {
    APP_OFF: """\
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNOTYsMTg4LjE2Yy01MC44OTg1NiwwIC05Mi4xNiwtNDEuMjYxNDQgLTkyLjE2LC05Mi4xNnYwYzAsLTUwLjg5ODU2IDQxLjI2MTQ0LC05Mi4xNiA5Mi4xNiwtOTIuMTZ2MGM1MC44OTg1NiwwIDkyLjE2LDQxLjI2MTQ0IDkyLjE2LDkyLjE2djBjMCw1MC44OTg1NiAtNDEuMjYxNDQsOTIuMTYgLTkyLjE2LDkyLjE2eiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48ZyBpZD0iTGF5ZXJfMSI+PGc+PGc+PGcgZmlsbD0iIzk1YTVhNiI+PGc+PHBhdGggZD0iTTIuOTYyNSw5NmMwLC01MS4zNzUgNDEuNjYyNSwtOTMuMDM3NSA5My4wMzc1LC05My4wMzc1YzUxLjM3NSwwIDkzLjAzNzUsNDEuNjYyNSA5My4wMzc1LDkzLjAzNzVjMCw1MS4zNzUgLTQxLjY2MjUsOTMuMDM3NSAtOTMuMDM3NSw5My4wMzc1Yy01MS4zNzUsMCAtOTMuMDM3NSwtNDEuNjYyNSAtOTMuMDM3NSwtOTMuMDM3NXoiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xNjMuNTc1LDY3LjUzNzVjLTMuODYyNSwwIC03Ljc2MjUsMC4wMzc1IC0xMS42MjUsMC4wMzc1Yy04LjczNzUsLTE5LjU3NSAtMjUuODc1LC0zMy43MTI1IC00Ny40NzUsLTM3LjA4NzVjLTI0LjE1LC0zLjc4NzUgLTQ3LjIxMjUsNy4zODc1IC02MC45NzUsMjcuMTEyNWMtNi4xMTI1LDguNzM3NSA4LjI1LDE2Ljk1IDE0LjMyNSw4LjI1YzE4Ljg2MjUsLTI3IDU5LjE3NSwtMjQuNTYyNSA3NS43MTI1LDEuNzYyNWMtMy42LDAgLTcuMTYyNSwwIC0xMC43NjI1LDAuMDM3NWMtMS4wMTI1LDAgLTEuNzI1LDAuNDUgLTIuMTM3NSwxLjA4NzVjLTAuNzEyNSwwLjc4NzUgLTEuMDEyNSwxLjkxMjUgLTAuMjI1LDMuMDc1YzYuODI1LDEwLjU3NSAxMy42NSwyMS4xODc1IDIwLjQ3NSwzMS43NjI1YzEuMTYyNSwxLjc2MjUgMy42Mzc1LDEuNzYyNSA0Ljc2MjUsMGM2Ljc4NzUsLTEwLjYxMjUgMTMuNTM3NSwtMjEuMjYyNSAyMC4zMjUsLTMxLjg3NWMxLjA4NzUsLTEuOCAtMC40MTI1LC00LjE2MjUgLTIuNCwtNC4xNjI1eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48cGF0aCBkPSJNMTM0LjM2MjUsMTI1LjY2MjVjLTE4LjcxMjUsMjYuODEyNSAtNTguNTM3NSwyNC42IC03NS4zMzc1LC0xLjEyNWMzLjQ1LDAgNi44NjI1LDAgMTAuMzEyNSwtMC4wMzc1YzEuMDEyNSwwIDEuNzI1LC0wLjQ1IDIuMTM3NSwtMS4wODc1YzAuNzEyNSwtMC43ODc1IDEuMDEyNSwtMS45MTI1IDAuMjI1LC0zLjA3NWMtNi44MjUsLTEwLjU3NSAtMTMuNjUsLTIxLjE4NzUgLTIwLjQ3NSwtMzEuNzYyNWMtMS4xNjI1LC0xLjggLTMuNjM3NSwtMS43NjI1IC00Ljc2MjUsMGMtNi43ODc1LDEwLjYxMjUgLTEzLjUzNzUsMjEuMjYyNSAtMjAuMzI1LDMxLjg3NWMtMS4xMjUsMS43NjI1IDAuMzM3NSw0LjEyNSAyLjM2MjUsNC4xMjVjNC4wMTI1LDAgNy45ODc1LC0wLjAzNzUgMTIsLTAuMDM3NWM4LjgxMjUsMTkuMjM3NSAyNS44LDMzLjExMjUgNDcuMjEyNSwzNi40ODc1YzI0LjExMjUsMy43ODc1IDQ3LjE3NSwtNy40MjUgNjAuOTM3NSwtMjcuMTEyNWM2LjExMjUsLTguNzM3NSAtOC4yNSwtMTYuOTEyNSAtMTQuMjg3NSwtOC4yNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxnPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjZTg0ODQ5Ii8+PHBhdGggZD0iTTE0MC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWMtMS4xNzMsLTEuMTczIC0xLjE3MywtMy4wNjkgMCwtNC4yNDJsMjQsLTI0YzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQybC0yNCwyNGMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMTY0LjgsMTY3LjU1NjhjLTAuNzY4LDAgLTEuNTM2LC0wLjI5NCAtMi4xMjEsLTAuODc5bC0yNCwtMjRjLTEuMTczLC0xLjE3MyAtMS4xNzMsLTMuMDY5IDAsLTQuMjQyYzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGwyNCwyNGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQyYy0wLjU4NSwwLjU4NSAtMS4zNTMsMC44NzkgLTIuMTIxLDAuODc5eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4K""",

    APP_SYNCED: """\
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiMzNDk4ZGIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjMWZhODViIi8+PHBhdGggZD0iTTE0OS44LDE2Ny41NTY4Yy0wLjc2NzYyLDAgLTEuNTM1MjUsLTAuMjkyODcgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0xLjE3MTg3LC0xLjE3MTg3IC0xLjE3MTg3LC0zLjA3MDUgMCwtNC4yNDIzOGMxLjE3MTg4LC0xLjE3MTg3IDMuMDcwNSwtMS4xNzE4NyA0LjI0MjM3LDBsOS44Nzg2Myw5Ljg3OTM4bDE4Ljg3OSwtMTguODc5YzEuMTcxODgsLTEuMTcxODcgMy4wNzA1LC0xLjE3MTg3IDQuMjQyMzcsMGMxLjE3MTg4LDEuMTcxODggMS4xNzE4OCwzLjA3MDUgMCw0LjI0MjM3bC0yMSwyMWMtMC41ODYxMiwwLjU4NTc1IC0xLjM1Mzc1LDAuODc4NjIgLTIuMTIxMzgsMC44Nzg2MnoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xMTYuOCwxODguNTU2OHYtNzJoNzJ2NzJ6IiBpZD0ib3ZlcmxheS1kcmFnIiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9Im5vbmUiIG9wYWNpdHk9IjAiLz48L2c+PC9nPjwvc3ZnPgo=""",

    APP_SYNC: """\
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiNlNjdlMjIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xNTIuOCwxMjIuNTU2OGMxNi41NjYsMCAzMCwxMy40MzQgMzAsMzBjMCwxNi41NjYgLTEzLjQzNCwzMCAtMzAsMzBjLTE2LjU2NiwwIC0zMCwtMTMuNDM0IC0zMCwtMzBjMCwtMTYuNTY2IDEzLjQzNCwtMzAgMzAsLTMwIiBmaWxsPSIjZmZmZmZmIi8+PHBhdGggZD0iTTE2NC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0wLjU2NCwtMC41NjEgLTAuODc5LC0xLjMyNiAtMC44NzksLTIuMTIxdi0yMWMwLC0xLjY1NiAxLjM0NCwtMyAzLC0zYzEuNjU2LDAgMywxLjM0NCAzLDN2MTkuNzU4bDExLjEyMSwxMS4xMjFjMS4xNzMsMS4xNzMgMS4xNzMsMy4wNjkgMCw0LjI0MmMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiMyNmE5ZTAiLz48Y2lyY2xlIGN4PSI0MDcuNDY2NjciIGN5PSI0MDYuODE4MTMiIHRyYW5zZm9ybT0ic2NhbGUoMC4zNzUsMC4zNzUpIiByPSIxMiIgZmlsbD0iIzI2YTllMCIvPjwvZz48L2c+PHBhdGggZD0iTTExNi44LDE4OC41NTY4di03Mmg3MnY3MnoiIGlkPSJvdmVybGF5LWRyYWciIGZpbGw9IiNmZjAwMDAiIHN0cm9rZT0ibm9uZSIgb3BhY2l0eT0iMCIvPjwvZz48L2c+PC9zdmc+Cg=="""
}

"""
--------------------------------------------------------------------------------
    Main application class
--------------------------------------------------------------------------------
"""
class TrayApp():

    def __init__(self, appid, icon=None, menu=None, check_interval=None):
        self.task_changed = True
        self.task = APP_SYNCED
        self.current_notification = None
        self.check_interval = CHECK_INTERVAL
        # set defaults and parameters
        if check_interval != None:
            self.check_interval = check_interval
        self.menu = self.build_menu()
        if menu != None:
            self.menu = menu
        self.menu.show_all()
        self.icon = icon
        if icon != None:
            self.icon = icon

        if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
            self.tray = AppIndicator3.Indicator.new(
                appid, self.icon,
                AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
            self.tray.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
            self.tray.set_menu(self.menu)
        else:
            self.tray = Gtk.StatusIcon()
            self.tray.set_visible(True);
            self.tray.connect('popup-menu', self.on_popup_menu)
            self.tray.connect("button-press-event", self.on_mouse_click)
            self.tray.set_tooltip_text(APP_NAME)
            self.tray.set_title(APP_NAME)
            self.tray.set_from_file(self.icon)
        # initialize task value
        self.update_task()
        # intial icon could be different from the current state, force update
        self.update_icon()
        # send notification on start only if labsync is syncing
        if self.task_changed:
            self.send_notification()
        # then start updating every CHECK_INTERVAL seconds
        # http://developer.gnome.org/pygobject/stable/glib-functions.html#function-glib--timeout-add-seconds
        GLib.timeout_add_seconds(self.check_interval, self.handler_timeout)

    def on_mouse_click(self, data, event):
        # 1-left, 2-middle, 3-right
        if event.button == 1:
            self.update_task()
            self.send_notification()
        # time is required by the popup
        # time = Gtk.get_current_event_time()
        #self.menu.popup(None, None, Gtk.StatusIcon.position_menu, self.tray,
        #    event.button, time)

    def on_popup_menu(self, icon, button, time):
        self.menu.popup(None, None, Gtk.StatusIcon.position_menu, icon,
            button, time)

    def build_menu(self):
        menu = Gtk.Menu()
        item_status = Gtk.MenuItem(STR_MENU_STATUS)
        item_status.connect("activate", self.handler_menu_status)
        menu.append(item_status)
        about = Gtk.MenuItem(STR_MENU_ABOUT)
        about.connect("activate", self.handler_menu_about)
        menu.append(about)
        menu.append(Gtk.SeparatorMenuItem())
        item_quit = Gtk.MenuItem(STR_MENU_EXIT)
        item_quit.connect("activate", self.handler_menu_quit)
        menu.append(item_quit)
        return menu

    def handler_menu_quit(self, evt):
        Gtk.main_quit()

    def handler_menu_status(self, evt):
        self.update_task()
        self.send_notification()

    def send_notification(self):
        if self.task == APP_SYNC:
            title = MSG_SYNC_TITLE
            txt = MSG_SYNC_TEXT
            icon = MSG_SYNC_ICON
            urgency = MSG_SYNC_URGENCY
        else:
            title = MSG_SYNCED_TITLE
            txt = MSG_SYNCED_TEXT
            icon = MSG_SYNCED_ICON
            urgency = MSG_SYNCED_URGENCY
        # close the current notification, if any
        if self.current_notification != None:
            self.current_notification.close()
            self.current_notification = None
        self.current_notification = Message(None, title, txt, icon, NOTIFY_TIME, urgency).notification
        self.current_notification.connect("closed", self.handler_current_notification_closed)

    def handler_current_notification_closed(self, evt):
        self.current_notification = None

    def handler_menu_about(self, evt):
        about_dialog = Gtk.AboutDialog()
        about_dialog.set_destroy_with_parent(True)
        about_dialog.set_icon_name("dialog-information")
        # no SVG?
        about_dialog.set_logo_icon_name("")
        about_dialog.set_program_name(APP_NAME)
        about_dialog.set_name(APP_NAME)
        about_dialog.set_version(APP_VERSION)
        about_dialog.set_license_type(Gtk.License.GPL_3_0)
        about_dialog.set_comments(APP_DESCR)
        about_dialog.set_authors(APP_AUTHORS)
        about_dialog.set_default_response(Gtk.ResponseType.CLOSE)
        about_dialog.run()
        about_dialog.destroy()

    def update_icon(self):
        if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
            self.tray.set_icon(self.icon)
        else:
            self.tray.set_from_file(self.icon)

    def update_task(self):
        exists = os.path.isfile(FILE_TO_CHECK)
        if exists:
            task = APP_SYNCED
            self.icon = APP_ICONS[APP_SYNCED]
        else:
            task = APP_SYNC
            self.icon = APP_ICONS[APP_SYNC]
        if self.task != task:
            self.task_changed = True
            self.update_icon()
        else:
            self.task_changed = False
        self.task = task

    def handler_timeout(self):
        """This will be called every few seconds by the GLib.timeout.
        """
        self.update_task()
        if self.task_changed:
            self.send_notification()
        # return True so that we get called again
        # returning False will make the timeout stop
        return True

    def main(self):
        Gtk.main()

"""
--------------------------------------------------------------------------------
    Message class
--------------------------------------------------------------------------------
"""
class Message():
    def mess_callback(*args, **kwargs):
        pass

    def __init__(self, parent, caption, msg, img=None, timeout=None,
        urgency=None):

        self.notification = None

        if timeout != None: pass
        else: timeout = 0 # message should not timeout

        if urgency: pass
        else: urgency = 0

        caps = Notify.get_server_caps()
        self.notification = Notify.Notification.new(caption, msg, img) # passing an image is optional
        self.notification.set_timeout(timeout) # milliseconds
        self.notification.set_urgency(urgency) # 0-Low, 1-Normal, 2-Critical
        # Without the following `add_action` option, no countdown to the timeout is shown
        if timeout != 0 and 'actions' in caps:
            self.notification.add_action("close", STR_BTN_CLOSE, self.mess_callback, None) # Show the countdown to close
            self.notification.show()
            # wait (seconds)
            time.sleep(timeout)
            self.notification.close()
        else:
            self.notification.show()

"""
--------------------------------------------------------------------------------
  Main
--------------------------------------------------------------------------------
"""
def handler_exit():
    cleanup()

def handler_signal(sig, frame):
    cleanup()

def initialize():
    create_tmp_dir()
    create_icon_files()

def cleanup():
    # we can skip files and folder cleanup
    # the icons are always needed and never cleaned up on session logout...
    #delete_icon_files()
    #delete_tmp_dir()
    Notify.uninit()
    if Gtk.main_level() > 0:
        Gtk.main_quit()

def create_tmp_dir():
    if not os.path.exists(TMP_DIR):
        os.makedirs(TMP_DIR)
        # short way to change permissions
        # see create_icon_files() for alternative way
        os.chmod(TMP_DIR, 0o777)

def delete_tmp_dir():
    if os.path.exists(TMP_DIR):
        os.rmdir(TMP_DIR)

def create_icon_files():
    for key, value in APP_ICONS.items():
        if not os.path.isfile(value):
            with open(value, 'wb') as fp:
                fp.write(base64.b64decode(APP_ICONS_DATA[key]))
            # these 2 command change file permissions to let everyone
            # delete the files, if needed
            st = os.stat(APP_ICONS[key])
            # https://docs.python.org/3/library/os.html#os.chmod
            # https://docs.python.org/3/library/stat.html#stat.S_IWUSR
            os.chmod(APP_ICONS[key],
                st.st_mode |
                stat.S_IRUSR | stat.S_IWUSR |
                stat.S_IRGRP | stat.S_IWGRP |
                stat.S_IROTH | stat.S_IWOTH
                )

def delete_icon_files():
    for key, value in APP_ICONS.items():
        if os.path.isfile(value):
            #print ("Deleted: " + value)
            os.remove(value)

# detect AppIndicator support
APPINDICATOR_SUPPORT = 1
try:
    from gi.repository import AppIndicator3
except:
    APPINDICATOR_SUPPORT = 0

if __name__ == "__main__":
    # parse command line arguments
    parser = argparse.ArgumentParser(description=APP_NAME + ' ' + APP_VERSION)
    parser.add_argument('-i', '--interval', type=int,
        help='check interval (seconds)')
    args = parser.parse_args()

    initialize()

    # clean up on exit
    atexit.register(handler_exit) # normal exit
    signal.signal(signal.SIGINT, handler_signal) # ctrl-c
    signal.signal(signal.SIGTERM, handler_signal) # kill signal (but not kill -9)

    Notify.init(APP_ID)
    app = TrayApp(APP_ID, icon=APP_ICONS[APP_OFF], check_interval=args.interval)
    print 'Check interval: ' + str(app.check_interval) + CHECK_INTERVAL_UNIT
    app.main()