/*
 * Code to maintain a registry for hardware bits and pieces
 */

#include "Registry.h"
#include "ulist.h"
#include "utils.h"

#include "CompEmul.h"
#include "MemEmul.h"		// For MMIO_Dev
#include "DiscEmul.h"

#include "dlc/dlc_server.h"

// The current model emulator
CompEmul *cur_model = NULL;

// A list of MMIO_Devs
UList<MMIO_Dev *> mmio_dev_list;

// A list of modules MMIO_Dev instances
UList<ModuleInfo *> modules_list;

// A list of disc emulations
UList<DiscEmul *> disc_emul_list;

void Set_Model (CompEmul *mdl)
{
	cur_model = mdl;
}

void Load_Model (char *name, char *args)
{
	CompEmul *comp;
	static char buf[2048];
	
	snprintf (buf, 2048, "./%s.so", name);
	
	// Now load it
	if (!DLC_Server.load (buf))
		fatal ("Unable to load class %s", name);
	snprintf (buf, 2048, "%sEmul", name);
	comp = New<CompEmul> (buf);
	if (comp == NULL)
		fatal ("Failed to create %s class...", buf);
	Set_Model (comp);
	if (args)
		comp->SetArgs (args);
}

void Register_MMIO (MMIO_Dev *dev)
{
	mmio_dev_list.Add (dev);
}

void Unregister_MMIO (MMIO_Dev *dev)
{
	mmio_dev_list.Remove (dev);
}

ModuleInfo *Register_Module (MMIO_Dev *dev, char *name)
{
	ModuleInfo *mod;
	
	mod = new ModuleInfo;
	mod->instance = dev;
	mod->name = name;
	modules_list.Add (mod);
	return mod;
}

static ModuleInfo *find_module (MMIO_Dev *dev)
{
	ListElem<ModuleInfo *> *le;
	
	for (le = modules_list.getFirst (); le; le = le->getNext ())
		if ((**le)->instance == dev)
			break;
	if (le)
		return **le;
	else
		return NULL;
}

static ModuleInfo *find_module (char *name)
{
	ListElem<ModuleInfo *> *le;
	
	for (le = modules_list.getFirst (); le; le = le->getNext ())
		if (!strcmp ((**le)->name, name))
			break;
	if (le)
		return **le;
	else
		return NULL;
}

void Unregister_Module (ModuleInfo *mod)
{
	modules_list.Remove (mod);
	delete mod;
}

void Unregister_Module (MMIO_Dev *dev)
{
	ModuleInfo *mod;
	
	mod = find_module (dev);
	if (mod)
		Unregister_Module (mod);
}

void Unregister_Module (char *name)
{
	ModuleInfo *mod;
	
	mod = find_module (name);
	if (mod)
		Unregister_Module (name);
}

void Register_Disc (DiscEmul *disc)
{
	disc_emul_list.Add (disc);
}

void Unregister_Disc (DiscEmul *disc)
{
	disc_emul_list.Remove (disc);
}

// Functions to load and unload modules
ModuleInfo *Load_Module (char *module, char *args, int add)
{
	static char buf[2048];
	MMIO_Dev *dev;
	
	snprintf (buf, 2048, "./%s.so", module);
	
	// Now load it
	if (!DLC_Server.load (buf))
		fatal ("Unable to load class %s", module);
	snprintf (buf, 2048, "%s", module);
	dev = New<MMIO_Dev> (buf);
	if (!dev)
	{
		warning ("%s: unable to instatiate MMIO_Dev class; not a device module?",
			module);
		return NULL;
	}
	if (add && cur_model && (dev->flags & MMIO_IS_TICKER))
		cur_model->Add_Ticker (dev, 1);
	else if (add)
		cur_model->AddDev (dev);
	return Register_Module (dev, args);
}

void Unload_Module (ModuleInfo *mod)
{
	char buf[2048];
	
	if (mod->instance)
		delete mod->instance;
	snprintf (buf, 2048, "./%s.so", mod->name);
	Unregister_Module (mod);
	DLC_Server.unload (buf);
}

void Unload_Module (MMIO_Dev *dev)
{
	ModuleInfo *mod;
	
	if (!(mod = find_module (dev)))
		return;
	Unload_Module (mod);
}

void Unload_Module (char *name)
{
	ModuleInfo *mod;
	
	if (!(mod = find_module (name)))
		return;
	Unload_Module (mod);
}

/* End of file. */
