import pprint
import re
import textwrap
import kutil
import klammer_base
import itertools
import math
from html_util import E

class List(klammer_base.Klammer_base):
    def __init__(self, K, list_type):
        super().__init__(K)
        self.list_type = list_type
        self.items = self.list_items
        word_count = max([len(e.strip().split()) for e in self.items])
        self.compressed = False #and word_count < int(self.List_compressed_word_max)
        
    def html(self):
        body = ""
        word_count = 0
        for e in self.items:
            body += f"<li>\n{kutil.format_for_paragraphs(e.strip())}\n</li>\n"
        cls = "compressed" if self.compressed else ""
        result = E(self.list_type).cls(cls).body(body.strip()).str().strip() + "\n"
        return result
    
    def tex(self):
        body = "\n\n".join([f"\\item {e}" for e in self.items])
        command = {'ol' : 'enumerate', 'ul' : 'itemize'}[self.list_type]
        init = ""
        if self.__dict__.get("initial") and self.initial != 1:
            init = "\\addtocounter{enumi}{" + str(self.initial - 1) + "}"
        #topsep = '[noitemsep,topsep=0pt]'
        topsep = "[noitemsep,topsep=0pt," if self.compressed else "[topsep=0pt,itemsep=2pt,"
        topsep += "leftmargin=16pt]"
        listsep = '\\setlist{nolistsep}' if self.cmp else ''
        result = f'{listsep}\n\\begin{{{command}}}{topsep}{init}\n{body}\n\\end{{{command}}}\n'
        return result

    def txt(self):
        n = 1
        result = ""
        for item in self.items:
            prefix = f"{n:3}. " if self.list_type == "ol" else "  • " # U+2022
            print("prefix", prefix)
            formatted_item = "\n".join(
                textwrap.wrap(item.strip(), width=80, subsequent_indent=" "*(len(prefix))))
            result += f"{prefix}{formatted_item}\n"
            if not self.compressed:
                result += "\n"
            n += 1
        print(result)
        return result


class Define(klammer_base.Klammer_base):
    def __init__(self, K):
        super().__init__(K)
        self.items = self.descriptions
        #self.show()
        #pprint.pprint(self.items)

    def html(self):
        def fontify(s):
            return {"r" : s,
                    "i" : f"<i>{s}</i>",
                    "b" : f"<b>{s}</b>",
                    "t" : f'<span class="monospace">{s}</span>',
                    "c" : f'<span class="monospace">{s}</span>',
                    "s" : f'<span class="sansserif">{s}</span>'}[self.font]
        result = "\n\n".join([f"<dt>{fontify(term)}</dt>\n<dd>{definition}</dd>"
                              for term,definition in self.items])
        result = f"<dl>\n{result}\n</dl>"
        return result

    def tex(self):
        def fontify(s):
            font = {'r' : r'\normalfont',
                    'i' : r'\normalfont\itshape',
                    'b' : r'\normalfont\bfseries',
                    't' : r'\normalfont\ttfamily',
                    'c' : r'\normalfont\ttfamily',
                    's' : r'\normalfont\sffamily'}[self.font]
            return f"{font} {s}"
        result = "\n\n".join([f"\\item[{fontify(term)}]\\hfill\n\\newline {definition}"
                              for term,definition in self.items])
        result = f"\\begin{{description}}[labelindent=12pt,nosep,itemsep=6pt]\n{result}\n\\end{{description}}"
        return result

    def txt(self):
        return "TXT DEFINITIONS"


class Lines(klammer_base.Klammer_base):
    def __init__(self, K):
        super().__init__(K)

    def html(self):
        #return " <br>".join(self.s.strip().split("\n"))        
        #return f'<pre style="font-family: var(--serif)">{self.s}</pre>'
        #return f'<pre>{self.s}</pre>'
        result = self.s
        result = re.sub("\n", "<br>\n", result)
        result = re.sub(" ", "&^#160;", result)
        return result

    def tex(self):
        return r"\\\newline\n".join(self.s.strip().split("\n"))

    def txt(self):
        return self.s.strip()


