#include <err.h>
#include <string.h>
#include <kernel/KObject.h>
#include <arch/ops.h>
#include <kernel/AutoSpinlock.h>

namespace OS {
namespace Kernel {

typedef List<KObject> KObjectList;
typedef ListNode<KObject> KObjectListNode;

// next object id to be allocated
static volatile kobj_id sNextId = 0;

// global hash table
static const int kObjHashSize = 256;
static KObjectList sObjIdHash[kObjHashSize];
static Spinlock sHashLock;

KObject::KObject(enum KObjectType type, const char *name)
	: mId(atomic_add(&sNextId, 1)),
	  mObjectIdHashNode(this),
	  mObjectType(type)
{
	strlcpy(mObjectName, name, sizeof(mObjectName));
}

KObject::~KObject()
{
	RemoveFromGlobal();
}

inline int hashVal(kobj_id id)
{
	return (id % kObjHashSize);
}

void KObject::MakeGlobal()
{
	AutoSpinlock al(sHashLock);

//	TRACE_ENTRY_OBJ;

	if (mObjectIdHashNode.IsInList()) {
		// already published
		return;
	}

	int hash = hashVal(mId);
	KObjectList *list = &sObjIdHash[hash];
	list->AddToHead(&mObjectIdHashNode);
}

status_t KObject::RemoveFromGlobal()
{
	AutoSpinlock al(sHashLock);

//	TRACE_ENTRY_OBJ;

	if (!mObjectIdHashNode.IsInList()) {
		// not published
		return NO_ERROR;
	}

	mObjectIdHashNode.Remove();

	return NO_ERROR;
}

//static 
KObject *KObject::Lookup(kobj_id id, KObjectType type)
{
	AutoSpinlock al(sHashLock);

	int hash = hashVal(id);
	KObjectList *list = &sObjIdHash[hash];
	for (KObject *obj = list->Head(); obj != NULL; obj = obj->mObjectIdHashNode.Next()) {
		if (obj->mId == id) {
			if (type == UNKNOWN)
				return obj;
			else if (type == obj->Type())
				return obj;
			else
				return NULL;
		}
	}

	return NULL;
}

}} // namespace OS::Kernel
