*
 * Master Solution Assignmnet 10
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "threads.h"

static struct thread *current_thread = NULL;
static struct thread *previous_thread = NULL;
static int next_tid = 0;

/* Context used to return after all thread finished. */
static jmp_buf main_env;


struct thread *thread_create(void (*f)(void *), void *arg) {
	struct thread *t;

	/* allocate new thread structure */
	if ((t = (struct thread *) malloc(sizeof(struct thread))) == NULL) {
		perror("Failed to allocate thread structure.");
	}

	/* allocate stack */
	if ((t->stack = malloc(STACK_SIZE)) == NULL) {
		perror("Failed to allocate stack.");
	}
	/* because the stack grows down, we need the reference to the end of the stack */
	t->stack_end = t->stack + STACK_SIZE;
	
	t->function = f;
	t->argument = arg;
	t->init = 0;
	t->tid = next_tid++;
	
	printf("DEBUG: Thread %d created.\n", t->tid);

	return t;
}
void dispatch(void) {
	int ret;

	/* save old state - not needed if previous thread exited */
	if (previous_thread != NULL) {
		ret = setjmp(previous_thread->env);
		if (ret) { /* return form longjmp */
			return;
		}
	}
	
	/* run next thread */
	if (current_thread->init) { /* resume previously run thread */
		longjmp(current_thread->env, 1);
	} else { /* start thread */
        /* 64 bit assembler */
//		__asm__ __volatile__("mov %%rax,%%rsp"
//									:
//									:"a"(current_thread->stack_end));
//		__asm__ __volatile__("mov %%rax,%%rbp"
//									:
//									:"a"(current_thread->stack_end));
        /* 32 bit assembler */
		__asm__ __volatile__("mov %%eax,%%esp"
									:
									:"a"(current_thread->stack_end));
		__asm__ __volatile__("mov %%eax,%%ebp"
									:
									:"a"(current_thread->stack_end));

		current_thread->init = 1;
		(*(current_thread->function))(current_thread->argument);
		thread_exit();
	}
}
