#############################################################################
#
# Authors: Michel F. SANNER, 
#
# Copyright: M. Sanner TSRI 2013
#
#############################################################################
# $Header: /opt/cvs/python/packages/share1.5/mglutil/errors.py,v 1.4 2013/10/21 23:50:02 sanner Exp $
#
# $Id: errors.py,v 1.4 2013/10/21 23:50:02 sanner Exp $
#

from mglutil.events import Event
import traceback, Tkinter, Pmw

class MGLException(Exception):
    pass

class ErrorEvent(Event):
    pass


"""
Error management component
"""

def getErrors(errorManager=None):
    if errorManager is None:
        errorManager = _ErrorManager
    return errorManager.errorStack


def hasErrors(errorManager=None):
    if errorManager is None:
        errorManager = _ErrorManager
    return len(errorManager.errorStack)>0

    
def clearErrors(errorManager=None):
    if errorManager is None:
        errorManager = _ErrorManager
    errorManager.clear()

    
def displayErrors(errorManager=None):
    if errorManager is None:
        errorManager = _ErrorManager
    errorManager.displayErrors()

    
def MGLError(exception, msg, errorManager=None, raiseException=False, **kw):
    if errorManager is None:
        errorManager = _ErrorManager
    stack = traceback.format_stack()
    error = Error(exception, msg, stack, errorManager.appName, **kw)
    errorManager.errorStack.append(error)
    if raiseException:
        raise MGLException
    return error
    
class Error:

    def __init__(self, exception, msg, stack, appName, **kw):
        self.exception = exception
        self.msg = msg
        self.kw = kw
        self.stack = stack
        self.appName = appName


    def getMsg(self, indent):
        lines = []
        lines.append(indent+'Message: ')
        nbc = 0
        line = indent+"  "
        for oneLine in self.msg.split('\n'):
            for word in oneLine.split():
                l = len(word)
                if nbc+l >= 75:
                    lines.append(line)
                    line = indent+"  "
                    nbc = 0
                line += word+' '
                nbc += l+1
            if line != indent+"  ":
                lines.append(line)
                nbc = 0
                line = indent+"  "
        if line != indent+"  ":
            lines.append(line)

        return lines

    def printMsg(self, indent):
        for l in self.getMsg(indent): print l


    def getException(self, indent):
        lines = []
        lines.append(indent+'Exception: ')
        lines.append(indent+'  '+self.exception.message)
        return lines

    def printException(self, indent):
        for l in self.getException(indent): print l
       
    def getStack(self, indent):
        lines = []
        lines.append(indent+'Stack: ')
        for l in self.stack:
            lines.append(indent+'  '+l[:-1])
        return lines

    def printStack(self, indent):
        for l in self.getStack(indent): print l

    def getContext(self, indent):
        lines = []
        if len(self.kw):
            lines.append("  -----------------------------------------------------------------------------")
            lines.append(indent+'  Context:')
            nbc = max([len(k) for k in self.kw.keys()])
            fmt = "  %"+"%-ds"%(nbc+1)
            for k,v in self.kw.items():
                lines.append(indent+fmt%k+":"+str(v))
        return lines

    def printContext(self, indent):
        for l in self.getContext(indent): print l

    def getAll(self, indent, stack=False):
        lines = []
        lines.append( "*******************************************************************************")
        lines.append( 'ERROR in %s'%self.appName)
        lines.append( "  -----------------------------------------------------------------------------")
        lines.extend(self.getMsg(indent="  "))
        lines.append( "  -----------------------------------------------------------------------------")
        lines.extend(self.getException(indent="  "))
        lines.extend(self.getContext(indent="  "))
        if stack:
            lines.append( "  -----------------------------------------------------------------------------")
            lines.extend(self.getStack(indent="  "))
        lines.append( "*******************************************************************************")
        return lines
                      
    def printAll(self, indent=''):
        for l in self.getAll(indent): print l



