""" sed-esque substitution support. taken from infobot on 24/09/2020. """
from . import command, reply_fn
import re
import itertools
import codecs
import string

ascii = [chr(i) for i in range(128)]

SUB_REGEX = re.compile(r"^s/((?:\\/|[^/])+)\/((?:\\/|[^/])*?)/([gixs]{0,4})?")
TRANS_REGEX = re.compile(r"^y/((?:\\/|[^/])+)\/((?:\\/|[^/])*?)/([cds]+)?")

def squeeze(lst, src):
    for ch in lst:
        src = re.sub(ch + r"{2,}", ch, src)
    return src

def sugar(src):
    return src.replace("\\/", "/") \
            .replace("[:upper:]", string.ascii_uppercase) \
            .replace("[:lower:]", string.ascii_lowercase) \
            .replace("[:alpha:]", string.ascii_letters) \
            .replace("[:digit:]", string.digits) \
            .replace("[:xdigit:]", string.hexdigits) \
            .replace("[:alnum:]", string.digits + string.ascii_letters) \
            .replace("[:blank:]", string.whitespace) \
            .replace("[:punct:]", string.punctuation) \
            .replace("[:cntrl:]", "".join([i for i in ascii if not i in string.printable])) \
            .replace("[:print:]", string.printable)

def expand(src):
    out = []
    escaped = False
    hyphen = False

    for char in src:
        if char == "\\":
            escaped = True
            continue
        elif char == '-' and not escaped:
            hyphen = True
            escaped = False
            continue
        elif hyphen:
            out.extend(range(out[-1] + 1, ord(char)))
            hyphen = False
        out.append(ord(char))
    return "".join([chr(i) for i in out])


class Substitution(object):
    """ A class representing a substitution in sed. """
    def __init__(self, pattern):
        self.pattern = pattern
        self.re, self.sub, self.flags, self.count = self.parse(pattern)

    def __repr__(self):
        return "Substitution(%r)" % (self.pattern)

    def parse(self, pattern):
        m = SUB_REGEX.match(pattern)
        if not m:
            raise TypeError("Pattern %r isn't a proper substitution pattern!" % (pattern))
        count = 1

        groups = m.groups()

        if 'g' in groups[2]:
            count = 0

        flags = 0
        for c in groups[2]: # for character in flags
            if c != 'g':
                flags += getattr(re, c.upper())

        sub = groups[1].replace("\\/", "/")
        sub = re.sub(r"(?<!\\)(\\)(?=\d+|g<\w+>)", r"\\\\", sub)
        sub = codecs.escape_decode(bytes(sub, "utf-8"))[0].decode('utf-8')

        pat = groups[0]

        return [pat, sub, flags, count]

    def do(self, on):
        return re.sub(self.re, self.sub, on, count=self.count, flags=self.flags)

@command(SUB_REGEX, additional_match_fn=reply_fn, prefix=False)
def sed(bea, bot, update, reply_to_msg):
    """ Substitute (part of) a message with a replacement. """
    text = update.message.text
    sub = Substitution(text)
    try:
        result = sub.do(reply_to_msg.text)
    except re.error as e:
        bea.reply(f'error while doing substitution: {e}')
    else:
        bea.reply(result, quote=False)

@command(TRANS_REGEX, additional_match_fn=reply_fn, prefix=False, pass_groups=True)
def tr(bea, bot, update, reply_to_msg, groups):
    lhs, rhs, flags = groups

    lhs = expand(sugar(lhs))
    rhs = expand(sugar(rhs))

    trans_table = {ord(i[0]):i[1] for i in itertools.zip_longest(lhs, rhs, fillvalue=rhs[-1])}

    msg = reply_to_msg.text
    msg = msg.translate(trans_table)
    return bea.reply(msg)

def init(bea, config):
    return [tr, sed]
