#!/usr/bin/env python3

import gi
import subprocess

script_name = "tpapplet.py"
ps_output = subprocess.check_output(["ps", "-ef"])
ps_lines = ps_output.decode("utf-8").split("\n")
process_num = 0
for line in ps_lines:
    if script_name in line:
        process_num += 1

if process_num > 1:
    print("TurboPrint applet is already running")
    quit()

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import GLib
try:
    gi.require_version('AyatanaAppIndicator3', '0.1')
    from gi.repository import AyatanaAppIndicator3 as appindicator
except:
    try:
       	gi.require_version('AppIndicator3', '0.1')
        from gi.repository import AppIndicator3 as appindicator
    except:
        print("Please install AyatanaAppIndicator3 or AppIndicator3")
        quit()
gi.require_version('Notify', '0.7')
from gi.repository import Notify as notifyclass
import os
import time
import math
from time import gmtime, strftime
import time
import subprocess
from array import *
from math import *
from copy import deepcopy

#define mode_warn 1

mode_status = 0
mode_warning = 1
mode_error = 2
TPSTATE_NONE=0
TPSTATE_PAPEROUT=1 
TPSTATE_INKOUT=2
TPSTATE_PRINTING=4 
TPSTATE_ERROR=8
TPSTATE_COVEROPEN=16 
TPSTATE_INKWARN=32
TPSTATE_DISCONNECTED=64
TPSTATE_CDERROR=128
TPSTATE_WARNING=8192


icons_idle=["prt_standard.svg"]
icons_printing=["prt_print1.svg","prt_print2.svg","prt_idle.svg"]
icons_paperout=["prt_nopaper.svg", "prt_idle.svg"]
icons_inkout=["prt_noink.svg", "prt_idle.svg"]
icons_inkwarn=["prt_inkwarn.svg", "prt_idle.svg"]
icons_error=["prt_error.svg", "prt_idle.svg"]
icons_cderror=["prt_cderror.svg", "prt_idle.svg"]
icons_disconnected=["prt_notconnected.svg", "prt_idle.svg"]
icons_warning=["prt_warning.svg", "prt_idle.svg"]

timeout_source = 0

class nstruct:
    def __init__(self,aprinter, amessage, alevel, apointer):
       self.printer=aprinter
       self.message=amessage
       self.level=alevel
       self.notification=apointer

class notifies:
    def __init__(self):
        self.notifies = []
        self.newlist = []

    def cleanup(self):
        for data in self.notifies:
            data.notification.close()
        self.notifies=[]
        self.newlist =[]

    def setNotify(self,printer,message,level):
        found = 0
       # check if printer already present in notifies list
        for data in self.newlist:
            if data.printer == printer:
                found = 1
            if data.level<level:
                data.message=message
                data.level=level
                break
        if found<1:
            self.newlist.append(nstruct(printer,message,level,0))
        
    def mergeNotifies(self):
       # mark all entries as unchanged
        for old in self.notifies:
            old.level = 0

        for data in self.newlist:
            found = 0
            for note in self.notifies:
                if note.printer == data.printer:
                    note.level=1
                    found=1
                if note.message == data.message:
                    break
                else:
                    note.message=data.message
                    result = note.notification.update("%s"%(data.printer), data.message, None)
                    note.notification.show()
                break
            if found==0:
                newnotify=nstruct(data.printer,data.message,1,0)
                newnotify.notification=notifyclass.Notification.new("%s"%(newnotify.printer), newnotify.message, None)
                newnotify.notification.show()
                self.notifies.append(newnotify)
        for old in self.notifies:
            if old.level == 0:
                old.notification.close()
                self.notifies.remove(old)
        self.newlist = []


