#!/usr/bin/env python
import sys, re, os.path, cgi, stat, math
from optparse import OptionParser
from color import getColorizer
class tblCell(object):
    def __init__(self, text, value = None, props = None):
        self.text = text
        self.value = value
        self.props = props
class tblColumn(object):
    def __init__(self, caption, title = None, props = None):
        self.text = caption
        self.title = title
        self.props = props
class tblRow(object):
    def __init__(self, colsNum, props = None):
        self.cells = [None] * colsNum
        self.props = props
def htmlEncode(str):
    return '
'.join([cgi.escape(s) for s in str])
class table(object):
    def_align = "left"
    def_valign = "middle"
    def_color = None
    def_colspan = 1
    def_rowspan = 1
    def_bold = False
    def_italic = False
    def_text="-"
    def __init__(self, caption = None):
        self.columns = {}
        self.rows = []
        self.ridx = -1;
        self.caption = caption
        pass
    def newRow(self, **properties):
        if len(self.rows) - 1 == self.ridx:
            self.rows.append(tblRow(len(self.columns), properties))
        else:
            self.rows[ridx + 1].props = properties
        self.ridx += 1
        return self.rows[self.ridx]
    def trimLastRow(self):
        if self.rows:
            self.rows.pop()
        if self.ridx >= len(self.rows):
            self.ridx = len(self.rows) - 1
    def newColumn(self, name, caption, title = None, **properties):
        if name in self.columns:
            index = self.columns[name].index
        else:
            index = len(self.columns)
        if isinstance(caption, tblColumn):
            caption.index = index
            self.columns[name] = caption
            return caption
        else:
            col = tblColumn(caption, title, properties)
            col.index = index
            self.columns[name] = col
            return col
    def getColumn(self, name):
        if isinstance(name, str):
            return self.columns.get(name, None)
        else:
            vals = [v for v in self.columns.values() if v.index == name]
            if vals:
                return vals[0]
        return None
    def newCell(self, col_name, text, value = None, **properties):
        if self.ridx < 0:
            self.newRow()
        col = self.getColumn(col_name)
        row = self.rows[self.ridx]
        if not col:
            return None
        if isinstance(text, tblCell):
            cl = text
        else:
            cl = tblCell(text, value, properties)
        row.cells[col.index] = cl
        return cl
    def layoutTable(self):
        columns = self.columns.values()
        columns.sort(key=lambda c: c.index)
        colspanned = []
        rowspanned = []
        self.headerHeight = 1
        rowsToAppend = 0
        for col in columns:
            self.measureCell(col)
            if col.height > self.headerHeight:
                self.headerHeight = col.height
            col.minwidth = col.width
            col.line = None
        for r in range(len(self.rows)):
            row = self.rows[r]
            row.minheight = 1
            for i in range(len(row.cells)):
                cell = row.cells[i]
                if row.cells[i] is None:
                    continue
                cell.line = None
                self.measureCell(cell)
                colspan = int(self.getValue("colspan", cell))
                rowspan = int(self.getValue("rowspan", cell))
                if colspan > 1:
                    colspanned.append((r,i))
                    if i + colspan > len(columns):
                        colspan = len(columns) - i
                    cell.colspan = colspan
                    #clear spanned cells
                    for j in range(i+1, min(len(row.cells), i + colspan)):
                        row.cells[j] = None
                elif columns[i].minwidth < cell.width:
                    columns[i].minwidth = cell.width
                if rowspan > 1:
                    rowspanned.append((r,i))
                    rowsToAppend2 = r + colspan - len(self.rows)
                    if rowsToAppend2 > rowsToAppend:
                        rowsToAppend = rowsToAppend2
                    cell.rowspan = rowspan
                    #clear spanned cells
                    for j in range(r+1, min(len(self.rows), r + rowspan)):
                        if len(self.rows[j].cells) > i:
                            self.rows[j].cells[i] = None
                elif row.minheight < cell.height:
                    row.minheight = cell.height
        self.ridx = len(self.rows) - 1
        for r in range(rowsToAppend):
            self.newRow()
            self.rows[len(self.rows) - 1].minheight = 1
        while colspanned:
            colspanned_new = []
            for r, c in colspanned:
                cell = self.rows[r].cells[c]
                sum([col.minwidth for col in columns[c:c + cell.colspan]])
                cell.awailable = sum([col.minwidth for col in columns[c:c + cell.colspan]]) + cell.colspan - 1
                if cell.awailable < cell.width:
                    colspanned_new.append((r,c))
            colspanned = colspanned_new
            if colspanned:
                r,c = colspanned[0]
                cell = self.rows[r].cells[c]
                cols = columns[c:c + cell.colspan]
                total = cell.awailable - cell.colspan + 1
                budget = cell.width - cell.awailable
                spent = 0
                s = 0
                for col in cols:
                    s += col.minwidth
                    addition = s * budget / total - spent
                    spent += addition
                    col.minwidth += addition
        while rowspanned:
            rowspanned_new = []
            for r, c in rowspanned:
                cell = self.rows[r].cells[c]
                cell.awailable = sum([row.minheight for row in self.rows[r:r + cell.rowspan]])
                if cell.awailable < cell.height:
                    rowspanned_new.append((r,c))
            rowspanned = rowspanned_new
            if rowspanned:
                r,c = rowspanned[0]
                cell = self.rows[r].cells[c]
                rows = self.rows[r:r + cell.rowspan]
                total = cell.awailable
                budget = cell.height - cell.awailable
                spent = 0
                s = 0
                for row in rows:
                    s += row.minheight
                    addition = s * budget / total - spent
                    spent += addition
                    row.minheight += addition
        return columns
    def measureCell(self, cell):
        text = self.getValue("text", cell)
        cell.text = self.reformatTextValue(text)
        cell.height = len(cell.text)
        cell.width = len(max(cell.text, key = lambda line: len(line)))
    def reformatTextValue(self, value):
        if isinstance(value, str):
            vstr = value
        elif isinstance(value, unicode):
            vstr = str(value)
        else:
            try:
                vstr = '\n'.join([str(v) for v in value])
            except TypeError:
                vstr = str(value)
        return vstr.splitlines()
    def adjustColWidth(self, cols, width):
        total = sum([c.minWidth for c in cols])
        if total + len(cols) - 1 >= width:
            return
        budget = width - len(cols) + 1 - total
        spent = 0
        s = 0
        for col in cols:
            s += col.minWidth
            addition = s * budget / total - spent
            spent += addition
            col.minWidth += addition
    def getValue(self, name, *elements):
        for el in elements:
            try:
                return getattr(el, name)
            except AttributeError:
                pass
            try:
                val = el.props[name]
                if val:
                    return val
            except AttributeError:
                pass
            except KeyError:
                pass
        try:
            return getattr(self.__class__, "def_" + name)
        except AttributeError:
            return None
    def consolePrintTable(self, out):
        columns = self.layoutTable()
        colrizer = getColorizer(out)
        if self.caption:
            out.write("%s%s%s" % ( os.linesep,  os.linesep.join(self.reformatTextValue(self.caption)), os.linesep * 2))
        headerRow = tblRow(len(columns), {"align": "center", "valign": "top", "bold": True, "header": True})
        headerRow.cells = columns
        headerRow.minheight = self.headerHeight
        self.consolePrintRow2(colrizer, headerRow, columns)
        for i in range(0, len(self.rows)):
            self.consolePrintRow2(colrizer, i, columns)
    def consolePrintRow2(self, out, r, columns):
        if isinstance(r, tblRow):
            row = r
            r = -1
        else:
            row = self.rows[r]
        #evaluate initial values for line numbers
        i = 0
        while i < len(row.cells):
            cell = row.cells[i]
            colspan = self.getValue("colspan", cell)
            if cell is not None:
                cell.wspace = sum([col.minwidth for col in columns[i:i + colspan]]) + colspan - 1
                if cell.line is None:
                    if r < 0:
                        rows = [row]
                    else:
                        rows = self.rows[r:r + self.getValue("rowspan", cell)]
                    cell.line = self.evalLine(cell, rows, columns[i])
                    if len(rows) > 1:
                        for rw in rows:
                            rw.cells[i] = cell
            i += colspan
        #print content
        for ln in range(row.minheight):
            i = 0
            while i < len(row.cells):
                if i > 0:
                    out.write(" ")
                cell = row.cells[i]
                column = columns[i]
                if cell is None:
                    out.write(" " * column.minwidth)
                    i += 1
                else:
                    self.consolePrintLine(cell, row, column, out)
                    i += self.getValue("colspan", cell)
            out.write(os.linesep)
    def consolePrintLine(self, cell, row, column, out):
        if cell.line < 0 or cell.line >= cell.height:
            line = ""
        else:
            line = cell.text[cell.line]
        width = cell.wspace
        align = self.getValue("align", ((None, cell)[isinstance(cell, tblCell)]), row, column)
        if align == "right":
            pattern = "%" + str(width) + "s"
        elif align == "center":
            pattern = "%" + str((width - len(line)) / 2 + len(line)) + "s" + " " * (width - len(line) - (width - len(line)) / 2)
        else:
            pattern = "%-" + str(width) + "s"
        out.write(pattern % line, color = self.getValue("color", cell, row, column))
        cell.line += 1
    def evalLine(self, cell, rows, column):
        height = cell.height
        valign = self.getValue("valign", cell, rows[0], column)
        space = sum([row.minheight for row in rows])
        if valign == "bottom":
            return height - space
        if valign == "middle":
            return (height - space + 1) / 2
        return 0
    def htmlPrintTable(self, out, embeedcss = False):
        columns = self.layoutTable()
        if embeedcss:
            out.write("
| \n" % (attr, css)) if th is not None: out.write(" %s\n" % htmlEncode(th.text)) out.write("\n") out.write(" | 
|---|
| \n" % (attr, css)) if td is not None: out.write(" %s\n" % htmlEncode(td.text)) out.write("\n") i += colspan out.write(" |