#include <debug.h>
#include <new.h>
#include <err.h>
#include <os/Module.h>
#include <kernel/AutoSpinlock.h>
#include <kernel/Scheduler.h>

namespace OS {

Module::Module(const char *name, int initOrder, status_t (*initRoutine)(Module *)) 
	: Kernel::KObject(Kernel::KObject::MODULE, name),
	  mInitOrder(initOrder),
	  mInitRoutine(initRoutine),
	  mInitRoutineCalled(false),
	  mModuleListNode(this)
{
	ModuleList::Instance()->AddModule(this);

//	dprintf("Module constructor: name %s, order %d, routine %p\n", Name(), mInitOrder, mInitRoutine);
}

Module::~Module()
{
	// remove us from the global list
	
}

ModuleList::ModuleList()
{
//	dprintf("ModuleList constructor\n");
}

// static singleton accessor
ModuleList *ModuleList::Instance()
{
	static ModuleList theInstance;

	return &theInstance;
}

void ModuleList::AddModule(Module *m)
{
	Kernel::ConditionalAutoSpinlock al(mModuleListLock, Kernel::Scheduler::IsStarted());

	ASSERT(!m->mModuleListNode.IsInList());

//	dprintf("%s: module %p\n", __PRETTY_FUNCTION__, m);
	
	// walk through the module list, adding the modules in init order
	Module *curr = mModuleList.Head();
	while (curr != NULL) {
		if (m->mInitOrder <= curr->mInitOrder) {
			m->mModuleListNode.AddBefore(&curr->mModuleListNode);
			return;
		}

		curr = curr->mModuleListNode.Next();
	}

	// we must have walked off the end of the list, add it at the tail
	mModuleList.AddToTail(&m->mModuleListNode);
}

status_t ModuleList::SetInitLevel(Module::ModuleInitLevel level)
{
  restart:
	{
		Kernel::ConditionalAutoSpinlock al(mModuleListLock, Kernel::Scheduler::IsStarted());

		// walk through the module list, adding the modules in init order
		Module *m = mModuleList.Head();
		while (m != NULL) {
			if (m->mInitOrder > level)
				break; // list is sorted, we're done for now

			if (!m->mInitRoutineCalled) {
				dprintf("%s: calling init routine on module '%s', level %d, level %d\n", __PRETTY_FUNCTION__, m->Name(), m->mInitOrder, m->mInitOrder);
				m->mInitRoutineCalled = true;

				// release the spinlock before the callback, callback, reacquire and start the loop over in case the callback
				// modifies the list.
				al.Release();
			
				m->mInitRoutine(m);

				goto restart;
			}

			m = m->mModuleListNode.Next();
		}
	}

	return NO_ERROR;
}

} // namespace OS