class printerstatus:
    def __init__(self):
        self.name = ''
        self.average = 0.0
        self.values = None
        self.itemStatus=[]
        self.itemState=[]
        self.messageStore=[[],[]]
        self.numEntries=[0,0,0,0]
        self.sharepath = "/usr/share/turboprint"
        self.varpath = "/var/log"
        self.binpath = "/usr/share"
        self.language = "en"
        self.flagChanged = 0;
        self.curMode = 0
        self.printerstate=0
        self.animcounter=0
        self.anim_sequence=0
        self.curimgpath = ''
        self.mynotifies = notifies()
        self.notifyThreshold = 2
 
        tplogpath=""
        animstate=0

    def cleanup(self):
        self.mynotifies.cleanup()

    def openTPMonitor(self,menuitem,myclass, queueName):
        if(len(queueName)>0):
            argument = "-P%s"%(queueName)
        else:
            argument = ''
        args = (self.binpath+"/turboprint-monitor", argument)
        popen = subprocess.Popen(args, stdout=subprocess.PIPE)
        # popen.wait() # don't block if monitor wasn't running

    def openTPControl(self,menuitem):
        popen = subprocess.Popen(self.binpath+"/turboprint", stdout=subprocess.PIPE)
        popen.wait()

    def openTPComposer(self,menuitem):
        popen = subprocess.Popen(self.binpath+"/turboprint-composer", stdout=subprocess.PIPE)
        # popen.wait() # allow multiple instances

    def animate_icon(self):
        global ind

        self.animcounter+=1
        if self.animcounter >= len(self.anim_sequence): self.animcounter=0
        newiconpath=self.sharepath+'/img/'+self.anim_sequence[self.animcounter]
        if ind.get_icon() != newiconpath:
            ind.set_icon_full(newiconpath, "printer status")

    def readStatusFile(self):
        queuestate=0
        self.numEntries[0]=0
        self.numEntries[1]=0
        self.numEntries[2]=0
        self.flagChanged =0
        try:
            f=open(self.varpath+"/turboprint/tpstatus", "r")
        except:
            self.messageStore[0][0]="ERROR"
            self.messageStore[1][0]="cannot load tpstatus"
            self.numEntries[0]=1
            self.flagChanged=1
            print("open error")
            return
        strings=f.readlines()

        inum=0
        num_entries=len(self.messageStore[0])
 
        for text in strings:
            if text.endswith('\n'): 
                text = text[:-1]
            if "[Warnings]" in text:
                mode = mode_warning
            elif "[Status]" in text:
                mode= mode_status
            elif "[Errors]" in text:
                mode= mode_error
            else:
                position=text.find(':')
                if position > 0:
                    printername = text[0:position]
                    message = text[position+1:]
                    position2 = message.find('=')
                    if position2 > 0:
                        msgnumber = int(message[0:position2],16)
                        queuestate|=msgnumber
                        msgtext = message[position2+1:]
                        self.numEntries[mode]+=1;
                               
                    if self.notifyThreshold > 2-mode: 
                        self.mynotifies.setNotify(printername,msgtext,mode)

                    if(inum<num_entries):
                            if self.messageStore[0][inum] != printername or self.messageStore[1][inum] != msgtext or self.itemState[inum]!=msgnumber:
                                self.messageStore[0][inum]=printername
                                self.messageStore[1][inum]=msgtext[:]
                                self.itemState[inum]=msgnumber
                                self.flagChanged=1
                    else:
                            self.messageStore[0].append(printername)
                            self.messageStore[1].append(msgtext)
                            self.itemState.append(msgnumber)
                            self.flagChanged=1
                    inum+=1
                    if self.curMode > mode: self.curMode=mode

        if inum<num_entries:
            self.messageStore[0]=self.messageStore[0][0:inum]
            self.messageStore[1]=self.messageStore[1][0:inum]
            self.itemState=self.itemState[0:inum]
            self.flagChanged=1
        if queuestate == TPSTATE_NONE and self.curMode> mode_warning:
            queuestate = TPSTATE_WARNING    
        self.printerstate=queuestate
        self.mynotifies.mergeNotifies()
        
        
    def load_tpcfg(self):
        self.sharepath = "/usr/share/turboprint"
        self.varpath = "/var/log"
        self.language = "en"


        f=open("/etc/turboprint/system.cfg","r")
        strings=f.readlines()

        for line in strings:
            if line.endswith('\0') or line.endswith('\n'): line=line[:-1]
            position=line.find('=')
            if position > 0:
                left = line[0:position]
                right = line[position+1:]
                if "TP_LANGUAGE" in left:
                    self.language=right
                elif "TPPATH_SHARE" in left:
                    self.sharepath=right
                elif "TPPATH_VAR" in left:
                    self.varpath=right
                elif "TPPATH_BIN" in left:
                    self.binpath=right
                elif "APPLET_NOTIFYLEVEL" in left:
                    self.notifyThreshold = int(right)
        f.close


