"""
Pretty printer setup helper module
"""

import sys
from types import StringType

class PrettyPrinter:
    """
    This class provides an elegant way to pretty print
    transparently what goes to stdout. It performs:

    - Line-wrap at specified column. Dynamically changeable
      via PrettyPrinter.column_wrap
    - Semi-automated tabular formating. A single print statement
      is tabbed accorging to first emmited characters. This is
      a very usefull feature for automated python code generation.
    - Pretty print toggle On and Off via PrettyPrinter.state boolean.
    - Transparent output duplication, configurable at object creation.
    """
    __command=''
    __tee=None
    __indentation=''
    __chunks=[]
    
    class Tee:
        """
        Similar to its infamous Unix counterpart, this is a class
        that writes a file twice: on stdout and on an other
        file specified at initialisation
        """
        __stdout=None
        __command=''
        def __init__(self, filehandle, filename=None):
            self.__old_stdout=filehandle
            if filename:
                self.__stdout=file(filename,'w')
        def __del__(self):
            if self.__stdout: self.__stdout.close()
        def write(self,str):
            if str=='\n':
                if self.__stdout: print >> self.__stdout, self.__command
                print >> self.__old_stdout, self.__command
                self.__command=''
            else:
                self.__command+=str

    def __init__(self, column_wrap=80, filename=None, init_state=False):
        """
        Instanciating an object of this class with proper parameters
        is everything you need to do:

        pp=PrettyPrinter(80,None,True)

        and every print statement in transparently 'pretty-printed'
        on heighty columns.
        """
        self.__tee=self.Tee(sys.stdout,filename)
        if column_wrap<=0:
            raise ValueError, 'Column wrap have to be greater than zero'
        self.column_wrap=column_wrap
        self.state=init_state
        sys.stdout=self
        
    def __del__(self):
        sys.stdout=self.__tee._Tee__old_stdout

    def write(self,command):
        if not self.state:
            print >> self.__tee , command ,
            return
        
        chunks=map(lambda x: x.split(), command.split('\n'))
        if len(chunks)>0:
            if len(self.__chunks)>0:
                chunks[0][:0]=self.__chunks[-1]
                self.__chunks.pop()
                chunks[:0]=self.__chunks
            else:
                for i in command:
                    if i=='\t':
                        self.__indentation+=i
                    else:
                        break
            self.__chunks=chunks
        
        if command=='\n':
            # Everything has to be flushed
            self.__chunks=[x for x in self.__chunks if len(x)>0]
            self._output_chunks(self.__chunks,0,self.__indentation, True)
#           print >> self.__tee._Tee__old_stdout, self.__chunks 
            self.__chunks=[]
            self.__indentation=''
        
    def _output_chunks(self, chunks,count, indentation, init=False):
        if not init: count+=self._indent(indentation)
        for i in range(len(chunks)):
            chunk=chunks[i]
            if self._is_sequence(chunk):
                self._output_chunks(chunk,count,indentation)
                print >> self.__tee
                if i!=len(chunks)-1: count=self._indent(indentation)
            else:
                if count+len(str(chunk))>=self.column_wrap:
                    print >> self.__tee
                    count=self._indent(indentation)
                print >> self.__tee, chunk,
                count+=len(str(chunk))+1

    def _indent(self,indentation):
        if len(indentation)>0:
            print >> self.__tee, indentation,
        return (len(indentation)*8)+1
    
    def _is_sequence(self,object):
        try:
            test = object[0:0]
        except:
            return False
        else:
            return type(object)!=StringType
