#include <ubixos/spinlock.h>
#include <ubixos/schedule.h>
#include <ubixos/kpanic.h>
#include <lib/kmalloc.h>

spinlock_t * spinlockCreate() {
  spinlock_t * lock = kmalloc(sizeof(spinlock_t),_current->id);
  if (lock == NULL)
    kPanic("SPINLOCK: Couldn't create spinlock\n");
    memset(lock, '\0', sizeof(spinlock_t));
  return(lock);
  }

void spinlockInit(spinlock_t * lock) {
  memset(lock, '\0', sizeof(spinlock_t));
  }

void spinlockDelete(spinlock_t * lock)
{
	int i;
	int notComplete = 1;
	
	lock->deleteInProgress = 1;
	
	while (notComplete == 1)
	{
		notComplete = 0;
		for (i = 0; i < 16; i++)
		{
			while(lock->choiceInProgress[i] != 0)
				notComplete = 1;
			while(lock->ticket[i] != 0)
				notComplete = 1;
		}
	}
	
	kfree(lock);
}

int spinlockLock(spinlock_t * lock, int cpuID)
{
	int max_ticket = 0;
	int i;
		
	if (lock->deleteInProgress == 1)
		return SPINLOCK_FAIL_DELETING;
	
	if (lock->owner == cpuID)
	{
	    lock->count++;
	    return SPINLOCK_SUCCESS;
	}
		
	lock->choiceInProgress[cpuID] = 1;
	
	for (i = 0; i < 16; i++)
		if (lock->ticket[i] >= max_ticket)
			max_ticket = lock->ticket[i];
	
	max_ticket++;
	
	lock->ticket[cpuID] = max_ticket;
	lock->choiceInProgress[cpuID] = 0;
	
	for (i = 0; i < 16; i++)
	{
		while(lock->choiceInProgress[i] == 1)
			;
		
		while (((lock->ticket[i] < max_ticket) || 
			((lock->ticket[i] == max_ticket) && (i < cpuID)))
			&& (lock->ticket[i] != 0))
			;
				
	}
	
	lock->owner = cpuID;
	
	return SPINLOCK_SUCCESS;
	
}

int spinlockUnlock(spinlock_t * lock, int cpuID)
{
	if (lock->owner == cpuID)
	{
	    lock->count--;
	    if (lock->count != 0)
	    {
		return SPINLOCK_SUCCESS;
	    }
	}
	lock->ticket[cpuID] = 0;
	lock->owner = -1;
	
	return SPINLOCK_SUCCESS;
}
		
