#!/usr/bin/env python3

help="""
textMonitor.py: read input and interpret xml-tags text,

example : put the text in row r and column c with blue background:
  - <text r="15" c="15" text="test" bg="blue"/>

background can be specified also in format: '#xxxxxx', ex: '#0000ff' for blue.

The following options can be used:

   --width: characters per line
   --height: lines
   --font: font to use
   --background: background color
   --input: file to get info from
   --help: shows this help

Use as:
displayPako.py --input pakoDisplay.txt

"""
   
__version__ = "$Id$"
__author__ = "as/wb"
#
## revision, adapted to python3  A.Sievers   2024-08-05

from tkinter import *
import time
import string
import threading
import sys
import getopt
import os


##sys.path.append('/ncsServer/mrt/ncs/lib/python')
##import mySmallXml
import displayPakoUtil

WidgetOptions = {'width': 130, 'height': 48, 'background':"#ffffd0",
                 'font': '*-courier-bold-*-*-*-*-120-*-*-*-*-*-*'}
OtherOptions = {'title': 'textMonitor','input':''} 

class TextWindow:
    """Show text in a x-window at specific row and column."""
    
    global colors
    
    def showTimeLoop(self, where = "1.1"):
        """Show time at position where every second (does not return!).

        The showTimeLoop thread shall be running to refresh the window.
        """
#        try:
#	if 1==1:
#            while 1:
#                time.sleep(1.0)
#                #time0 = myUtil.tcsTime()
#                time0 = " "   # do not write anything
#                #self.write(where, time0, "")
#		self.win.update()
#        except:
#            print ". exception in thread showTimeLoop"
#            sys.exit(1)
    def exit(self,event):
        self.threadExit = 1
        sys.exit(0)
        
    def __init__(self, frame, dataFile="", configFile = "" , argDict = {}, **args):
        """Initialize a (tkinter) text window.

        Parameters:
        configFile: configuration file (not yet used)
        **args: shall only contain keys that are valid for Text widgets.
        """ 

        # set default widget options 
        self.attributes = WidgetOptions

        # use internal arguments to set options 
        for key in args.keys():
            self.attributes[key] = args[key]

        # use arguments to set widget options
        for key in argDict.keys():
            if key in WidgetOptions.keys():
                self.attributes[key] = argDict[key]
        
        # print ". attributes:", self.attributes
        
        self.win = Text(frame, self.attributes)
        # print ". height", self.win.cget("height")
        # inhibit that widget can be overwritten from keyboard
        self.win.bind("<Any-KeyPress>","print ''",None)
        # stop thread when widget is closed
        self.win.bind("<Destroy>",self.exit,None)        
        
        self.win.config(cursor="left_ptr")
        
        # display widget
        self.win.pack()
        
        #Refresh button
        self.buttonRefresh = Button(frame,text='Refresh', command=self.win.update())
        self.buttonRefresh.focus()
        self.buttonRefresh.pack(side=LEFT,padx=15)
        
        #self.win.bind("<Button-1>",self.exit,None)
        
        #Exit button
        self.buttonExit = Button(frame,text='Exit')
        self.buttonExit.bind('<Button-1>',self.exit)
        self.buttonExit.pack(side=RIGHT, padx=15)
        
        # define tag "default"
        self.tagName="default"
        self.win.tag_config (self.tagName,
                 {"background": WidgetOptions["background"]})

        # write line by line for widget, don't know why it is needed
        count = 0
        while count < int(self.attributes["height"]):
            self.win.insert("end", " "*int(self.attributes["width"])+"\n",
                            self.tagName)
            self.win.update()
            count = count + 1

        # self.fields: dictionary of fields in widget - for future
        self.fields = {}
        self.configFile = configFile
        if configFile:
            try:
                self.readConfig()
            except:
                print (". configuration file:", configFile, "not found")
	
        if dataFile:
            if 1==1:
                self.open = open(dataFile,'r')
           
		#Find the size of the file and move to the end
                st_results = os.stat(dataFile)
                st_size = st_results[6]
		##print st_size
                line = self.open.readline()
		##print line,len(line)
                st_size = st_size - (len(line)*100)
		##print st_size
                if st_size<0: st_size=0
                self.open.seek(st_size)
                line = self.open.readline()
                if (str.find(line,'>')!=-1) and (str.find(line,'<')==-1):
                    #line is wrong, should start with the next line
		    # e.g: '                           " bg="#AAF"/>'
                    pass
                else:
		    #line is good, reseek to read it
                    st_size = st_size - (len(line))
                    self.open.seek(st_size)
	   
                # start showTime thread
                self.threadExit = 0
                t = threading.Thread(target = self.readLine)
                t.start()        

                self.win.after(1,self.displayLine)        
            else:    
            #except:

                print (". data file:", dataFile, "not found")

        self.colors = [\
            "white", "black", "red", "green", "blue", "cyan", "yellow",
            "magenta"]
        self.colors = []
        for color in self.colors:
            self.win.tag_add("bg"+color,1.1,1.1)
            self.win.tag_config("bg"+color, {"background": color})    

        
    def readConfig(self):
        """Read configuration file and get field definitions."""
        
        inp = open(self.configFile)
        configuration = inp.readlines()
        for conf in configuration:
            # print conf
            attrs = displayPakoUtil.getAllAttributes(conf)
            self.fields[attrs["field"]] = attrs
            self.win.tag_add(attrs["field"], attrs["from"], attrs["to"])
            self.win.tag_config(attrs["field"], {"background": attrs["bg"],
                                                 "foreground": attrs["fg"]})
            
        # print self.fields
        
    def write(self, pos, text, tag):
        """Write text at position (in format row.col) with tag.

        Parameter:
          - pos: start position in row.column
          - text: text to write (use blanks to erase)
          - tag: can be used set background
        """

        #print ".write ",pos, text, tag
       	self.win.delete(pos, posAdd(pos, "0.%d" % len(text)))
       	self.win.insert(pos, text, tag)
        self.win.update()        
    
    def addTag(self, attrs):
        
        index1 = attrStopos(attrs)
        index2 = posAdd(index1, "0."+str(len(attrs["text"])))
        self.tagName = "tag"+index1
        # print attrs, self.tagName, index1, index2
                                         
        background = "#FFF%3.3x"%(int(attrs["r"])*64)
        # print ". background", background
        self.win.tag_config (self.tagName,
                            {"background": background})
        self.win.tag_add(self.tagName, index1, index2)

    def readLine(self):
        """Acts like a tail command. Executes in a thread"""
        self.lineReaden = ''
        
        while not(self.threadExit):
            where = self.open.tell()            
            self.lineReaden = self.open.readline()
            if not self.lineReaden:
                time.sleep(1)
                self.open.seek(where)
            else:
                #print self.lineReaden,
                #if self.lineReaden == '</p>':
                self.displayLine()

    def displayLine(self):        
        """Gets a line readen from the file, and updates the TextWidget with it"""
        
        line = self.lineReaden[:-1]
        if len(line) < 2 or line=='\n' or line==' ':
            #time.sleep(0.5)
            pass
        else:
