/*
 * -----------------------------------------------------------------------------
 *  Copyright (c) 1994,1995 Southeastern Universities Research Association,
 *                          Continuous Electron Beam Accelerator Facility
 * 
 *  This software was developed under a United States Government license
 *  described in the NOTICE file included as part of this distribution.
 * 
 * -----------------------------------------------------------------------------
 * 
 *  Description:
 *       cdevCollection class implementation
 * 
 *  Author:  Walt Akers, Chip Watson & Jie Chen
 * 
 *  Revision History:
 *    cdevCollection.cc,v
// Revision 1.2  1996/11/21  16:54:57  akers
// Ongoing development
//
 *  Revision 1.1  1996/11/12  20:32:10  akers
 *  New collection device source code
 * 
 * -----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>

#include <cdevSystem.h>
#include <cdevDirectory.h>
#include <cdevErrCode.h>
#include <cdevCollectionRequest.h>
#include <cdevErrorCollection.h>
#include "cdevCollection.h"

// Construct a pre-existing or an empty collection
// Never called unless device known not to exist in this system
// AND known to not be a simple device (i.e. no name conflicts)
cdevCollection::cdevCollection(char *devName, cdevSystem &sys)
:cdevDevice(devName, sys), nameList_()
	{
	#ifdef _TRACE_OBJECTS
	printf("    Create cdevCollection class object\n");
	#endif
	cdevData data, result;
	data.insert("device", devName);
	int status=(sys.nameServer()).send("queryCollection", data, result);
	if(status == CDEV_SUCCESS)
		{
		// must be pre-defined collection
		size_t num=0;
		if(result.getElems("value", &num) == CDEV_SUCCESS)
			{
			if(num > 0)
				{
				char * name[2];
				char **names;
				if(num==1)
					{
					names = name;
					names[1] = NULL;
					result.find("value", (void * &)name[0]);
					}
				else result.find("value", (void* &)names);
				status=add(num, names);
				// add names
				}
			}
		}
	}

cdevCollection::~cdevCollection(void)
	{
	#ifdef _TRACE_OBJECTS
	printf("    Destroy cdevCollection class object\n");
	#endif
	// remove names
	cdevSlistIterator nlit(nameList_);
	char *name;
	for(nlit.init(); !nlit; ++nlit)
		{
		name=(char *) nlit();
		delete[]name;
		}
	}

cdevCollection &cdevCollection::attachRef(char *name)
	{
	return cdevCollection::attachRef(name, cdevSystem::defaultSystem());
	}

cdevCollection *cdevCollection::attachPtr(char *name)
	{
	return cdevCollection::attachPtr(name, cdevSystem::defaultSystem());
	}

cdevCollection &cdevCollection::attachRef(char *name, cdevSystem &system)
	{
	cdevCollection *col=cdevCollection::attachPtr(name, system);
	if(col == NULL) return *(new cdevErrorCollection(name));
	return *col;
	}

cdevCollection *cdevCollection::attachPtr(char *name, cdevSystem &system)
	{
	cdevDevice *dev=0;
	cdevCollection *col=0;
	cdevService *service=0;
	
	if(system.deviceCreated(name))
		{
		// device exists in this system
		dev=system.device(name);
		if(::strcmp(dev->className(), "cdevCollection") != 0)
		return NULL;
		// if it is not a collection, error
		col=(cdevCollection *) dev;
		col->refCount_++;
		}
	else
		{
		cdevData data, result;
		data.insert("device", name);
		int status=(system.nameServer()).send("queryClass", data, result);
		if(status == CDEV_SUCCESS)
			{
			// device is defined in directory
			char *name;
			result.find("value", (void* &) name);
			if(::strcmp(name, "collection") != 0)
			// if it is not a collection, error
			return NULL;
			}
		col=new cdevCollection(name, system);
		}
	return col;
	}

void
cdevCollection::detach(cdevCollection &dev)
	{
	cdevCollection::detach(&dev);
	}

void
cdevCollection::detach(cdevCollection *dev)
	{
	if(--dev->refCount_ <= 0)
	// no more reference attached
	delete dev;
	}

cdevRequestObject *cdevCollection::getRequestObject(char *msg)
	{
	cdevRequestObject *obj=NULL;
	getRequestObject(msg, obj);
	return obj;
	}

int
cdevCollection::getRequestObject(char *msg, cdevRequestObject* &reqobj)
	{
	reqobj=findRequestObject(msg);
	if(reqobj == NULL)
	reqobj=cdevCollectionRequest::attachPtr(*this, msg, system_);
	return (reqobj==NULL)?CDEV_ERROR:CDEV_SUCCESS;
	}

char **cdevCollection::getList()
	{
	int num=nameList_.count();
	char **list=new char*[num+1];
	char **listStart=list;
	cdevSlistIterator nlit(nameList_);

	// ***********************************************
	// * Note this method reverses the order of the
	// * strings as it extracts them from the
	// * list.
	// ***********************************************
	list[num]=NULL;
	for(nlit.init(); !nlit && num > 0; ++nlit)
		{
		num--;
		list[num]=(char *) nlit();
		}
	return listStart;
	}

int
cdevCollection::add(char *name)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	if(name == 0 || name[0] == '\0') return CDEV_INVALIDOP;
	cdevSlistIterator nlit(nameList_);
	char *test;
	for(nlit.init(); !nlit; ++nlit)
		{
		test=(char *) nlit();
		if(::strcmp(test, name) == 0)
		return CDEV_WARNING;
		}
	char *newname=strdup(name);
	nameList_.add((void *) newname);
	return CDEV_SUCCESS;
	}

int
cdevCollection::add(int num, char *firstname, ...)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	char *device;
	int status, tmpstatus;
	va_list argp;
	va_start(argp, firstname);
	
	status=CDEV_SUCCESS;
	if(firstname) status=add(firstname);
	while(--num > 0)
		{
		device=va_arg(argp, char *);
		if(device) tmpstatus=add(device);
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	va_end(argp);
	return status;
	}

int
cdevCollection::add(int num, char **names)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	int status, tmpstatus;
	status=CDEV_SUCCESS;
	while(num-->0)
		{
		tmpstatus=add(*(names++));
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	return status;
	}

int
cdevCollection::add(char **names)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	if(names == 0) return CDEV_INVALIDOP;
	int status, tmpstatus;
	status=CDEV_SUCCESS;
	while(*names)
		{
		tmpstatus=add(*(names++));
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	return status;
	}

int
cdevCollection::addRegexp(char *regexp)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	cdevData data, result;
	data.insert("device", regexp);
	int status=(system_.nameServer()).send("query", data, result);
	if(status == CDEV_SUCCESS)
		{
		size_t num;
		char **names;
		result.getElems("value", &num);
		result.find("value", (void* &) names);
		status=add(num, names);
		}
	return status;
	}

int
cdevCollection::remove(char *name)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	if(name == 0 || name[0] == '\0') return CDEV_WARNING;
	cdevSlistIterator nlit(nameList_);
	char *test;
	for(nlit.init(); !nlit; ++nlit)
		{
		test=(char *) nlit();
		if(::strcmp(test, name) == 0)
			{
			nameList_.remove((void *) test);
			return CDEV_SUCCESS;
			}
		}
	return CDEV_WARNING;
	}

int
cdevCollection::remove(int num, char *name, ...)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	char *device;
	int status, tmpstatus;
	va_list argp;
	va_start(argp, name);
	
	status=CDEV_SUCCESS;
	if(name) status=remove(name);
	while(--num > 0)
		{
		device=va_arg(argp, char *);
		if(device) tmpstatus=remove(device);
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	va_end(argp);
	return status;
	}

int
cdevCollection::remove(int num, char **names)
	{
	int status, tmpstatus;
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	if(names == 0) return CDEV_INVALIDOP;
	status=CDEV_SUCCESS;
	while(num-->0)
		{
		tmpstatus=remove(*(names++));
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	return status;
	}

int
cdevCollection::remove(char **names)
	{
	int status, tmpstatus;
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	if(names == 0) return CDEV_INVALIDOP;
	status=CDEV_SUCCESS;
	while(*names)
		{
		tmpstatus=remove(*(names++));
		if(status == CDEV_SUCCESS) status=tmpstatus;
		}
	return status;
	}

int
cdevCollection::removeRegexp(char *regexp)
	{
	if(reqObjList_.isEmpty() == 0) return CDEV_INVALIDOP;
	cdevData data, result;
	data.insert("device", regexp);
	int status=(system_.nameServer()).send("query", data, result);
	if(status == CDEV_SUCCESS)
		{
		size_t num;
		result.getElems("value", &num);
		char **names;
		result.find("value", (void* &) names);
		status=remove(num, names);
		}
	return status;
	}
