import ply.lex
import ply.yacc

from . import htmllex
from . import csslex
from . import jslex

#class CustomLex():
#    def __init__(self, tokens):
#        self.tokens = tokens
#    def token(self):
#        tok = self.tokens.pop(0) if self.tokens else None
#        if tok and tok.type == 'SPACE':
#            return self.token()
#        else:
#            return tok
# Usage:
#    lex = appparse.CustomLex(tokens)
#    print(len(lex.tokens))
#
#    print('Parsing...', flush=True)
#
#    parser = ply.yacc.yacc(module=appparse.htmlparse, debug=True)
#    print(parser.parse(lexer=lex, debug=True))

QUOTES = ['\'', '\"', '`']


def getHTMLTokens(inFileName):

    tokens = []

    with open(inFileName, encoding='utf-8') as inFile:
        inStr = inFile.read()

        lex = ply.lex.lex(module=htmllex, debug=False)
        lex.input(inStr)

        while True:
            tok = lex.token()
            if not tok:
                break

            tokens.append(tok)

    return tokens

def getCSSTokens(inFileName):

    tokens = []

    with open(inFileName, encoding='utf-8') as inFile:
        inStr = inFile.read()

        lex = ply.lex.lex(module=csslex, debug=False)
        lex.input(inStr)

        while True:
            tok = lex.token()
            if not tok:
                break

            tokens.append(tok)

    return tokens

def getJSTokens(inFileName):

    tokens = []

    with open(inFileName, encoding='utf-8') as inFile:
        inStr = inFile.read()

        lex = ply.lex.lex(module=jslex, debug=False)
        lex.input(inStr)

        while True:
            tok = lex.token()
            if not tok:
                break

            tokens.append(tok)

    return tokens

def tokensInsert(tokens, what, after=None):
    '''
    insert 'what' (token or tokens) into the tokens list
    if 'after' specified, insert after that token, else insert at the end
    '''
    # allow both single tokens and lists
    what = what if type(what) == list else [what]

    if after != None:
        try:
            insIndex = tokens.index(after) + 1
        except ValueError:
            raise ValueError('Token not found: ' + str(after))
        tokens[insIndex:insIndex] = what
    else:
        tokens.extend(what)

def writeTokens(tokens, outFileName):

    outChunks = [tok.value for tok in tokens]
    # join here to prevent file erasing when some non-str value found
    outStr = ''.join(outChunks)

    with open(outFileName, 'w', encoding='utf-8') as outFile:
        outFile.write(outStr)

def tokValueSet(tok, value):
    if tok.type in ['ATTRIBUTE', 'STRING', 'STRING_LITERAL']:
        if len(tok.value) >= 2 and tok.value[0] in QUOTES and tok.value[-1] in QUOTES:
            tok.value = tok.value[0] + value + tok.value[-1]
        else:
            tok.value = value
    elif tok.type == 'URI':
        if 'url(' not in tok.value:
            raise ValueError('Invalid token value')

        if tok.value[4] in QUOTES and tok.value[-2] in QUOTES:
            tok.value = tok.value[0:5] + value + tok.value[-2:]
        else:
            tok.value = tok.value[0:4] + value + tok.value[-1]
    else:
        tok.value = value

def tokValueGet(tok):
    if tok.type in ['ATTRIBUTE', 'STRING', 'STRING_LITERAL']:
        if len(tok.value) >= 2 and tok.value[0] in QUOTES and tok.value[-1] in QUOTES:
            return tok.value[1:-1]
        else:
            return tok.value
    elif tok.type == 'URI':
        if 'url(' not in tok.value:
            raise ValueError('Invalid token value')

        url = tok.value[4:-1].strip()

        if tok.value[4] in QUOTES and tok.value[-2] in QUOTES:
            return tok.value[5:-2]
        else:
            return tok.value[4:-1]
    else:
        return tok.value

def tokValueCheck(tok, value):
    if type(value) == str and len(value) and value[0] == '^':
        inverse = True
        value = value[1:]
    else:
        inverse = False
    return (tokValueGet(tok) != value) if inverse else (tokValueGet(tok) == value)

def matchToken(tok, tokPattern):
    # handle array
    tokPattern = [tokPattern] if type(tokPattern) in (str, tuple) else tokPattern

    for t in tokPattern:
        if type(t) == str and tok.type == t:
            return True
        elif type(t) == tuple and tok.type == t[0] and tokValueCheck(tok, t[1]):
            return True

    return False

def filterTokens(tokens, pattern, ignore=['SPACE']):

    if len(pattern) > len(tokens):
        return []

    offset = 0
    offsetIgnore = 0

    while True:

        if len(pattern) + offset + offsetIgnore > len(tokens):
            return []

        for i in range(len(pattern)):
            while matchToken(tokens[offset + offsetIgnore + i], ignore):
                #print("IGNORED", tokens[offset + offsetIgnore + i])
                offsetIgnore += 1
                if len(pattern) + offset + offsetIgnore > len(tokens):
                    return []

            if not matchToken(tokens[offset + offsetIgnore + i], pattern[i]):
                offsetIgnore = 0
                break

            #print("MATHCHED", tokens[offset + offsetIgnore + i], pattern[i])
        else:
            break

        offset+=1

    matched = tokens[offset : offset + offsetIgnore + len(pattern)]

    return stripTokens(matched, ignore)

def stripTokens(tokens, pattern='SPACE'):
    '''strip tokens from start/end, similar to python str.strip() method'''
    stripStart = 0
    stripEnd = 0

    for tok in tokens:
        if matchToken(tok, pattern):
            stripStart += 1
        else:
            break

    for tok in reversed(tokens):
        if matchToken(tok, pattern):
            stripEnd += 1
        else:
            break

    return tokens[stripStart : len(tokens) - stripEnd]

def filterTokensAll(tokens, pattern, ignore=['SPACE']):
    out = []
    tokensPart = tokens
    while True:
        f = filterTokens(tokensPart, pattern, ignore)
        if f:
            out.append(f)
            tokensPart = tokens[tokens.index(f[-1]) + 1 : ]
        else:
            break
    return out

def nthOfType(tokens, type, n):
    counter = 0
    for tok in tokens:
        if tok.type == type:
            if counter == n:
                return tok
            counter += 1
    return None

def spaceToken(space):
    tok = ply.lex.LexToken()
    tok.type = 'SPACE'
    tok.value = space
    tok.lineno = 0
    tok.lexpos = 0
    return tok

def customToken(type, value):
    tok = ply.lex.LexToken()
    tok.type = type
    tok.value = value
    tok.lineno = 0
    tok.lexpos = 0
    return tok
