# -*- coding: utf-8 -*-
# remimplement gpoline.icn and digest.cin python.
# This will implement basic functionality of converting gpo locator codes
# to html matching thomas/lis .
#

import re
import logging
logging.basicConfig(format='%(levelname)s %(pathname)s %(lineno)s:%(message)s', level=logging.DEBUG)
#logging.basicConfig(format='%(levelname)s %(filename)s %(lineno)s:%(message)s', level=logging.DEBUG)
logger = logging.getLogger(__name__)


from itertools import zip_longest
# Globals used in this module.
#             char(0)  char(10) char(13)
#             char(27) char(28) char(127)
REMOVE_CHARS = [b'\x000', b'\x00A', b'\x00D',
                b'\x01B', b'\x01C', b'\xac',
                b'\x07F\d+', #seems to appear at the head of a page/section
                b'\x07S\d+', # subformat codes?
                #b'\xad',
                #b'\xa8'
]
# translate mapping tab to space
MAPPING = {b'\x009': b' ',  # tab to space
           b'\xff09':b'&ndash;' , # b'\xff09': u'\u2013',
           b'\x19':  b' ' , # hex 19 End of Medium?  change to space  i.e. Page\x19S2128 -> Page S2128
           b'\x5f': b'--' , # replace underbar with double hyphen??? TODO: check
           b'\x18': b'<br />' , # \x18 CANCEL-> <br/> ?
           b'\x1a': b'<br />' , # \x1a Substitute -> <br/> ?
           #b'\xff': b'&yuml;' , #  y with .. dots over it. used to indicate accents
}


def grouper(iterable, n, fillvalue=None):
    '''Collect data into fixed-length chunks or blocks,
     Modified recipe from  http://docs.python.org/3/library/itertools.html#recipes
     grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    '''
    args = [ filter( lambda x: x!=b'' , iter(iterable)) ] * n
    #args = [  iter(iterable) ] * n
    return zip_longest(*args, fillvalue=fillvalue)



def remove_chars(line, remove_chars, to_string=b''):
    '''Remove a list of chars from a line
    replacing with empty string by default or passed in variable
    .'''
    for character in (remove_chars):
        m = re.search(character, line)
        if m:
            line = re.sub(character, to_string, line)
            logger.debug(b"Replaced [%s] with [%s] in input", character, to_string)
    return line



def translate_chars(line, mapping):
    '''Given a mapping dictionary, replace all keys found in the line
    with the matching values.
    '''
    for k, v in mapping.items():
        line = remove_chars(line, [k], v)
    return line


def find_page(data):
    '''I90.*\{(D\d+)\}
    '''
    page = None
    #m = re.match(b'.+?\x07I90.+?\{(D\d+)\}.+?', data)
    m = re.match(b'\x07I90.+?\{(D\d+)\}', data)
    if m:
        page = m.group(1)
    logger.debug("find_page->(%s)", page)
    return page, m

def find_locators(line):
    '''given a line return a regex match for locator codes in the line.
    match.start gives position start,  match.end gives position end
    match.group('locator') gives the locator code without the Bell
    '''
    code = None
    m = re.finditer(b'\x07(?P<locator>I67H|I66F|T\d?|g\d{0,3}|[a-su-zA-SU-Z]\d{0,2})', line)
    if m:
        code = m
    return code

def find_escape(line, current_grid=b'G1'):
    ''' Escape sequences usually replace a preceding char, like an accented
    e in resume or in foreign accented chars.
    '''
    code = None
    #m = re.finditer(b'(?P<replace>\w)?\xff(?P<esc>\w{2,3})', line)
    m = re.finditer(b'(?P<replace>.?)\xff(?P<esc>AE\d|AF\d|0\d|\dA|E\d)', line)
    if m:
        code = m
    return code

def translate_locator(locator, grid=b'G2',
                      locator_table=None,
                      font_table=None):
    ''' A locator code has a start and end  conversion (html) and a grid code.
    The Grid code controls which  Font  locator code conversion to use.
    so if the active locator code has a grid G2 we use the font code for grid G2.
    The default grid is G2.
    '''
    converted_code = locator_table.get(locator)
    if not converted_code:
        #check if a font, using passed in grid:
        font_grid = font_table.get(grid)
        if font_grid:
            converted_code= font_grid.get(locator)
    if converted_code:
        return converted_code
    else:
        return {'start':'', 'end':'', 'grid':grid }


