# Path: ColumnsTable.py
# class ColumnsTable(object):
#     def __init__(self, columns, verbose=False):
#         'columns is an iterable with elements (name, width, formatter, (header-list), legend)'
#         self._columns = []
#         self._number_of_header_lines = 0
#         self._column_names = set()
#         for column_def in columns:
#             assert len(column_def) == 5, 'column_def must have length 5: %s' % str(column_def)
#             ctf = ColumnsTableFields(*column_def)
#             headers = ctf.headers
#             # convert 'abc' to ('abc',)
#             headers = (headers,) if isinstance(headers, str) else headers
#             if self._number_of_header_lines != 0:
#                 if self._number_of_header_lines != len(headers):
#                     print '+++++++++++++++++++++'
#                     print 'inconsistent number of lines in header'
#                     print 'have both %d and %d' % (self._number_of_header_lines, len(headers))
#                     print 'found at header: %s' % headers
#                     print '+++++++++++++++++++++'
#                     pdb.set_trace()
#             self._number_of_header_lines = len(headers)
#             self._columns.append(ColumnsTableFields(ctf.name,
#                                                     ctf.width,
#                                                     ctf.formatter,
#                                                     headers,
#                                                     ctf.legend))
#             if ctf.name in self._column_names:
#                 print '+++++++++++++++++++++'
#                 print 'column name already defined: %s' % ctf.name
#                 print '+++++++++++++++++++++'
#                 pdb.set_trace()
#             self._column_names.add(ctf.name)
#         self._lines = []
#         self._verbose = verbose
#         self._header()
# 
#     def append_legend(self, prefix_width=20):
#         def cat_headers(headers):
#             text = ''
#             for header in headers:
#                 if len(text) > 0:
#                     text += ' '
#                 text += header.strip()
#             return text
# 
#         self.append_line(' ')
#         self.append_line('column legend:')
#         for cd in self._columns:
#             # TODO: replace fix width below with value detemined from column headings
#             format = '%%%ds -> %%s' % prefix_width
#             line = format % (cat_headers(cd.headers), cd.legend)
#             self.append_line(line)
# 
#     def iterlines(self):
#         for line in self._lines:
#             yield line
# 
#     def append_detail(self, **kwds):
#         line = ''
#         # detect and report extra arguments
#         for k in kwds.keys():
#             if k not in self._column_names:
#                 print 'Column %s is not defined in the ColumnTable' % k
#                 print 'defined column names are', self._column_names
#                 pdb.set_trace()
#         # format values in kwds
#         for cd in self._columns:
#             if cd.name in kwds and kwds[cd.name] is not None:
#                 try:
#                     glyph = cd.formatter % kwds[cd.name]
#                 except TypeError as e:
#                     print '+++++++++++++++++++++++++'
#                     print TypeError, e
#                     print 'formatter  : %s' % cd.formatter
#                     print 'field name : %s' % cd.name
#                     print 'field value: %s' % kwds[cd.name]
#                     print '+++++++++++++++++++++++++'
#                     pdb.set_trace()
#             else:
#                 # if missing or None, print spaces for the value
#                 glyph = ' ' * cd.width
#             if len(line) > 0:
#                 line += ' '
#             line += glyph
#         self.append_line(line)
# 
#     def append_line(self, line):
#         if self._verbose:
#             print line
#         self._lines.append(line)
# 
#     def _header(self):
#         def append_header(index):
#             line = ''
#             for cd in self._columns:
#                 formatter = '%' + str(cd.width) + 's'
#                 formatted = formatter % cd.headers[index]
#                 line += (' ' if len(line) > 0 else '') + formatted
#             self.append_line(line)
# 
#         for header_line_index in xrange(self._number_of_header_lines):
#             append_header(header_line_index)
# 
#     def _print(self):
#         for line in self._lines:
#             print line
# 
# Path: Report.py
# class Report(object):
#     def __init__(self, also_print=True):
#         self._also_print = also_print
#         self._lines = []
# 
#     def append(self, line):
#         self._lines.append(line)
#         if self._also_print:
#             print line
# 
#     def append_lines(self, lines):
#         for line in lines:
#             self.append(line)
# 
#     def append_report(self, other_report):
#         for line in other_report.iterlines():
#             self.append(line)
# 
#     def extend(self, lines):
#         for line in lines:
#             self.append(line)
# 
#     def write(self, path):
#         f = open(path, 'w')
#         for line in self._lines:
#             try:
#                 f.write(str(line))
#             except:
#                 print line
#                 print type(line)
#                 pdb.set_trace()
#             f.write('\n')
#         f.close()
# 
#     def iterlines(self):
#         for line in self._lines:
#             yield line
# 
#     def lines(self):
#         return self._lines


from ColumnsTable import ColumnsTable
from Report import Report


