
"""
/*
 * gentabtex.py 1.2
 * gentabtex.py: high layer interface for rendering LaTeX tables.
 *
 * Copyright (C) Manuel Gutierrez Algaba,  2004, algaba@seul.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
*/
carmonense@andaluciajunta.es
changes
version : date:
0.1 : March 2004: added automatical balancing of columns.
1.2 : January 2006: added operators todas todasmenos en desdehasta
"""

separador = " }& "
terminador = r"\\" + "\n"

class distribuidor:
    class pesocolumna:
        def __init__(self):
            self.filas = []
        def anyadefila(self, fila):
            self.filas.append(fila)
        def dapeso(self):
            s= 0
            for i in self.filas:
                s += len(i)
            return s

        def dapesonor(self):
            return self. pesonormalizado

        def hazpesonor(self, p):
            self. pesonormalizado = p 
            
        def normalizapeso(self, total, factor):
            self.pesonormalizado =  factor * ( self.dapeso() * 1.0  / total)
            #print "Peso normalizado",  self.pesonormalizado, "total ", total, "factor", factor, "peso", self.dapeso(), "relativo", ( self.dapeso() * 1.0  / total)

        def __cmp__(self, o):
            p=  self.dapeso()
            po = o.dapeso()
            if p<po:
                return -1
            else:
                return 1
        
    def __init__(self):
        self.listacolumnas=[]

    def anyadecolumna(self):
        self.listacolumnas. append( distribuidor.pesocolumna())

    def anyadeencolumna(self, fila):
        self.listacolumnas[-1].anyadefila(fila)
        
    def calculapesototal(self):
        t = 0 
        for i in self.listacolumnas:
            t += i.dapeso()
        return t

    def calculapesorel(self, lis):
        t = 0 
        for i in lis:
            t += i.dapeso()
        return t

    def calculapeso(self, columna):
        return self.listacolumnas[columna].dapeso()

    def imprimepesos(self):
        print "Imprimiendo pesos"
        for i in self.listacolumnas:
            print i.dapeso()

            
    def asignapesos(self, anchura=60):
        t = self.calculapesototal()
        for i in self.listacolumnas:
            #print i.dapeso()
            i.dapeso()

    def repartepesos(self, tamanyototal, tamanyominimo):
        l = self.listacolumnas[:]
        l.sort()
        return self.recrepartepesos1(tamanyototal, tamanyominimo, l )

    def recrepartepesos1(self, tamanyototal, tamanyominimo,  columnas):
        if columnas==[]: return []
        total = self. calculapesorel(columnas)
        for i in [columnas[0]]:
            i. normalizapeso( total, tamanyototal * 1.0)
        return self.recrepartepesos( tamanyototal, tamanyominimo, columnas)
    
    def recrepartepesos(self, tamanyototal, tamanyominimo, restante):
        """
        """
        pesocolprimera= restante[0].dapesonor() 
        if pesocolprimera < tamanyominimo:
            restante[0]. hazpesonor(tamanyominimo )
            return [ tamanyominimo ] + self.recrepartepesos1(tamanyototal - tamanyominimo,
                                                         tamanyominimo, restante[1:])
        else:
            return [pesocolprimera ] +  self.recrepartepesos1(tamanyototal - pesocolprimera,
 tamanyominimo, restante[1:])


    def dapesosformatostex(self):
        dev= "{"
        for i in self.listacolumnas:
            dev += "p{" + "%2.2f" % i.dapesonor()  +"cm}"
        #print dev
        return dev +"}"
    