class ErrorManager:

    """
    Defines an object for handling errors
    """

    def __init__(self, appName):
        """
        Create an error manager for an application
        """
        self.appName = appName # name of the application for which the object
                               # is created
        self.Status = 'OKAY'
        self.errorStack = []
        self.handleError = None # function to be called when an error occurs
        
        self.showDetails = False
        
    def setAppName(self, name):
        self.appName = name

        
    def error(self, exception, msg, stack, **kw):
        """
        Handle an error
        """
        error = Error(exception, msg, stack, self.appName, **kw)
        self.errorStack.append(error)
        if self.handleError:
            self.handleError(error)
        else:
            error.printAll()

    def clear(self):
        self.errorStack = []


    def displayErrors(self, master=None):
        if len(self.errorStack)==0: return
        # fixme: we should show all errors or an interface to go betwen erros
        errMsg = self.errorStack[0].getAll(indent='', stack=self.showDetails)
        self.currentlyDisplayedError = 0

        if master:
            self.master = master
        else:
            self.master = None

        if len(self.errorStack)>1:
            buttons = ('Previous Error', 'Next Error', 'OK', 'Details >>')
        else:
            buttons = ('OK', 'Details >>')
        self.dialog = Pmw.Dialog(self.master,
                                 buttons = buttons,
                                 defaultbutton = 'OK',
                                 title = '%s Error(s)'%len(self.errorStack),
                                 command = self.execute)

        self.st = Pmw.ScrolledText(self.dialog.interior(),
                                   #labelpos = 'n',
                                   #label_text='%d Errors'%len(self.errorStack),
                                   #usehullsize = 1,
                                   #hull_width = 400,
                                   #hull_height = 300,
                                   #text_wrap='none',
                                   #text_font = fixedFont,
                                   #Header_font = fixedFont,
                                   #Header_foreground = 'blue',
                                   #rowheader_width = 3,
                                   #rowcolumnheader_width = 3,
                                   text_padx = 4,
                                   text_pady = 4,
                                   #Header_padx = 4,
                                   #rowheader_pady = 4,
                                   )
        for line in errMsg:
            self.st.insert('end', line+'\n')
        self.st.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
        self.dialog.activate(geometry = 'centerscreenalways')

            #self.master.pack()

    def execute(self, result):
        if result == 'OK':
            self.dialog.deactivate(result)
        elif result=='Next Error':
            if self.currentlyDisplayedError<len(self.errorStack)-1:
                self.currentlyDisplayedError += 1
                errMsg = self.errorStack[self.currentlyDisplayedError].getAll(indent='', stack=self.showDetails)
                self.st.delete('0.0', 'end')
                for line in errMsg:
                    self.st.insert('end', line+'\n')
        elif result=='Previous Error':
            if self.currentlyDisplayedError>0:
                self.currentlyDisplayedError -= 1
                errMsg = self.errorStack[self.currentlyDisplayedError].getAll(indent='', stack=self.showDetails)
                self.st.delete('0.0', 'end')
                for line in errMsg:
                    self.st.insert('end', line+'\n')
        elif result=='Details >>':
            self.showDetails = not self.showDetails
            errMsg = self.errorStack[self.currentlyDisplayedError].getAll(indent='', stack=self.showDetails)
            self.st.delete('0.0', 'end')
            for line in errMsg:
                self.st.insert('end', line+'\n')
            
try:
    _ErrorManager
except:
    _ErrorManager = ErrorManager('MGLTools')



if __name__=='__main__':
    #pm = ErrorManager('test')
    kw = {'value':35, 'line': 'Hello World 35', 'file':'myTestFile'}
    msg = "Unexpected value while parsing file"
    try:
        1/0.0
    except Exception, e:
        #print dir(e)
        #print e.args
        #print e.message
        #stack = traceback.format_stack()
        #pm.error(e, msg, stack, **kw)
        MGLError(e, msg, **kw)
        
    print
    print
    msg = "Let's make this error message very long to test if handling messages spanning multiple lines works effectively and the words get broken in the right place and it all look good.\nAnd lets throw in a new line to see what happens"
    l = range(24)
    try:
        l[100] = 3
    except Exception, e:
        #stack = traceback.format_stack()
        #pm.error(e, msg, stack)
        MGLError(e, msg)

    _ErrorManager.displayErrors()
