from ..base import *
from ..conversions import *
from ..func_utils import *
from pyjsparser import parse
from ..byte_trans import ByteCodeGenerator, Code


def Function(this, args):
    # convert arguments to python list of strings
    a = map(to_string, tuple(args))
    _body = u';'
    _args = ()
    if len(a):
        _body = u'%s;' % a[-1]
        _args = a[:-1]
    return executable_function(_body, _args, args.space, global_context=True)


def executable_function(_body, _args, space, global_context=True):
    func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body)

    co = executable_code(
        code_str=func_str, space=space, global_context=global_context)
    return co()


# you can use this one lovely piece of function to compile and execute code on the fly! Watch out though as it may generate lots of code.
# todo tape cleanup? we dont know which pieces are needed and which are not so rather impossible without smarter machinery something like GC,
# a one solution would be to have a separate tape for functions
def executable_code(code_str, space, global_context=True):
    # parse first to check if any SyntaxErrors
    parsed = parse(code_str)

    old_tape_len = len(space.byte_generator.exe.tape)
    space.byte_generator.record_state()
    start = space.byte_generator.exe.get_new_label()
    skip = space.byte_generator.exe.get_new_label()
    space.byte_generator.emit('JUMP', skip)
    space.byte_generator.emit('LABEL', start)
    space.byte_generator.emit(parsed)
    space.byte_generator.emit('NOP')
    space.byte_generator.emit('LABEL', skip)
    space.byte_generator.emit('NOP')
    space.byte_generator.restore_state()

    space.byte_generator.exe.compile(
        start_loc=old_tape_len
    )  # dont read the code from the beginning, dont be stupid!

    ctx = space.GlobalObj if global_context else space.exe.current_ctx

    def ex_code():
        ret, status, token = space.byte_generator.exe.execute_fragment_under_context(
            ctx, start, skip)
        # todo Clean up the tape!
        # this is NOT a way to do that because the fragment may contain the executable code! We dont want to remove it
        #del space.byte_generator.exe.tape[old_tape_len:]
        if status == 0:
            return ret
        elif status == 3:
            raise token
        else:
            raise RuntimeError(
                'Unexpected return status during JIT execution: %d' % status)

    return ex_code


def _eval(this, args):
    code_str = to_string(get_arg(args, 0))
    return executable_code(code_str, args.space, global_context=True)()


def log(this, args):
    print(' '.join(map(to_string, args)))
    return undefined