"""This module removes JS functions from source code"""
from __future__ import print_function

from jsparser import *
from utils import *

INLINE_NAME = 'PyJsLvalInline%d_'
INLINE_COUNT = 0
PRE_EXP_STARTS = {
    'return', 'new', 'void', 'throw', 'typeof', 'in', 'instanceof'
}
PRE_ALLOWED = IDENTIFIER_PART.union({';', '{', '}', ']', ')', ':'})
INCREMENTS = {'++', '--'}


def reset_inline_count():
    global INLINE_COUNT
    INLINE_COUNT = 0


def remove_functions(source, all_inline=False):
    """removes functions and returns new source, and 2 dicts.
        first dict with removed hoisted(global) functions and second with replaced inline functions"""
    global INLINE_COUNT
    inline = {}
    hoisted = {}
    n = 0
    limit = len(source) - 9  # 8 is length of 'function'
    res = ''
    last = 0
    while n < limit:
        if n and source[n - 1] in IDENTIFIER_PART:
            n += 1
            continue
        if source[n:n + 8] == 'function' and source[n +
                                                    8] not in IDENTIFIER_PART:
            if source[:n].rstrip().endswith(
                    '.'):  # allow function as a property name :)
                n += 1
                continue
            if source[n + 8:].lstrip().startswith(
                    ':'):  # allow functions inside objects...
                n += 1
                continue
            entered = n
            res += source[last:n]
            name = ''
            n = pass_white(source, n + 8)
            if source[n] in IDENTIFIER_START:  # hoisted function
                name, n = parse_identifier(source, n)
            args, n = pass_bracket(source, n, '()')
            if not args:
                raise SyntaxError('Function misses bracket with argnames ()')
            args = args.strip('() \n')
            args = tuple(parse_identifier(e, 0)[0]
                         for e in argsplit(args)) if args else ()
            if len(args) - len(set(args)):
                # I know its legal in JS but python does not allow duplicate argnames
                # I will not work around it
                raise SyntaxError(
                    'Function has duplicate argument names. Its not legal in this implementation. Sorry.'
                )
            block, n = pass_bracket(source, n, '{}')
            if not block:
                raise SyntaxError(
                    'Function does not have any code block to execute')
            mixed = False  # named function expression flag
            if name and not all_inline:
                # Here I will distinguish between named function expression (mixed) and a function statement
                before = source[:entered].rstrip()
                if any(endswith_keyword(before, e) for e in PRE_EXP_STARTS):
                    #print 'Ended ith keyword'
                    mixed = True
                elif before and before[-1] not in PRE_ALLOWED and not before[
                        -2:] in INCREMENTS:
                    #print 'Ended with'+repr(before[-1]), before[-1]=='}'
                    mixed = True
                else:
                    #print 'FUNCTION STATEMENT'
                    #its a function statement.
                    # todo remove fucking label if present!
                    hoisted[name] = block, args
            if not name or mixed or all_inline:  # its a function expression (can be both named and not named)
                #print 'FUNCTION EXPRESSION'
                INLINE_COUNT += 1
                iname = INLINE_NAME % INLINE_COUNT  # inline name
                res += ' ' + iname
                inline['%s@%s' % (
                    iname, name
                )] = block, args  #here added real name at the end because it has to be added to the func scope
            last = n
        else:
            n += 1
    res += source[last:]
    return res, hoisted, inline


if __name__ == '__main__':
    print(remove_functions(
        '5+5 function n  (functiona ,functionaj) {dsd  s, dsdd}'))
