"""This module removes JS functions from source code"""
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}')