"""
/*
* gentabtex.py 1.2
* gentabtex.py: high layer interface for rendering LaTeX tables.
*
* Copyright (C) Manuel Gutierrez Algaba,  2004, [email protected]
*
* 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.
*/
[email protected]
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()