class gentabtex:
    class matrizdispersa:
        """
           a(y,x )
           -------> x                      
           !
           !
           !
           !
           V
           y
        """
        def calculapesos(self):
            self.dist = distribuidor()
            for j in xrange(0, self.dimx()):
                self.dist.anyadecolumna()
                for i in xrange(0, self.dimy()):
                    if self.mmatriz. has_key((i,j)):
                        self.dist.anyadeencolumna(self.mmatriz[(i,j)])

        def imprimepesos(self):
            self.dist.imprimepesos()
            
        def __init__(self):
            self.mmatriz = {}
        def anyade(self, posx, posy, el):
            self.mmatriz[(posy, posx) ] = el

        def __getitem__(self, dupla):
            return self.mmatriz[dupla]
            
        def dimx(self):
            return max(map( lambda(x):x.__getitem__(1), (self.mmatriz.keys()))) + 1
        
        def dimy(self):
            return max(map( lambda(x):x.__getitem__(0), (self.mmatriz.keys()))) + 1

        def __str__(self):
            #print "Dimensiones", self.dimy(), self.dimx()
            a= ""
            for i in xrange(0, self.dimy()):
                for j in xrange(0, self.dimx()):
                    if self.mmatriz. has_key((i,j)):
                        if type(self.mmatriz[(i,j)]) != type(""):
                            a += str(self.mmatriz[(i,j)])
                        else:
                            a += self.mmatriz[(i,j)]
                            
                    a += separador
                a = a[:-(len(separador))]
                a += terminador
            return a
        def imprime(self):
            print self.mmatriz
        
        def repartepesos(self, tamanayototal, tamanyominimo):
            return self.dist. repartepesos( tamanayototal, tamanyominimo)

        def dapesosformatostex(self):
            return self. dist. dapesosformatostex()
            
    
    class filasenorden:
        def __init__(self, columna=0): 
            self.filaorden=1
            self.listafilas=[]
            self.columna= columna
            
        def anyade(self, t):
            self.filaorden += 1
            self.listafilas.append(t)

        def devnumerofilas(self):
            return len(self.listafilas)

        def devfila(self, nfila, col):
            if col==self.columna:
                return self.listafilas[nfila]

        def escribeenmatriz(self, matriz):
            j = 1
            for i in self.listafilas:
                matriz. anyade( self.columna, j, i )
                j += 1
                
    class columnasenorden:
        def __init__(self, fila=0): 
            self.columnaorden=1
            self.listacolumnas=[]
            self.fila= fila
            
        def anyade(self, t):
            self.columnaorden += 1
            self.listacolumnas.append(t)

        def devnumerocolumnas(self):
            return len(self.listacolumnas)

        def devcolumna(self, nfila, col):
            if nfila==self.fila:
                return self.listacolumnas[col]

        def escribeenmatriz(self, matriz):
            j = 0
            for i in self.listacolumnas:
                matriz. anyade( j, self.fila, i )
                j += 1


    class cequis:
        def __init__(self, columna, filas, columnanum=None, extendido= None):
            self.columna = columna 
            self.filas = filas 
            self. columnanum = columnanum
            self. extendido = extendido

        def escribeenmatriz(self, matriz):
            if self.columna=="ultimacolumna":
                self.escribe1(matriz)
            else:
                if not self. columnanum is None:
                    self. escribe2(matriz)
                elif self.columna == "todafila":
                    #return
                    for i in xrange(0,matriz.dimy()):
                        puedeescribir = 0
                        #print self.extendido, self.filas
                        for j in self.filas:
                            if self.esvalido(j, (i,0), matriz):
                                puedeescribir = 1
                                break
                        if puedeescribir:
                            for c in xrange(1, matriz.dimx()):
                                matriz.anyade( c, i,  'x')
                    #print "trastodafila" , matriz

        def escribe2(self, matriz):
            def esextendidovalido(matriz, i):
                for j in self. extendido:
                    if  self.esvalido( j, (i, 0), matriz ):
                        return 1
            c= self. columnanum - 1
            if self.filas == "todas" or self.filas=="toda":
                for i in xrange(1,matriz.dimy()):
                    matriz.anyade(  c + 1, i , 'x')
            if self.filas == "todamenos" or self.filas=="todasmenos":
                for i in xrange(1,matriz.dimy()):
                    puedeescribir = 1
                    #print self.extendido
                    for j in self. extendido:
                        if  self.esvalido( j, (i, 0), matriz ):
                            puedeescribir = 0
                    if puedeescribir:
                        matriz.anyade(  c+1, i, 'x')
            elif self.filas=="desdehasta":
                #print "desdehasta"
                def escribetramovalido(matriz, j,c):
                    for k in xrange(j, matriz.dimy()):
                        matriz.anyade(  c+1, k, 'x')
                        if esextendidovalido(matriz, k):
                            break
                for i in xrange(0,matriz.dimy()):
                    #print self.extendido
                    if esextendidovalido( matriz, i):
                        matriz.anyade(  c+1, i, 'x')
                        escribetramovalido(matriz, i+1, c)
                        break
            elif self.filas=="en":
                for i in xrange(0,matriz.dimy()):
                    puedeescribir = 0
                    for j in self. extendido:
                        if  self.esvalido( j, (i, 0), matriz ):
                            puedeescribir = 1
                    if puedeescribir:
                        matriz.anyade(  c+1, i, 'x')
                
                        

        def esvalido(self, duplacond, duplacoor, matriz):
            """ duplacond es de la forma:
            ('c', 'subcarpeta')
            duplacoor:
            (0, 1) (y, x)
            matriz:{(9, 0): 'Eliminar subcarpetas y archivos', (8, 0): 'Atributos extendidos de escritorio', (3, 0): 'Atributos de lectura' ... }
            Se trata de decidir si en matriz, en la posicion (0,1) hay un texto que
            contenga la cadena 'subcarpeta' 
            
            """
            try:
                s = matriz[duplacoor]
            except KeyError:
                return 0
            #matriz.imprime()
            #print "duplacond",  str(s), duplacond, duplacoor
            if duplacond[0]=='c' and type(s)==type(""):
                return s.find(duplacond[1] )!=-1
            
        def escribe1(self, matriz):
            c = matriz.dimx() - 1
            j = 1
            for i in self.filas:
                matriz.anyade(c, j, i)
                j +=1
            
            
    class ccabecera:
        def __init__(self, lista ):
            self.lista= lista

        def escribeenmatriz(self, matriz):
            j = 0
            #print len(self.lista)
            for  i in self.lista :
                matriz. anyade(  j, 0, i )
                j +=1
            
        def ncolumnas(self):
            return len(self.lista)
        
    def __init__(self):
        self.lamatrizdispersa= gentabtex.matrizdispersa()
        self. ofilasenorden = gentabtex.filasenorden()
        self. todasfilasenorden = [ self. ofilasenorden ]
        self. todosequis = []
        self. ocolumnasenorden = gentabtex.columnasenorden()
        self. todascolumnasenorden = [ self. ocolumnasenorden ]
        self. aspcab =""
        self. acoletilla=""

    def coletilla(self, colt):
        self. acoletilla = colt
        return self
    
    def sync(self):
        for i in self. todasfilasenorden:
            i. escribeenmatriz(self. lamatrizdispersa)
        for i in self. todascolumnasenorden:
            i. escribeenmatriz(self. lamatrizdispersa)
        self.ocabecera. escribeenmatriz(self. lamatrizdispersa)
        for i in self. todosequis:
            i. escribeenmatriz(self. lamatrizdispersa
)
        return self
    def __str__(self):
        if self.aspcab=="":
            self.aspcab="{" + "l" * self. lamatrizdispersa.dimx() +"}"
        a=r"""\begin{tabular}""" + self.aspcab + "\n"
        a += str(self. lamatrizdispersa)
        a+= r"""\end{tabular}"""
        a+=self. acoletilla
        return a

    def imprimepesos(self):
        self. lamatrizdispersa. imprimepesos()
        
    def otrafilaenorden(self,col):
        self. ofilasenorden = gentabtex.filasenorden(col)
        self. todasfilasenorden. append(self. ofilasenorden)
        return self

    def otracolumnaenorden(self,col):
        self. ocolumnasenorden = gentabtex.columnasenorden(col)
        self. todascolumnasenorden. append(self. ocolumnasenorden)
        return self

    def otraen(self, columna):
        return self. otrafilaenorden(columna)
    
    def columnas(self, acolumna ):
        self. ofilasenorden. columna= acolumnas
        return self

    def co(self, acolumna):
        return self. columnas(acolumna)

    def anchuras(self, nanchuras ):
        self.nanchuras = anchuras
        return self

    def enfilaorden(self, dato):
        self. ofilasenorden.anyade(dato)
        return self

    def encolumnaorden(self,dato):
        self. ocolumnasenorden.anyade(dato)
        return self

    def en(self, dato):
        if type(dato) == type([]):
            for i in dato:
                self.enfilaorden(i)
            return self
        else:
            return self. enfilaorden(dato)

    def fil(self, dato):
        if type(dato) == type([]):
            for i in dato:
                self.encolumnaorden(i)
            self. otracolumnaenorden(self. ocolumnasenorden.fila + 1)
            return self
        else:
            return self. encolumnaorden(dato)

    def cabecera(self, listacab):
        self.ocabecera= gentabtex.ccabecera(listacab)
        return self

    def cab(self,listacab):
        return self.cabecera(listacab)

    def equis(self, columna, filas, extendido=None):
        if type(columna)==type(1):
            self.todosequis.append(gentabtex.cequis(None, filas, columnanum=columna, extendido=extendido))
        else:
            self.todosequis.append(gentabtex.cequis(columna, filas))
        return self

    def defaspectocolumna(self,  asp):
        """
        {p{2cm}p{2cm}p{0.5cm}p{0.5cm}p{0.5cm}p{0.5cm}}
        """
        self. aspcab = asp
        return self

    def daaspectautoma(self):
        self. aspcab = self. lamatrizdispersa. dapesosformatostex()

        return self
    
    def repartepesos(self, tamanyototal, tamanyominimo):
        self. lamatrizdispersa. repartepesos(tamanyototal, tamanyominimo)
        return self

    def calculapesos(self):
        self. lamatrizdispersa. calculapesos()
        return self
    
