import functools
import collections
import re
import sys
import traceback
import pprint

import kutil
import klammer_base
import html_util
from html_util import E
import latex_util
from sequences import Sequences
import table_cell
import font

def extend(lst, count, fill=None):
    if isinstance(lst, str):
        lst = lst.strip().split("\\s+")
    if fill is None:
        fill = lst[-1] if lst else ""
    return lst + ([fill] * (count - len(lst)))

def parse_hpos(target, pos):
    if '"' in pos:
        pos_list = []
        parts = pos.split('"')
        i = 0
        while i < len(parts):
            pos_list += parts[i].split()
            i += 1
            if i == len(parts):
                break
            #pos_list.append(f"{{{parts[i]}}}")
            pos_list.append(kutil.parse_length(target, f'"{parts[i]}"')[0])
            i += 1
    else:
       # pos_list = pos.split()
        pos_list = pos
    #print("pos_list:", pos_list)
    return pos_list

class Table(klammer_base.Klammer_base):
    id = 0
    def __init__(self, K):
        super().__init__(K)
        # pprint.pprint(self.__dict__)
        if self.grid:
            self.vline = "all"
            self.hline = "all"
        self.number = self.number == "true"
        self.rows = kutil.rest_args(self.rows, 2)
        self.row_count = len(self.rows)
        if self.header:
            self.hline += f" 1 {self.row_count}"
        self.row_size = max([len(e) for e in self.rows])
        self.s_vline = Sequences(self.row_size + 1, self.row_count - 1, self.vline)
        self.s_hline = Sequences(self.row_count + 1, self.row_size - 1, self.hline)
        self.s_rowspan = Sequences(self.row_size, self.row_count, self.rowspan)
        self.s_colspan = Sequences(self.row_count, self.row_size, self.colspan)
        self.cell_hpos = extend(parse_hpos(self.K_target, self.cell_hpos.split()), self.row_size)
        #self.cell_hpos = self.cell_hpos.split(";")
        self.font = extend(self.font, self.row_size)
        self.make_cells(self.rows)

    def span_count(self, span_seq, row_i, col_i):
        result = 0
        row_seq = span_seq[row_i]
        if row_seq:
            for range in row_seq.ranges:
                if range[0] == col_i:
                    result = range[1] - range[0] + 1
        return result

    def remove_redundant_borders(self):
        remove_right = []
        for row_i in range(self.row_count):
            for cell_i in range(self.row_size):
                a = self.cells[row_i][cell_i]
                b = self.cells[row_i][cell_i+1]
                if a.border.right and b.border.left:
                    a.border.right = False
                    a.border.right_all = False

    def column_width_text(self):
        self.column_widths = []
        # print("column_width_text:", len(self.cells), len(self.cells[0]))
        for col_i in range(len(self.cells[0])):
            longest = ""
            for row_i in range(len(self.cells)):
                cell = self.cells[row_i][col_i]
                if cell is not None:
                    cell_text = cell.text
                    #lines = [e.strip() for e in cell_text.split("\\newline")]
                    lines = [e.strip() for e in cell_text.split("\newline")]
                    lines = sorted(lines, key=len)
                    longest_in_line = lines[-1]
                    if len(longest_in_line) > len(longest):
                        longest = longest_in_line
            self.column_widths.append(longest)
        # kutil.msg("column_widths:")
        # print(self.column_widths)

    def make_cells(self, rows):
        result = []
        cells = []
        for row_i, row in enumerate(rows):
            row_cells = []
            for cell_i, cell in enumerate(row):
                rspan = self.span_count(self.s_rowspan, row_i, cell_i)
                cspan = self.span_count(self.s_colspan, row_i, cell_i)
                font = self.font[cell_i]
                if row_i == 0 and self.header:
                    font = self.header_font
                row_cells.append(
                    table_cell.Cell(
                        cell,
                        font, self.cell_hpos[cell_i],
                        self.s_hline.has(row_i, cell_i),
                        self.s_vline.has(cell_i+1, row_i),
                        self.s_hline.has(row_i+1, cell_i),
                        self.s_vline.has(cell_i, row_i),
                        self.s_vline.sequences.get(cell_i),
                        self.s_vline.sequences.get(cell_i+1),
                        rspan, cspan))
            cells.append(row_cells)
        widths = [len(e) for e in cells]
        max_width = max(widths)
        self.cells = [extend(row, max_width, None) for row in cells] \
            if max_width != min(widths) else cells
        self.column_width_text()
                
    # HTML

    def html(self):
        result = ""
        for row in self.cells:
            row_html = ""
            for cell in row:
                row_html += cell.html().strip() + "\n"
            result += E("tr").body(row_html).str()
        result = E("table").body(result)
        if self.number or self.caption:
            result = html_util.add_caption(
                result, "Table", self.number, self.caption, self.caption_font)
        else:
            result = result.str()
        return result

    # LaTeX

    def tex_hpos(self):
        def par_format(s, justification):
            command = {"l" : "raggedright",
                       "c" : "centering",
                       "r" : "raggedleft"}[justification]
            return f">{{\\{command}}}p{{{s}}}"

        widths = []
        hpos_pat = re.compile("(f|(?:0?(\\.\\d+)(t))|\\*)?([lcr]?)")

        def parse(s, width_text):
            match = hpos_pat.match(s)
            # print("MATCH:", match, match.groups())
            width, frac, table, just = match.groups()
            width = width or "f"
            just = just or "l"
            if width and width[0] == "{":
                width = f"\\widthof{{{s}}}"
            elif table == "t":
                width = f"{frac}\\tablewidth"
            elif width == "f":
                width = f"\\widthof{{{width_text}}}"

            if width != "*":
                widths.append(width)

            result = par_format(width, just) if width != "*" else s
            # print("PARSE:", result)
            return result

        # print("self.column_widths:", len(self.column_widths), self.column_widths)
        # return extend([parse(e) for e in self.cell_hpos], self.row_size)
        hpos_list = []
        for i, hpos in enumerate(extend(self.cell_hpos, self.row_size)):
            # print(f"  Loop {i}:", hpos)
            if i >= len(self.column_widths):
                print(f"Warning: Ignoring table column width: {hpos}")
            else:
                hpos_list.append(parse(hpos, self.column_widths[i]))

        # print("hpos_list:", hpos_list)
        fill_count = sum([1 if "*" in e else 0 for e in hpos_list])
        # print("fill_count:", fill_count)

        if fill_count > 0:
            margins = f"(\\tabcolsep * {2 * len(hpos_list)})"
            # print("MARGINS:", margins)
            #expr = "\\linewidth - " + " - ".join(widths) + str("
            if fill_count == len(hpos_list):
                expr = f"{1/fill_count}\\tablewidth"
            else:
                #expr = f"(\\textwidth - {margins} - {' - '.join(widths)}) / {fill_count}"                
                expr = f"(\\tablewidth - {' - '.join(widths)}) / {fill_count}"                
            # print(expr)
            result = []
            for h in hpos_list:
                if h[0] == "*":
                    just = h[1] if len(h) > 1 else "l"
                    result.append(par_format(expr, just))
                else:
                    result.append(h)
        else:
            result = hpos_list
        # print("tex_hpos:", result)

        return result

    def tex_column_spec(self):
        parts = [""] * (self.row_size * 2 + 1)
        for i in self.s_vline.sequences:
            parts[i * 2] = "|"
        for i, hpos in enumerate(self.tex_hpos()):
            parts[i * 2 + 1] = hpos
        # print("tex_column_spec:", "".join(parts))
        return "".join(parts)

    def tex_hline(self, index):
        hline = ""
        bottom = index == self.row_count
        if bottom:
            index -= 1
        count = 0
        for i, cell in enumerate(self.cells[index]):
            has_border = cell.border.bottom if bottom else cell.border.top
            if has_border:
                hline += f"\\cline{{{i+1}-{i+1}}} "
                count += 1
        #if count == self.row_size:
        #    hline = "\\hline"
        return hline.strip() + "\n"

    def tex_rows(self):
        result = ""
        for row_i, row in enumerate(self.cells):
            result += self.tex_hline(row_i)
            tab = ""
            for cell_i, cell in enumerate(row):
                result += tab + cell.tex()
                tab = " & "
            #result += " \\\\\n"
            result += " \\tabularnewline\n"
        result += self.tex_hline(self.row_count)
        return result

    def get_width(self):
        box = "\\savebox{\\tablebox}{%\n"
        box += "\\begin{tabular}{"
        box += self.tex_column_spec()
        box += "}\n"
        box += self.tex_rows()
        box += "\\end{tabular}}\n"
        result = box + "\\setlength{\\tableboxwidth}{\\wd\\tablebox}\n"
        # result += "\\the\\tableboxwidth\n\n"
        # print(result)
        return result

    def make_caption(self):
        column_count = str(len(self.rows[0]))
        caption = latex_util.make_caption_text(
            self.number, "Table", self.caption, self.caption_font, self.caption_font_size)
        result = "\\multicolumn{" + column_count + "}{c}{\\parbox{\\tableboxwidth}{"
        result += "\\vspace*{8pt}\\centering\\small\\em "
        result += caption
        result += "}} \\\\ \\endlastfoot\n"
        return result

    def tex(self):
        result = self.get_width()
        result += "\\vspace*{-.75\\baselineskip}"
        # result = ""
        result += "\\renewcommand*{\\arraystretch}{1.3}\n"
        if self.allow_break:
            result += "\\vspace*{12pt}\n"
        result += "\\begin{longtable}{"
        result += self.tex_column_spec()
        result += "}\n"
        if self.allow_break:
            result += self.make_caption()
        result += self.tex_rows()
        result += "\\end{longtable}\n"
        
        if not self.allow_break:
            if self.number or self.caption:
                result = latex_util.add_caption(result, "Table", self.number, self.caption, "\\tablewidth")
            else:
                result = latex_util.caption_wrapper(result, "center")

        name = f"Reference-Table-{Table.id}"
        result = f"\\hypertarget{{{name}}}{{}}\\label{{Label-{name}}}\n{result}"
        result = f"\\setlength{{\\tablewidth}}{{\\textwidth - {2 * self.row_count}\\tabcolsep}}\n" + result
        # result += "\\vspace*{-8pt}"
        result = re.sub(r"\newline", r"\\\\", result)
        return result
    
    def txt(self):
        return "TXT"