"""
def lines(K):
    return {'html' : html_lines,
            'tex' : tex_lines,
            'pdf' : tex_lines}[K._target](K)

def tex_lines(K):
    return " \\\\\n".join(K.s.strip().split("\n"))
    
def html_lines(K):
    return " <br>".join(K.s.strip().split("\n"))
"""




"""
def definition_item(K):
    if K._format == 'html':
        return '<dt>{}</dt>\n<dd>{}</dd>'.format(
            format_for_paragraphs(K.term),
            format_for_paragraphs(K.desc))
    else:
        leading = '' if K.inlist else '\\vspace*{-16pt}'
        return '\\item[{}]\\leavevmode{}\n\n{}'.format(
            K.term, leading, K.desc)
"""




"""
def list(K, list_type):
    items = kutil.rest_args(K.list_items)
    return {'html' : html_list,
            'tex' : tex_list,
            'pdf' : tex_list}[K._target](K, list_type, items)

def html_list(K, list_type, items):
    body = "\n" + "".join([E("li").body(e).str()+"\n" for e in items])
    result = E(list_type).body(body).str().rstrip()
    return result

def tex_list(K, list_type, items):
    body = "\n\n".join([f"\\item {e}" for e in items])
    command = {'ol' : 'enumerate', 'ul' : 'itemize'}[list_type]
    topsep = '[topsep=0pt]'
    listsep = '\\setlist{nolistsep}' if K.cmp != 'false' else ''
    result = f'{listsep}\n\\begin{{{command}}}{topsep}\n{body}\n\\end{{{command}}}\n'
    return result

def describe(K):
    items = "\n\n".join(kutil.rest_args(K.descriptions))
    return {'html' : html_describe,
            'tex' : tex_describe,
            'pdf' : tex_describe}[K._target](K, items)

def html_describe(K, items):
    return E("dl").body(items).str()

def tex_describe(K, items):
    return f'\\begin{{description}}\n{items}\n\\end{{description}}'


def entry(K):
    return {'html' : html_entry,
            'tex' : tex_entry,
            'pdf' : tex_entry}[K._target](K)
    
def html_entry(K):
    #return f'<dt>{K.item}</dt>\n<dd>{K.description}</dd>'
    return E("dt").body(K.item).str(0) + E("dd").body(K.description).str(0)

def tex_entry(K):
    return f'\\item[{K.item}]\n{K.description}\n'


def lines(K):
    return {'html' : html_lines,
            'tex' : tex_lines,
            'pdf' : tex_lines}[K._target](K)

def tex_lines(K):
    return " \\\\\n".join(K.s.strip().split("\n"))
    
def html_lines(K):
    return " <br>".join(K.s.strip().split("\n"))

"""