if __name__=='__main__':
    import sys
    f=open("tablasejemplo.tex", "w")
    #sys.stdout=f
    separador = " & "
    g = gentabtex().en("rojo").en("azul").en("verde").en("amarillo"). \
cab(["colores", "buen gusto", "coche", "gallumbos", "fruta"]).otraen(1). \
en(["es dificil conseguir algo que no sobresalte y quede bien",
    "los pantalones, camisas, color discreto donde los haya",
    "si no es fuerte o amerillento",
    "hortero en la mayoria de las ocasiones"]).otraen(2).\
    en(["implica conduccion agresiva",
        "es un color que como tal rara vez se da, es mas corriente en los nortes",
        "no esta de moda",
        "es el color que mejor se ve"]).\
equis("ultimacolumna", ["tomate", "alga", "melon", "melon"]).\
defaspectocolumna("")

    g.sync()
    print g
    print r"\\"
    g. lamatrizdispersa. calculapesos()
    #g.imprimepesos()    
    g.repartepesos(8, 0.3)
    g. daaspectautoma()
    print g
    print r"\\"

    g2 = gentabtex().en(["Recorrer carpetas/Ejecutar archivo",
                         "Listar carpeta/Leer datos",
                         "Atributos de lectura", "Atributos extendidos de lectura",
                         "Crear archivos/Escribir datos", "Crear carpetas/Anexar datos",
                         "Atributos de escritura", "Atributos extendidos de escritura",
                         "Eliminar subcarpetas y archivos", "Eliminar",
                         "Permisos de lectura", "Cambiar permisos",
                         "Tomar posesión", "Sincronizar"]).\