import sys
def output(input_line, prefix=None, postfix=None, outf=sys.stdout):
    ''' Print output to filehandle outf or sys.stdout if no filehandle
    passed in.  Attempt to convert bytes from latin1 to utf-8.
    prefix is printed prior to input_line, postfix is print after input_line.
    '''
    if not postfix:
        postfix = ''
    _output(input_line, prefix, postfix, outf)

def _output(input_line, prefix=None, postfix=None, outf=sys.stdout):
    #logger.debug("[%s] %s [%s]", prefix, input_line, postfix)
    if isinstance(input_line, bytes):
        line = input_line.decode('latin1').encode('utf-8')
    else:
        line  = input_line
    if line and line != b'':
        if prefix:
            logger.debug("%s", prefix)
            if isinstance(prefix, bytes) :
                outf.write (prefix.decode("utf-8"))
            else:
                outf.write (line)
        if isinstance(line, bytes) :
            outf.write (line.decode("utf-8"))
        else:
            outf.write (line)
        if postfix:
            logger.debug("%s", postfix)
            if isinstance(postfix, bytes) :
                outf.write (postfix.decode("utf-8"))
            else:
                outf.write (postfix)

ESCAPE_SEQUENCES = {#esc    # action
                    b'1A' : { 'desc' :'Thin space' , 'html':b'&#160;' },
                    b'09' : { 'desc' :'N dash' ,     'html':b'&#150;' },   #TODO: check
                    # 08  not clear what to do  All Mark
                    b'AF' : { 'desc' :'copywright' , 'html':b'&#169;' },
                    b'0A' : { 'desc' :'Multiplication', 'html':b'&#215;' },
                    # esc   # replace # action
                    b'AE0': { 'S':  {'desc' :'breve' ,      'html':b'&#169;' },
                             's':  {'desc' :'breve' ,      'html':b'&#154;' },
                           },

                    b'AE1':{ b'A':  {'desc' :'acute' ,      'html':b'&#193;' },
                             b'E':  {'desc' :'acute' ,      'html':b'&#201;' },
                             b'I':  {'desc' :'acute' ,      'html':b'&#205;' },
                             b'O':  {'desc' :'acute' ,      'html':b'&#211;' },
                             b'U':  {'desc' :'acute' ,      'html':b'&#218;' },
                             b'Y':  {'desc' :'acute' ,      'html':b'&#221;' },

                             b'c':  {'desc' :'acute' ,      'html':b'c&#180;' }, #TODO check
                             b's':  {'desc' :'acute' ,      'html':b's&#180;' }, #TODO check

                             b'a':  {'desc' :'acute' ,      'html':b'&#225;' },
                             b'e':  {'desc' :'acute' ,      'html':b'&#233;' },
                             b'i':  {'desc' :'acute' ,      'html':b'&#237;' },
                             b'o':  {'desc' :'acute' ,      'html':b'&#243;' },
                             b'u':  {'desc' :'acute' ,      'html':b'&#250;' },
                             b'y':  {'desc' :'acute' ,      'html':b'&#253;' },
                   }
    }
def process_escapes(found, orig_line, current_start, current_line, current_grid , escape_sequences=ESCAPE_SEQUENCES ):
    ''' if current_grid > 4 then do something else
    do our conversions see documentation..found on cornell law site
    <replace char>\xff<esc>
        escape_sequences.get(esc).get(<replace_char>).get('html')
    or
    \xff<esc>
        escape_sequences.get(esc).get('html')
    '''
    logger.debug("process esc:%s", current_line)
    output = current_line
    logger.debug("Current_grid[1:]:%s ", current_grid[1:])
    if int(current_grid[1:]) <= 4:
        replace = found.group('replace')
        esc = found.group('esc')
        if esc:
            '''2 types of esc sequences:
            One where we match first the
            esc char and then the replace char to get the action part of the
            dictionary.
            Two: just match the esc char and then we get the action part of the
            dictionary.
            '''
            temp = escape_sequences.get(esc, {'desc': 'default', 'html': b'' } )
            # if the temp action has a dictionary matching the replace char,
            # make that dictionary the action
            action = temp.get(replace)
            keep_replacement = False
            if not action: