from blc2.constants import INFTY

def parse_interval(s):
    if not s:
        return None, s
    dash = False
    init_s = s
    buff = ""
    r = [-1, -1]
    i = 0
    while s:
        if not dash and s[0] == '-':
            if buff:
                r[0] = int(buff)
                buff = ""
            dash = True
        elif s[0] in "0123456789":
            buff += s[0]
        else:
            break
        s = s[1:]
        i += 1

    if buff:
        r[1 if dash else 0] = int(buff)
    elif not buff and not dash:
        return None, s, None
    
    if not dash:
        r[1] = r[0]

    return tuple(r), s, init_s[:i]

def parse_range(s):
    if not s:
        return None, s, None

    rs = []
    disp = []
    tc = False
    while s:
        r, s, d = parse_interval(s)
        if r is None:
            break
        tc = False
        rs.append(r)
        disp.append(d)
        if s and s[0] == ',':
            s = s[1:]
            tc = True
        else:
            break

    if not rs:
        return None, s, None
    return tuple(rs), s, ", ".join(disp) + (", " if tc else "")

def parse_channelrange(s):
    if not s:
        return None, s, None

    f, s, d1 = parse_range(s)
    if f is None:
        return None, s, None
    elif not s or s[0] != ';':
        return (f, ((-1,-1),)), s, d1


    s = s[1:]
    c, s, d2 = parse_range(s)
    if c is None:
        return (f, ((-1, -1),)), s, d1 + "; "

    return (f, c), s, d1+"; "+d2

def parse_value(s):
    if not s:
        return None, s, None
    
    buff = ""
    while s:
        if s[0] in "0123456789" and int(buff+s[0]) < 256:
            buff += s[0]
            s = s[1:]
        else:
            break

    return (None if not buff else int(buff)), s, buff

def parse_num(s):
    if not s:
        return None, s, None
    
    buff = ""
    while s:
        if s[0] in "0123456789":
            buff += s[0]
            s = s[1:]
        else:
            break

    return (None if not buff else int(buff)), s, buff

_POSTFIXES = "mcisahkegtp"

def parse_time(s):
    if not s:
        return None, s, None

    v, s, d = parse_num(s)
    if v is None:
        if s[0].lower() == 'i':
            return INFTY, s[1:], "infinity"
        return None, s, None

    if not s:
        return v, s, d

    try:
        idx = _POSTFIXES.index(s[0].lower())
    except ValueError:
        return v, s, d

    v *= 10**idx
    return v, s[1:], d+s[0].lower()

_ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz"
def parse_any_letter(s):
    if not s:
        return None, s

    l = s[0].lower()
    
    if l not in _ALL_LETTERS:
        return None, s, None
    
    return _ALL_LETTERS.index(l), s[1:], l

def make_parse_letter(letter, display):
    def inner(s):
        if not s or s[0] != letter:
            return None, s, None
        return True, s[1:], display

    return inner

def parse_null(s):
    return True, s, ""

def parse_string(s):
    if not s:
        return None, s, None

    buff = ""
    while s:
        if s[0] != ' ':
            buff += s[0]
            s = s[1:]
        else:
            if not buff:
                return None, s, None
            break

    return buff, s, buff

def parse_quotedstring(s):
    if not s:
        return None, s, None

    if s[0] != "'":
        return None, s, None 
    s = s[1:]

    buff = ""
    bs = False
    while s:
        if s[0] == '\\':
            buff += s[0]
            bs = not bs
        elif s[0] == "'":
            if bs:
                buff += s[0]
                bs = False
            else:
                s = s[1:]
                break
        else:
            buff += s[0]
            bs = False
        s = s[1:]
    else:
        return buff, s, "'"+buff
    return buff, s, "'"+buff+"'"

PARSE_MAP = {
    "$channel_range": parse_channelrange,
    "$value": parse_value,
    "$null": parse_null,
    "$string": parse_string,
    "$num": parse_num,
    "$quoted_string": parse_quotedstring,
    "$letter": parse_any_letter,
    "$time": parse_time,
}