cab([0,1,2,3,4,5,6]). \
equis(1, "toda").equis(2, "todamenos", [('c', 'subcarpeta'),
                                        ('c', 'Cambiar permisos')]).\
 equis("todafila", [('c', 'Permisos de lectura'),
                    ('c', 'Sincronizar')]).\
 equis(3, "desdehasta", (('c',"Recorrer"),('c', "extendidos"))).\
  equis(4, "desdehasta", (('c',"Recorrer"),('c', "extendidos"))).\
 equis(5, "desdehasta", (('c',"Listar"),('c', "extendidos"))).\
 equis(5, "en", [('c',"extendidos de escritura")]).\
 equis(6, "desdehasta", (('c',"Crear archivos"),('c', "Atributos de escritura"))).\
 coletilla("""\\\\
1 control total
 
2 modificar
 
3 leer y ejecutar
 
4 listar el contenido de la carpeta
 
5 lectura
 
6 escribir
 
""").\
 sync()
# daaspectautoma()
#calculapesos(). repartepesos(6, 0.5). 
    print g2
    #print r"\\"

    tabcabtec = gentabtex().cab(["Protocolo", "Velocidad nominal",
                                "Frecuencia base", "Capacidad exigible al cable",
                                "Categoría de cable requerida"]).\
                                fil([ "10Base-T", "10 Mbps", "10 MHz", "10 MHz", "Cat-3"]).\
                                fil(["100Base-T4", "100 Mbps", "12.5 MHz", "12.5 MHz", "Cat-3"]).\
                                fil(["802.12 (VG)", "100 Mbps", "15 MHz", "15 MHz", "Cat-3"]).\
                                fil(["100Base-TX", "100 Mbps", "31.25 MHz", "80 MHz", "Cat-5"]).\
                                fil(["FDDI  (*)", "100 Mbps", "31.25 MHz", "80 MHz", "Cat-5"]).\
                                fil(["ATM  (**)", "155 Mbps", "77.5 MHz", "100 MHz", "Cat-5"]).\
          sync().calculapesos().repartepesos(6, 0.5). daaspectautoma()
    print tabcabtec
    f.close()
