.include "include/data.s"

#TODO apply1 seems obsolete by generic apply

# | fun | arg | -> cont
.thunkcode apply1
	thunkto %rsi, $apply1_fini, $3, 030(%rbp), %rbp, %rsi
	enter 020(%rbp) # evaluate fun

# fun -> | arg | ret | cont |
.thunkcode apply1_fini
	# we now know that fun points to a FUN with at least one arg missing.
	# we're certainly going to copy a lot of args.
	mov 020(%rsi), %r11 # amount of args applied now

	# the copying code is shared so let's do that first:
	pushq 020(%rbp) #push the new arg
	lea 030(%rsi), %rdx # the end (first arg)
	lea (%rdx, %r11, 010), %rbx # address behind the last arg

	cmp %rdx, %rbx
	jbe apply1_fini_cont
apply1_fini_copy:
	sub $010, %rbx # iterate down
	pushq (%rbx) # push what we have
	cmp %rdx, %rbx # check if we are at the end
	ja apply1_fini_copy # if not, continue
apply1_fini_cont:
	add $1, %r11
	pushq %r11 # new number of args of fun/thunk
	pushq 010(%rsi) # thunk code pointer

	# copying of all args and their thunky header is now done, let's find
	# out how we need to finish it.

	mov (%rsi), %rdi # infotable for the original fun
	mov -010(%rdi), %r12 # amount of args required to make the thunk
	cmp %r11, %r12
	ja apply1_fini_feed # not enough args, just make a bigger FUN

	# if there was enough args, we simply have a thunk that we want to
	# continue evaluating, so let's jump to it.
	mov 030(%rbp), %rdi # load the original thunk
	mov %rsp, 010(%rdi) # set indirect to the new thunk
	movq $IND_code, 0(%rdi)
	mov 040(%rbp), %rsi # set continuation to the original continuation
	enter %rsp # evaluate the new thunk

apply1_fini_feed:
	# if there were not enough args, we push the function info and return
	pushq (%rsi) # copy the function infoptr

	mov 030(%rbp), %rdi # load the original thunk
	mov %rsp, 010(%rdi) # set the indirect to the new FUN
	movq $IND_code, 0(%rdi)
	mov %rsp, %rsi # return the new FUN
	enter 040(%rbp) # jump to the continuation

# | fun | arg[1] | arg[2] | ... | arg[args-1] | -> cont
.thunkcode apply
	thunkto %rsi, $apply_fini, $2, %rbp, %rsi
	enter 020(%rbp)

# fun -> | ret (with args) | cont |
.thunkcode apply_fini
	mov (%rsi), %r9 # infotable for the original function
	mov 020(%rbp), %r10 # the original thunk
	mov 020(%rsi), %r11 # amount of args applied in the closure
	mov -010(%r9), %r12 # amount of args required to make a thunk
	mov 010(%r10), %r13 # amount of args in the original thunk
	sub $1, %r13 # amount of args we want to apply (the 1st one is the FUN)

	mov %r11, %r14
	add %r13, %r14
	cmp %r12, %r14 # do we have enough arguments?
	ja apply_fini_o

apply_fini_pt:
	# not enough args or exactly enough args.
	# Basically make a function or a thunk that is almost the same.

	# first move thunk params
	mov %r13, %rcx
	cmp $0, %rcx
	jz apply_fini_pt_thunk_skip
	lea 030(%r10, %r13, 010), %rdx
	apply_fini_pt_thunk_copy:
	sub $010, %rdx
	pushq (%rdx)
	loop apply_fini_pt_thunk_copy
	apply_fini_pt_thunk_skip:

	# now move the fun params
	mov %r11, %rcx
	cmp $0, %rcx
	jz apply_fini_pt_fun_skip
	lea 030(%rsi, %r11, 010), %rdx
	apply_fini_pt_fun_copy:
	sub $010, %rdx
	pushq (%rdx)
	loop apply_fini_pt_fun_copy
	apply_fini_pt_fun_skip:

	# make a thunk
	thunk 010(%rsi), %r14
	cmp %r12, %r14 # are we precisely at the right amount of arguments for a thunk?
	je apply_fini_pt_thunk # if not, wrap a closure
	apply_fini_pt_closure:
	thunkto %rsi, %r9

	# replace the original thunk with an indirect
	mov %rsi, 010(%r10)
	movq $IND_code, (%r10)
	# return the closure (%rsi) to the original continuation
	enter 030(%rbp)

	apply_fini_pt_thunk:
	# it is a thunk, point to it and start evaluating it
	mov %rsp, 010(%r10)
	movq $IND_code, (%r10)
	# tell the thunk to evaluate into the original continuation
	mov 030(%rbp), %rsi
	enter %rsp

apply_fini_o: #TODO needs to be tested
	# too many args, we need to split off a bit
	# first move just the right amount of args off the thunk
	mov %r12, %rcx
	sub %r11, %rcx
	cmp $0, %rcx
	jz apply_fini_o_tc_skip
	lea 030(%r10, %rcx, 010), %rdx
	apply_fini_o_tc_copy:
	sub $010, %rdx
	pushq (%rdx)
	loop apply_fini_o_tc_copy
	apply_fini_o_tc_skip:

	# move all args from the closure
	mov %r11, %rcx
	cmp $0, %rcx
	jz apply_fini_o_fun_skip
	lea 030(%rsi, %r11, 010), %rdx
	apply_fini_o_fun_copy:
	sub $010, %rdx
	pushq (%rdx)
	loop apply_fini_o_fun_copy
	apply_fini_o_fun_skip:

	# make the thunk for the application that can be evaluated later
	thunkto %r15, 010(%rsi), %r14

	# now make a thunk with the rest of the stuff
	mov %r14, %rcx
	sub %r12, %rcx
	mov %rcx, %r14 # backup leftover count for later
	cmp $0, %rcx
	jz apply_fini_o_tt_skip
	lea 030(%r10, %r13, 010), %rdx
	apply_fini_o_tt_copy:
	sub $010, %rdx
	pushq (%rdx)
	loop apply_fini_o_tt_copy
	apply_fini_o_tt_skip:

	# finish the leftovers thunk
	add $1, %r14 # (1 fun to apply to + args)
	thunk $apply,%r14,%r15

	# replace the original thunk with an indirect
	mov %rsp, 010(%r10)
	movq $IND_code, (%r10)
	# evaluate to the original continuation
	mov 030(%rbp), %rsi
	enter %rsp