# -*- coding: utf-8 -*-
"""
Logger class for IPython's logging facilities.

$Id: Logger.py,v 1.17 2004/11/30 08:52:05 fperez Exp $
"""

#*****************************************************************************
#       Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
#       Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#*****************************************************************************

#****************************************************************************
# Modules and globals

from IPython import Release
__author__  = '%s <%s>\n%s <%s>' % \
              ( Release.authors['Janko'] + Release.authors['Fernando'] )
__license__ = Release.license

# Python standard modules
import os,sys,glob

# Homebrewed
from IPython.genutils import *

#****************************************************************************
# FIXME: The logger class shouldn't be a mixin, it throws too many things into
# the InteractiveShell namespace. Rather make it a standalone tool, and create
# a Logger instance in InteractiveShell that uses it. Doing this will require
# tracking down a *lot* of nasty uses of the Logger attributes in
# InteractiveShell, but will clean up things quite a bit.

class Logger:
    """A Logfile Mixin class with different policies for file creation"""

    # FIXME: once this isn't a mixin, log_ns should just be 'namespace', since the
    # names won't collide anymore.
    def __init__(self,log_ns):
        self._i00,self._i,self._ii,self._iii = '','','',''
        self.do_full_cache = 0 # FIXME. There's also a do_full.. in OutputCache
        self.log_ns = log_ns
        # defaults
        self.LOGMODE = 'backup'
        self.defname = 'logfile'
        
    def create_log(self,header='',fname='',defname='.Logger.log'):
        """Generate a new log-file with a default header"""
        if fname:
            self.LOG = fname

        if self.LOG:
            self.logfname = self.LOG
        else:
            self.logfname = defname
        
        if self.LOGMODE == 'over':
            if os.path.isfile(self.logfname):
                os.remove(self.logfname) 
            self.logfile = open(self.logfname,'w')
        if self.LOGMODE == 'backup':
            if os.path.isfile(self.logfname):
                backup_logname = self.logfname+'~'
                # Manually remove any old backup, since os.rename may fail
                # under Windows.
                if os.path.isfile(backup_logname):
                    os.remove(backup_logname)
                os.rename(self.logfname,backup_logname)
            self.logfile = open(self.logfname,'w')
        elif self.LOGMODE == 'global':
            self.logfname = os.path.join(self.home_dir, self.defname)
            self.logfile = open(self.logfname, 'a')
            self.LOG = self.logfname
        elif self.LOGMODE == 'rotate':
            if os.path.isfile(self.logfname):
                if os.path.isfile(self.logfname+'.001~'): 
                    old = glob.glob(self.logfname+'.*~')
                    old.sort()
                    old.reverse()
                    for f in old:
                        root, ext = os.path.splitext(f)
                        num = int(ext[1:-1])+1
                        os.rename(f, root+'.'+`num`.zfill(3)+'~')
                os.rename(self.logfname, self.logfname+'.001~')
            self.logfile = open(self.logfname,'w')
        elif self.LOGMODE == 'append':
            self.logfile = open(self.logfname,'a')
            
        if self.LOGMODE != 'append':
            self.logfile.write(header)
        self.logfile.flush()

    def logstart(self, header='',parameter_s = ''):
        if not hasattr(self, 'LOG'):
            logfname = self.LOG or parameter_s or './'+self.defname
            self.create_log(header,logfname)
        elif parameter_s and hasattr(self,'logfname') and \
             parameter_s != self.logfname:
            self.close_log()
            self.create_log(header,parameter_s)
            
        self._dolog = 1

    def switch_log(self,val):
        """Switch logging on/off. val should be ONLY 0 or 1."""

        if not val in [0,1]:
            raise ValueError, \
                  'Call switch_log ONLY with 0 or 1 as argument, not with:',val
        
        label = {0:'OFF',1:'ON'}

        try:
            _ = self.logfile
        except AttributeError:
            print """
Logging hasn't been started yet (use %logstart for that).

%logon/%logoff are for temporarily starting and stopping logging for a logfile
which already exists. But you must first start the logging process with
%logstart (optionally giving a logfile name)."""
            
        else:
            if self._dolog == val:
                print 'Logging is already',label[val]
            else:
                print 'Switching logging',label[val]
                self._dolog = 1 - self._dolog

    def logstate(self):
        """Print a status message about the logger."""
        try:
            logfile = self.logfname
        except:
            print 'Logging has not been activated.'
        else:
            state = self._dolog and 'active' or 'temporarily suspended'
            print """
File:\t%s
Mode:\t%s
State:\t%s """ % (logfile,self.LOGMODE,state)

        
    def log(self, line,continuation=None):
        """Write the line to a log and create input cache variables _i*."""

        # update the auto _i tables
        #print '***logging line',line # dbg
        #print '***cache_count', self.outputcache.prompt_count # dbg
        input_hist = self.log_ns['_ih']
        if not continuation and line:
            self._iii = self._ii
            self._ii = self._i
            self._i = self._i00
            # put back the final \n of every input line
            self._i00 = line+'\n'
            #print 'Logging input:<%s>' % line  # dbg
            input_hist.append(self._i00)

        # hackish access to top-level namespace to create _i1,_i2... dynamically
        to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
        if self.do_full_cache:
            in_num = self.outputcache.prompt_count
            # add blank lines if the input cache fell out of sync. This can happen
            # for embedded instances which get killed via C-D and then get resumed.
            while in_num >= len(input_hist):
                input_hist.append('\n')
            new_i = '_i%s' % in_num
            if continuation:
                self._i00 = '%s%s\n' % (self.log_ns[new_i],line)
                input_hist[in_num] = self._i00
            to_main[new_i] = self._i00
        self.log_ns.update(to_main)
        
        if self._dolog and line:
            self.logfile.write(line+'\n')
            self.logfile.flush()

    def close_log(self):
        if hasattr(self, 'logfile'):
            self.logfile.close()
            self.logfname = ''
