#!/usr/bin/env python

#############################################################################
##
# This file is part of Taurus
##
# http://taurus-scada.org
##
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
##
# Taurus 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 Lesser General Public License for more details.
##
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################

""" """

__all__ = ["Table"]

__docformat__ = "restructuredtext"


class Table:

    DefTermWidth = 80

    PrettyOpts = {'col_sep': ' |', 'col_head_sep': '-', 'border': '='}

    def __init__(self, elem_list, elem_fmt=None, term_width=None,
                 row_head_str=None, row_head_fmt='%-*s', row_head_width=None,
                 col_head_str=None, col_head_fmt='%*s',  col_head_width=None,
                 col_sep=' ', row_sep=' ', col_head_sep=None, border=None):

        self.nr_col = len(elem_list)
        self.nr_row = len(elem_list[0])
        self.elem_list = elem_list

        if elem_fmt is None:
            elem_fmt = self.nr_row * ['%*s']
        if len(elem_fmt) == 1:
            elem_fmt *= self.nr_row
        self.elem_fmt = elem_fmt

        self.term_width = term_width or Table.DefTermWidth
        self.col_sep = col_sep
        self.row_sep = row_sep
        self.col_head_sep = col_head_sep
        self.border = border

        max_len_fn = lambda x: reduce(max, map(len, x))

        self.row_head_str = row_head_str
        self.row_head_fmt = row_head_fmt
        if row_head_str is not None and len(row_head_str) != self.nr_row:
            msg = 'RowHeadStr nr (%d) and RowNr (%d) mistmatch' % \
                  (len(row_head_str), self.nr_row)
            raise ValueError, msg
        if row_head_width is None:
            if row_head_str is not None:
                row_head_width = max_len_fn(row_head_str)
            else:
                row_head_width = 0
        self.row_head_width = row_head_width

        self.col_head_str = col_head_str
        self.col_head_fmt = col_head_fmt
        if col_head_str is not None and len(col_head_str) != self.nr_col:
            msg = 'ColHeadStr nr (%d) and ColNr (%d) mistmatch' % \
                  len(col_head_str), self.nr_col
            raise ValueError, msg
        if col_head_width is None:
            if col_head_str is not None:
                col_head_width = reduce(max, map(max_len_fn, col_head_str))
            else:
                col_head_width = 10
        self.col_head_width = col_head_width
        if col_head_str is not None:
            self.col_head_lines = len(col_head_str[0])
        else:
            self.col_head_lines = 0

    def updateElem(self, elem_list):
        new_col, new_row = len(elem_list), len(elem_list[0])
        if new_col != self.nr_col or new_row != self.nr_row:
            raise 'Invalid new elem list size %dx%d, was %dx%d' % \
                  (new_col, new_row, self.nr_col, self.nr_row)
        self.elem_list = elem_list

    def genOutput(self, term_width=None):
        if term_width is None:
            term_width = self.term_width

        rhw, chw = self.row_head_width, self.col_head_width
        chl = self.col_head_lines
        lcs = len(self.col_sep)
        width = term_width - chw   # At least one disp column!
        if rhw > 0:
            width -= rhw + lcs
        disp_cols = width / (chw + lcs) + 1
        tot_width = chw + (disp_cols - 1) * (chw + lcs)
        tot_rows = chl + self.nr_row
        if rhw > 0:
            tot_width += rhw + lcs

        output = []

        if self.row_head_str is not None:
            row_head = []
            fmt = self.row_head_fmt
            for head in [''] * chl + self.row_head_str:
                head = fmt % (rhw, head)
                row_head.append(head + self.col_sep)
        else:
            row_head = [''] * tot_rows

        for i in xrange(0, self.nr_col, disp_cols):
            if i > 0:
                nr_sep = tot_width / len(self.row_sep)
                output.append(self.row_sep * nr_sep)

            row_end = min(i + disp_cols, self.nr_col)
            line = list(row_head)
            for j in xrange(i, row_end):
                elem = self.elem_list[j]
                if chl:
                    col_head = self.col_head_str[j]
                    if j > i:
                        for k in xrange(tot_rows):
                            line[k] += self.col_sep
                    fmt = self.col_head_fmt
                    for k in xrange(chl):
                        line[k] += fmt % (chw, col_head[k])

                for k in xrange(self.nr_row):
                    fmt = self.elem_fmt[k]
                    line[chl + k] += fmt % (chw, elem[k])

            max_width = reduce(max, map(len, line))
            if self.border is not None:
                nr_border = max_width / len(self.border)
                output.append(self.border * nr_border)
            for l in line[:chl]:
                output.append(l)
            if self.col_head_sep is not None:
                nr_sep = max_width / len(self.col_head_sep)
                output.append(self.col_head_sep * nr_sep)
            for l in line[chl:]:
                output.append(l)
            if self.border is not None:
                output.append(self.border * nr_border)

        return output