def tick(args=None):
        global mystatus
        global counter
        global ind

        if counter%5==0:       
            mystatus.flagChanged=0
            mystatus.readStatusFile()
            old_pstate = mystatus.printerstate
            if mystatus.flagChanged==1:
                msgcode = mystatus.printerstate
                if msgcode & TPSTATE_PAPEROUT: mystatus.anim_sequence = icons_paperout
                elif msgcode & TPSTATE_CDERROR: mystatus.anim_sequence = icons_cderror
                elif msgcode & TPSTATE_INKOUT: mystatus.anim_sequence = icons_inkout
                elif msgcode & TPSTATE_ERROR: mystatus.anim_sequence = icons_error
                elif msgcode & TPSTATE_COVEROPEN: mystatus.anim_sequence = icons_error
                elif msgcode & TPSTATE_DISCONNECTED: mystatus.anim_sequence = icons_disconnected
                elif msgcode & TPSTATE_INKWARN: mystatus.anim_sequence = icons_inkwarn
                elif msgcode & TPSTATE_WARNING: mystatus.anim_sequence = icons_warning
                elif msgcode & TPSTATE_PRINTING: mystatus.anim_sequence = icons_printing
                else:
                    mystatus.anim_sequence = icons_idle
                    msgcode = TPSTATE_NONE
                mystatus.printerstate=msgcode
                if old_pstate != msgcode:
                    mystatus.anim_iter=0

                mymenu=ind.get_menu()
                children=mymenu.get_children()
                if(len(children)>2):
                    for i in range(0,len(children)-2):
                        mymenu.remove(children[i])
    
                messagecount = mystatus.numEntries[0] + mystatus.numEntries[1] + mystatus.numEntries[2]
                itemComposer = gtk.MenuItem(label='TurboPrint Composer')
                itemComposer.connect('activate',mystatus.openTPComposer)
                mymenu.prepend(itemComposer)
                itemControl = gtk.MenuItem(label='TurboPrint Control')
                itemControl.connect('activate',mystatus.openTPControl)
                mymenu.prepend(itemControl)
#                mymenu.prepend(gtk.SeparatorMenuItem())

                item=[]
                if messagecount > 0:
                    for i in range(messagecount):
                        # print(i, messagecount)
                        message = "%s: %s" %(mystatus.messageStore[0][i],mystatus.messageStore[1][i])
                        item.append(gtk.MenuItem(label=message))
                        item[i].connect('activate', mystatus.openTPMonitor,mystatus, mystatus.messageStore[0][i])
                        mymenu.prepend(item[i])
                else:
                    item = gtk.MenuItem(label='TurboPrint-Monitor')
                    item.connect('activate',mystatus.openTPMonitor,mystatus, '')
                    mymenu.prepend(item)
                mymenu.show_all()

        counter+=1
            
        mystatus.animate_icon()
        return True

def quit(source):
    global ind
    global timeout_source
    global mystatus
    mystatus.cleanup()
    notifyclass.uninit()
    ind.set_status(appindicator.IndicatorStatus.PASSIVE)
    GLib.source_remove(timeout_source)
    gtk.main_quit()
    

if __name__ == "__main__":
    
    APPINDICATOR_ID = 'turboprint'

    mystatus=printerstatus()
    mystatus.load_tpcfg()

    counter =0
    mystatus.anim_sequence = icons_idle
    mystatus.printerstate=TPSTATE_NONE
    
    
    ind = appindicator.Indicator.new(APPINDICATOR_ID, '', appindicator.IndicatorCategory.SYSTEM_SERVICES)
    ind.set_status (appindicator.IndicatorStatus.ACTIVE)

    menu = gtk.Menu()
    
    image = gtk.Image()
    image.set_from_icon_name("gtk-quit", 24)
    if(mystatus.language == 'de'):
        # item_quit = gtk.ImageMenuItem('Beenden')
        item_quit = gtk.MenuItem(label='Beenden')
    elif(mystatus.language == 'es'):
        # item_quit = gtk.ImageMenuItem('Dejar')
        item_quit = gtk.MenuItem(label='Dejar')
    else:
        # item_quit = gtk.ImageMenuItem('Quit')
        item_quit = gtk.MenuItem(label='Quit')
        
    # item_quit.set_image(image)
    # item_quit.set_always_show_image(True)
    item_quit.connect("activate", quit)
    
    menu.append(gtk.SeparatorMenuItem())
    menu.append(item_quit)
    
    menu.show_all()
    mystatus.messageStore[0].append('empty')
    mystatus.messageStore[1].append('empty')
    mystatus.itemState.append(0)
    ind.set_menu(menu)
    timeout_source = GLib.timeout_add(900, tick)
    notifyclass.init(APPINDICATOR_ID)
    tick()
    gtk.main()