class ReportWithColumnsTable(object):
    def __init__(self, header_lines, column_defs, print_as_spaces, verbose=True):
        self._report = Report()
        self._header(header_lines)
        self._ct = ColumnsTable(column_defs, verbose)
        self._print_as_spaces = print_as_spaces

    def _header(self, header_lines):
        for line in header_lines:
            self._report.append(line)

    def append_detail(self, **kwds):
        # replace NaN with None
        with_spaces = {k: (None if self._print_as_spaces(k, v) else v)
                       for k, v in kwds.iteritems()

# Path: ColumnsTable.py
# class ColumnsTable(object):
#     def __init__(self, columns, verbose=False):
#         'columns is an iterable with elements (name, width, formatter, (header-list), legend)'
#         self._columns = []
#         self._number_of_header_lines = 0
#         self._column_names = set()
#         for column_def in columns:
#             assert len(column_def) == 5, 'column_def must have length 5: %s' % str(column_def)
#             ctf = ColumnsTableFields(*column_def)
#             headers = ctf.headers
#             # convert 'abc' to ('abc',)
#             headers = (headers,) if isinstance(headers, str) else headers
#             if self._number_of_header_lines != 0:
#                 if self._number_of_header_lines != len(headers):
#                     print '+++++++++++++++++++++'
#                     print 'inconsistent number of lines in header'
#                     print 'have both %d and %d' % (self._number_of_header_lines, len(headers))
#                     print 'found at header: %s' % headers
#                     print '+++++++++++++++++++++'
#                     pdb.set_trace()
#             self._number_of_header_lines = len(headers)
#             self._columns.append(ColumnsTableFields(ctf.name,
#                                                     ctf.width,
#                                                     ctf.formatter,
#                                                     headers,
#                                                     ctf.legend))
#             if ctf.name in self._column_names:
#                 print '+++++++++++++++++++++'
#                 print 'column name already defined: %s' % ctf.name
#                 print '+++++++++++++++++++++'
#                 pdb.set_trace()
#             self._column_names.add(ctf.name)
#         self._lines = []
#         self._verbose = verbose
#         self._header()
# 
#     def append_legend(self, prefix_width=20):
#         def cat_headers(headers):
#             text = ''
#             for header in headers:
#                 if len(text) > 0:
#                     text += ' '
#                 text += header.strip()
#             return text
# 
#         self.append_line(' ')
#         self.append_line('column legend:')
#         for cd in self._columns:
#             # TODO: replace fix width below with value detemined from column headings
#             format = '%%%ds -> %%s' % prefix_width
#             line = format % (cat_headers(cd.headers), cd.legend)
#             self.append_line(line)
# 
#     def iterlines(self):
#         for line in self._lines:
#             yield line
# 
#     def append_detail(self, **kwds):
#         line = ''
#         # detect and report extra arguments
#         for k in kwds.keys():
#             if k not in self._column_names:
#                 print 'Column %s is not defined in the ColumnTable' % k
#                 print 'defined column names are', self._column_names
#                 pdb.set_trace()
#         # format values in kwds
#         for cd in self._columns:
#             if cd.name in kwds and kwds[cd.name] is not None:
#                 try:
#                     glyph = cd.formatter % kwds[cd.name]
#                 except TypeError as e:
#                     print '+++++++++++++++++++++++++'
#                     print TypeError, e
#                     print 'formatter  : %s' % cd.formatter
#                     print 'field name : %s' % cd.name
#                     print 'field value: %s' % kwds[cd.name]
#                     print '+++++++++++++++++++++++++'
#                     pdb.set_trace()
#             else:
#                 # if missing or None, print spaces for the value
#                 glyph = ' ' * cd.width
#             if len(line) > 0:
#                 line += ' '
#             line += glyph
#         self.append_line(line)
# 
#     def append_line(self, line):
#         if self._verbose:
#             print line
#         self._lines.append(line)
# 
#     def _header(self):
#         def append_header(index):
#             line = ''
#             for cd in self._columns:
#                 formatter = '%' + str(cd.width) + 's'
#                 formatted = formatter % cd.headers[index]
#                 line += (' ' if len(line) > 0 else '') + formatted
#             self.append_line(line)
# 
#         for header_line_index in xrange(self._number_of_header_lines):
#             append_header(header_line_index)
# 
#     def _print(self):
#         for line in self._lines:
#             print line
# 
# Path: Report.py
# class Report(object):
#     def __init__(self, also_print=True):
#         self._also_print = also_print
#         self._lines = []
# 
#     def append(self, line):
#         self._lines.append(line)
#         if self._also_print:
#             print line
# 
#     def append_lines(self, lines):
#         for line in lines:
#             self.append(line)
# 
#     def append_report(self, other_report):
#         for line in other_report.iterlines():
#             self.append(line)
# 
#     def extend(self, lines):
#         for line in lines:
#             self.append(line)
# 
#     def write(self, path):
#         f = open(path, 'w')
#         for line in self._lines:
#             try:
#                 f.write(str(line))
#             except:
#                 print line
#                 print type(line)
#                 pdb.set_trace()
#             f.write('\n')
#         f.close()
# 
#     def iterlines(self):
#         for line in self._lines:
#             yield line
# 
#     def lines(self):
#         return self._lines


# Path: ReportWithColumnsTable.py
from ColumnsTable import ColumnsTable
from Report import Report



class ReportWithColumnsTable(object):
    def __init__(self, header_lines, column_defs, print_as_spaces, verbose=True):
        self._report = Report()
        self._header(header_lines)
        self._ct = ColumnsTable(column_defs, verbose)
        self._print_as_spaces = print_as_spaces

    def _header(self, header_lines):
        for line in header_lines:
            self._report.append(line)

    def append_detail(self, **kwds):
        # replace NaN with None
        with_spaces = {k: (None if self._print_as_spaces(k, v) else v)
                       for k, v in kwds.iteritems()

                       }