r"""
# List items

paragraph_separator_re = re.compile('\n *\n', re.S)

def format_for_paragraphs(s):
    result = s
    if len(paragraph_separator_re.findall(result)) > 0:
        result = '\n\n{}\n\n'.format(result)
    return result

def li(K):
    if K._format == 'html':
        result = K.s.strip()
        result = format_for_paragraphs(result)
        result = '<li>{}</li>'.format(result)
    else:
        #result = '\\item{{\\Kinlisttrue {}}} '.format(K.s.strip())
        #result = '\\item\\begin\\Kinlisttrue {}\\end '.format(K.s.strip())
        #result = '\\item\\parbox{{\\linewidth}}{{\\Kinlisttrue {}}} '.format(K.s.strip())
        #result = '\\item\\Kinlisttrue {} '.format(K.s.strip())
        result = '\\item {} '.format(K.s.strip())

        if K.keep:
            result = '\\parbox{{\\linewidth}}{{{}}}'.format(result)
    return result

def mli(K):
    result = '@0item {} | @0ulc\n'.format(K.args[0])
    result += "\n".join(['@0li {} li@'.format(e) for e in K.args[1:]]) + '\n'
    result += ' ulc@ item@\n'
    return result

    
# Ordered and unordered lists:

def list_html(K, list_type):
    result = ''
    if K.list_items[0][0:4] != '<li>':
        result = "\n".join(['<li>{}</li>'.format(e) for e in K.list_items])
    else:
        result = K.list_items[0]
    tag = 'ol'
    if list_type == 'ul':
        tag = 'ul style="list-style-type:{};"'.format(K.bullet)
    cls = 'compressed' if K.cmp else ''
    if list_type == 'ul' and K.bullet == 'none':
        cls += ' nobullet'
    cls = ' class="{}"'.format(cls.strip())

    result = '<{}{}>\n{}\n</{}>\n'.format(tag, cls, result, list_type)
    return result

def list_latex(K, list_type):
    result = ''
    first_word = K.list_items[0][:5]
    if first_word not in {'\\item', '\\parb'}:
        result = "  " + "\n".join(['@0li {} li@'.format(e.strip()) for e in K.list_items])
    else:
        result = K.list_items[0]
    #options = 'nosep,itemsep=1pt,topsep=0pt,partopsep=8pt,parsep=4pt'
    options = 'nosep,itemsep={}pt'.format(2 if K.cmp else 6)
    if K.cmp:
        itemsep=',nosep'
    else:
        itemsep=''

    '''
    if list_type == 'ul' and K.bullet == 'none' and K.indent:
        left_margin = ',leftmargin=18pt'
    else:
        left_margin = ''
    '''

    left_margin = ''
    if K.in_define:
        if list_type == 'ul':
            left_margin = ',leftmargin=4pt'
        else:
            left_margin = ',leftmargin=16pt'

    if K.parsep:
        parsep = K.parsep
    else:
        parsep = 2 if K.cmp else 6

    #options = '[nosep,parsep={}pt{},{}]'.format(parsep, left_margin,itemsep)
    options = '[parsep={}{}{}]'.format(parsep, left_margin, itemsep)

    '''
    if K.cmp:
        options = '[nosep]'
    else:
        options = ''
    '''   

    #options = '[itemsep={}]'.format(K.itemsep) if K.itemsep else ''

    #options = '[topsep=0pt,parsep={}pt]'.format(2 if K.cmp else 6)
    #options = '\\setlength{{\\topsep}{0pt}}\n\\setlength{{\parsep}}{{{}}}\n'.format(
    bullet = ''
    if list_type == 'ul' and K.bullet == 'none':
        bullet = '\\renewcommand\\labelitemi{\\hspace*{-8pt}}'
    itemsep = '\n\\setlength{{\\itemsep}}{{{}}}\n'.format(K.itemsep) if K.itemsep else ''
    #result = '''\\listvmargin%
    result = '''\\begin{{{0}}}{1}{2}
{3}
{4}
\\end{{{0}}}
'''.format('itemize' if list_type == 'ul' else 'enumerate',
           options, itemsep, bullet, result.strip())
    result = re.compile('\n\n', re.S).sub('\n', result)
    result = re.compile(r' +\\begin', re.S).sub(r'\\begin', result)
    if K.uncover:
        N = 1
        def replace(match):
            nonlocal N
            result = '{}<{}-> '.format(match.group(1), N)
            N += 1
            return result
        result = re.compile(r'(\\item(\[|\s))').sub(replace, result)
    return result

#\\listvmargin

def ul(K):
    if K._format == 'html':
        return list_html(K, 'ul')
    elif K._format == 'latex':
        return list_latex(K, 'ul')
    else:
        util.format_not_defined(K._format, 'ul')

def ol(K):
    if K._format == 'html':
        return list_html(K, 'ol')
    elif K._format == 'latex':
        return list_latex(K, 'ol')
    else:
        util.format_not_defined(K._format, 'ol')

# Definitions

def definition_item(K):
    if K._format == 'html':
        return '<dt>{}</dt>\n<dd>{}</dd>'.format(
            format_for_paragraphs(K.term),
            format_for_paragraphs(K.desc))
    else:
        leading = '' if K.inlist else '\\vspace*{-16pt}'
        return '\\item[{}]\\leavevmode{}\n\n{}'.format(
            K.term, leading, K.desc)

def dfont(K):
    if K._format == 'latex':
        result = {'r' : '\\normalfont',
                  'i' : '\\normalfont\\itshape',
                  'b' : '\\normalfont\\bfseries',
                  't' : '\\normalfont\\ttfamily',
                  's' : '\\normalfont\\sffamily\\large'
                  }[K.font]
    elif K._format == 'html':
        s = K.term
        result = {'r' : s,
                  'i' : '<i>{}</i>'.format(s),
                  'b' : '<b>{}</b>'.format(s),
                  't' : '<span class="monospace">{}</span>'.format(s),
                  's' : '<span class="sansserif">{}</span>'.format(s)
                  }[K.font]
    else:
        error.Klammertext_error(
            'There is not defined format named "{}".'.format(K._format))
    return result

def escape_code_term(s):
    def replace(match):
        left, term, right = match.groups()
        term = re.sub('\[', r'$\\lbrack$\\,', term)
        term = re.sub('\]', r'\\,$\\rbrack$', term)
        #term = re.sub('\[', '\\[', term)
        #term = re.sub('\]', '\\]', term)
        term = re.sub('--', '-{}-', term)
        result = '{}{}{}'.format(left, term, right)
        return result
    pat = re.compile(r'(\\item\[)(.*?)(\]\\)', re.S)
    return pat.sub(replace, s)

def define(K):
    if K._format == 'latex':
        #itemsep=1pt,topsep=2pt,partopsep=8pt,parsep=4pt,%
        options = 'nosep,parsep=4pt,leftmargin=32pt'
        fontname = {'r' : '\\normalfont',
                    'i' : '\\normalfont\\itshape',
                    'b' : '\\normalfont\\bfseries',
                    't' : '\\normalfont\\ttfamily',
                    's' : '\\normalfont\\sffamily',
                    'si' : '\\normalfont\\sffamily\\itshape',
                    'sb' : '\\normalfont\\sffamily\\bfseries',
                    'c' : '\\normalfont',
                  }[K.font]
        term = K.s
        if K.font == 'c':
            term = escape_code_term(term)
            #term = re.compile('item\[(.*?)\]', re.S).sub(r'item[\\verb+\1+]', term)
            #term = re.compile('
        return '''
\\listvmargin \\begin{{description}}[{},
                      labelindent=16pt,%
                      style=nextline,font={}]
%\\raggedright
{}
\\end{{description}} \\listvmargin\n'''.format(options,fontname, term)

    elif K._format == 'html':
        term_class = {'r' : 'normal',
                      'i' : 'italic',
                      'b' : 'bold',
                      't' : 'monospace',
                      's' : 'sansserifbody',
                      'si' : 'sansserifitalic',
                      'sb' : 'sansserifbold',
                      'c' :  'normal',
                 }[K.font]
        term = K.s
        if K.font == 'c':
            #term = re.sub('&#8211;', '&#8208;'*2, term)
            term = re.sub('&#8211;', '- __UNSPACE__ -', term)
        body = re.sub('<dt>', '<dt class="{}">'.format(term_class), term)
        return '''
<dl>
{}
</dl>'''.format(body)
"""


def make_column_lists(lst, count):
    cols = math.ceil(len(lst) / count)
    result = []
    for i in range(count):
        #print(i * cols, (i + 1) * cols)
        result.append(lst[i * cols : (i+1) * cols])
    return list(itertools.zip_longest(*result, fillvalue=""))

def xmake_column_lists(lst, column_count):
    count = len(lst)
    row_count = math.ceil(count / column_count)
    print(count, column_count, row_count)
    i = 0
    while i < count:
        i += 1

def columns(K):
    rows = make_column_lists(K.items.split(), K.n)
    result = "@table\n"    # :lines false :header_line false :leading 1.1 :vmargin false |\n"
    for row in rows:
        #print("row:", row)
        result += " | ".join(row) + " ||\n"
    result += "table@\n"
    if K.K_target == "latex":
        result = f"\\vspace*{{-12pt}}\n{result}"
    return result