#            print 'line:',line
            self.lineReaden = ''  
            #try:
            if 1==1:
                try:
                    attrs = displayPakoUtil.getAllAttributes(line)
                except:
                    self.readLine()
                # w.addTag(attrs)
                # if we have a new background color we have to define a tag
                if attrs.__contains__("bg"):
                    color = attrs["bg"]
                    if color.startswith("#"):
                        tag = "bg" + color[1:]
                    else:
                        tag = "bg" + color
#                    print (color, dir(), tag)
                    if not color in []:
                        # print ". adding tag ", tag, " for color:", color
                        self.win.tag_add(tag, 1.1,1.1)
                        self.win.tag_config(tag, {"background": color})
                else:
                    tag = "bg" + self.attributes["background"]
                # write string at specified position with background color
                try:
                    self.write(str.strip(attrs["r"])+"."+str.strip(attrs["c"]), attrs["text"], tag) # write from tkinter
                except:
                    pass
#                    print (e1)
            #except:
            else:
                print (". exception in main thread")
                sys.exit(1)
                pass

            self.win.after(1,self.displayLine)

def attrsToPos(attrs):
    """Translate keys r and c of attrs dictionary to format r.c."""

    return attrs["r"] + "." + attrs["c"]

def posAdd(pos, off):
    """Add offset to position, both given in row.column."""
    
    pos1 = str.split(pos, ".")
    off1 = str.split(off, ".")
    # print pos1, off1
    res = "%d.%d" % (int(pos1[0]) + int(off1[0]), int(pos1[1]) + int(off1[1]))
    # print pos1, off1, res
    return res

def optionListToDict(optList):
    """Transform an option list to a dictionary."""
    
    optDict = {}
    for item in optList:
        optDict[item[0][2:]] = item[1]
    return optDict

def optionList(dict):
    """Generate an option list for getopt from dictionary keys."""
    
    myList = []
    for key in dict.keys():
        myList.append(str(key + "="))
    # print ". optionList:", myList
    return myList

if __name__ == "__main__":

    if (len(sys.argv) > 1 and (sys.argv[1]=='--help' or sys.argv[1]=='-h')):
        print (help)
        sys.exit(1)


    optionDict = {}
    optionDict.update(WidgetOptions)
    optionDict.update(OtherOptions)
    optionList, args = getopt.getopt(sys.argv[1:], '', optionList(optionDict))

    # print optionList, args
    optionDict.update(optionListToDict(optionList))
    # print optionDict
    
    if 'input' in optionDict.keys():
        filename=optionDict['input']
    else:
        filename='pakoDisplay.txt'
    
    frame = Frame()
    frame.master.title(optionDict["title"])
    frame.grid()
    w = TextWindow(frame, dataFile=filename, configFile="obsMonitor.conf", argDict=optionDict)
    
    colors = ["white", "black", "red", "green", "blue", "cyan", "yellow",
              "magenta"]
#    colors = []
    for color in colors:
        w.win.tag_add("bg"+color,1.1,1.1)
        w.win.tag_config("bg"+color, {"background": color})    

    frame.mainloop()
