#ifndef _CDEV_CLASS_DEFINITION_H_
#define _CDEV_CLASS_DEFINITION_H_

#ifndef _CDEV_DIRECTORY_TABLE_H_
	#error "You must include cdevDirectoryTable.h to load cdevClassDefinition.h"
#endif

// *****************************************************************************
// * class cdevClassDefinition :
// *	This class stores the definition of a single CDEV DDL class and all
// *	of its associated verbs, attributes and messages.
// * 
// *	This class maintains a variety of internal data items for the following
// *	 purposes...
// * 
// *	directory   - This is the cdevDirectoryTable that the class definition 
// *                  is a member of...
// * 
// *	name        - This is the name of the class as defined in the CDEV DDL
// *                  file.
// * 
// *    verbs       - This is a list of cdevElementDefinitions that identify
// *	              each verb that was defined within the class definition.
// * 
// *    attributes  - This is a list of cdevElementDefinitions that identify
// *                  each attribute that was defined within the class 
// *                  definition.
// * 
// *    messages    - This is a list of cdevElementDefinitions that identify 
// *	              each messages that was defined within the class defintion.
// * 
// * 	parent      - This is the class definition for the class that this one
// *	              inherited from - if any.
// * 
// *    deviceHead  - This is a list of device definitions for all devices that
// *    deviceTail    were created from this class.
// * 
// *	redirector  - This is a table of all complete messages that this class
// *                  will support.  This table is only generated if devices are
// *                  instanciated from the class - and it includes the message
// *                  contents from all parent classes.
// * 
// *    next        - This is the next class definition that occurs... This list
// *                  is maintained in the cdevDirectoryTable object.
// *****************************************************************************
class cdevClassDefinition 
{
private:
	cdevDirectoryTable    & directory;		
	cdevClassDefinition   * next;		
	char                  * name;		
	cdevElementDefinition * verbs;		
	cdevElementDefinition * attributes;	
	cdevElementDefinition * messages;	
	cdevClassDefinition   * parent;		
	cdevDeviceDefinition  * deviceHead;	
	cdevDeviceDefinition  * deviceTail;
	StringHash              redirector;	
	
public:
	inline cdevClassDefinition(cdevDirectoryTable   &Master,
				  char                  *Name       = NULL, 
				  cdevClassDefinition   *Parent     = NULL,
				  cdevElementDefinition *Verbs      = NULL, 
				  cdevElementDefinition *Attributes = NULL, 
				  cdevElementDefinition *Messages   = NULL);
	inline ~cdevClassDefinition ( void );
	inline int  addElement   (cdevElementDefinition *&target,
				  cdevElementDefinition * element );
	inline int  addElement   (cdevElementDefinition *& target,
				  char                  *  eleName,
				  cdevServiceDefinition *  service,
				  char                  ** serviceData,
				  int                      nItems);
	inline int  addDevice    (char * device, char * substName = NULL );
	inline int  addVerb      (char * eleName, cdevServiceDefinition *service, char ** serviceData, int nItems );
	inline int  addVerb      (cdevElementDefinition * element );
	inline int  addAttribute (char * eleName, cdevServiceDefinition *service, char ** serviceData, int nItems );
	inline int  addAttribute (cdevElementDefinition * element );
	inline int  addMessage   (char * eleName, cdevServiceDefinition *service, char ** serviceData, int nItems );
	inline int  addMessage   (cdevElementDefinition * element );
	inline int  isA          (char * className );
	inline void asciiDump    (FILE * fp = stdout );
	inline void asciiDumpInstances     ( FILE * fp = stdout );
	inline void asciiDumpRedirector    ( FILE * fp = stdout );
	inline void createRedirectionTable ( void );

	// *********************************************************************
	// * Member access functions:
	// *********************************************************************
	inline char                  * getName       ( void );
	inline cdevDirectoryTable    & getDirectory  ( void );
	inline cdevClassDefinition   * getParent     ( void ); 
	inline cdevElementDefinition * getVerbs      ( void );
	inline cdevElementDefinition * getAttributes ( void );
	inline cdevElementDefinition * getMessages   ( void );
	inline cdevDeviceDefinition  * getDevices    ( void );
	inline StringHash            & getRedirector ( void );
	inline int                     getElements   ( cdevDirectoryTable::ElementType type,
						       cdevElementDefinition       ** &def, 
						       int                            &nItems );
	inline cdevClassDefinition   * getNext       ( void );
	inline void                    setNext       ( cdevClassDefinition * Next );	

};


// *****************************************************************************
// * cdevClassDefinition::cdevClassDefinition   :
// *	This is the constructor for the class definition object.  I
// *****************************************************************************
inline cdevClassDefinition::cdevClassDefinition  
	( cdevDirectoryTable    &Master,
	  char                  *Name, 
	  cdevClassDefinition   *Parent,
	  cdevElementDefinition *Verbs, 
	  cdevElementDefinition *Attributes, 
	  cdevElementDefinition *Messages)
	: directory  (Master),
	  name       (Name), 
	  parent     (Parent), 
	  verbs      (Verbs), 
	  attributes (Attributes), 
	  messages   (Messages),
	  deviceHead (NULL),
	  deviceTail (NULL),
	  next       (NULL)
	{
	}

// *****************************************************************************
// * cdevClassDefinition::~cdevClassDefinition  :
// *	This is the descructor for the cdevClassDefinition object.  It will 
// *	delete the class name and each item from the elements list.
// *****************************************************************************
inline cdevClassDefinition::~cdevClassDefinition ( void )
	{
	cdevElementDefinition    * ptr;
	cdevDeviceDefinition     * dPtr;
	cdevRedirectorDefinition * rPtr;
	StringHashIterator         iter(&redirector);
	
	delete name;

	while(verbs!=NULL)
		{
		ptr   = verbs;
		verbs = verbs->getNext();
		delete ptr;
		}
	while(messages!=NULL)
		{
		ptr      = messages;
		messages = messages->getNext();
		delete ptr;
		}
	while(attributes!=NULL)
		{
		ptr        = attributes;
		attributes = attributes->getNext();
		delete ptr;
		}	
	while(deviceHead!=NULL)
		{
		dPtr       = deviceHead;
		deviceHead = deviceHead->getNext();
		directory.getDeviceHash().remove(dPtr->getName());
		delete dPtr;
		}	
	for(iter.first(); (rPtr = (cdevRedirectorDefinition *)iter.data())!=NULL; )
		{
		iter++;
		redirector.remove(rPtr->getName());
		delete rPtr;
		}
	}

// *****************************************************************************
// * cdevClassDefinition::addElement :
// *	This method is used to add an element (verb, attribute, or message) to
// *	the cdevClassDefinition. The target parameter is a reference to the 
// *	list that will receive the incoming element.
// *****************************************************************************
inline int cdevClassDefinition::addElement
	( cdevElementDefinition *&target,
	  cdevElementDefinition * element )
	{
	cdevElementDefinition *curr, *prev;
	int                    result = CDEV_SUCCESS;
	
	for(curr=target, prev=NULL; 
	    curr!=NULL && strcmp(curr->getName(), element->getName()); 
	    curr=curr->getNext())
	    	{
	    	prev = curr;
	    	}
	if(curr == NULL)
		{
		if(prev) prev->setNext(new cdevElementDefinition(element));
		else     target = new cdevElementDefinition(element);
		}
	else result = CDEV_ERROR;
	return result;
	}
	
// *****************************************************************************
// * cdevClassDefinition::addElement :
// *	This method will create a new element and add it to the list of elements
// *	that is reference by the target paremeter.
// *****************************************************************************
inline int cdevClassDefinition::addElement
	       (cdevElementDefinition *& target,
                char                  *  eleName,
                cdevServiceDefinition *  service,
                char                  ** serviceData,
                int                      nItems)
	{
	cdevElementDefinition *curr, *prev;
	int                    result = CDEV_SUCCESS;
	
	for(curr=target, prev=NULL; 
	    curr!=NULL && strcmp(curr->getName(), eleName); 
	    curr=curr->getNext())
	    	{
	    	prev = curr;
	    	}
	if(curr == NULL)
		{
		if(prev) prev->setNext(new cdevElementDefinition(eleName, service, serviceData, nItems));
		else     target = new cdevElementDefinition(eleName, service, serviceData, nItems);
		}
	else result = CDEV_ERROR;
	return result;
	}

// *****************************************************************************
// * cdevClassDefinition::addDevice  :
// *	This method will add a new device to the device list.  This device name
// *	will also be placed in the hashTable in the cdevDirectoryTable object
// *	specified by directory.  
// * 
// *	Note the device name becomes the property of the cdevDeviceDefinition
// *	class and should not be deleted.
// * 
// *	The redirection table for this device will also be created as the
// *	first device is instanciated.
// *****************************************************************************
inline int cdevClassDefinition::addDevice ( char * device, char * substName )
	{
	int result = CDEV_SUCCESS;

	if(device!=NULL && directory.getDeviceHash().find(device)==NULL)
		{
		cdevDeviceDefinition * def = 
			new cdevDeviceDefinition(directory, device, substName, *this);

		if(deviceTail==NULL)
			{
			deviceHead = def;
			deviceTail = def;
			createRedirectionTable();
			directory.addClassInstance(this);
			}
		else	{
			deviceTail->setNext( def );
			deviceTail = def;
			}

		directory.addDevice(def);
		}
	else result = CDEV_ERROR;

	return result;
	}
	
// *****************************************************************************
// * cdevClassDefinition::addVerb    :
// *	This method will add a verb to the verbs element definition list.
// *****************************************************************************
inline int cdevClassDefinition::addVerb
	( char * eleName, cdevServiceDefinition *service, char ** serviceData, int nItems )
	{
	return addElement(verbs, eleName, service, serviceData, nItems);
	}

// *****************************************************************************
// * cdevClassDefinition::addVerb  :
// *	This method will add a verb to the verbs element definition list.
// *****************************************************************************
inline int cdevClassDefinition::addVerb ( cdevElementDefinition * element )
	{
	return addElement(verbs, element);
	}
	
// *****************************************************************************
// * cdevClassDefinition::addAttribute    :
// *	This method will add an attribute to the attributes element 
// *	definition list.
// *****************************************************************************
inline int cdevClassDefinition::addAttribute
	( char * eleName, cdevServiceDefinition * service, char ** serviceData, int nItems )
	{
	return addElement(attributes, eleName, service, serviceData, nItems);
	}

// *****************************************************************************
// * cdevClassDefinition::addAttribute  :
// *	This method will add an attribute to the attributes element 
// *	definition list.
// *****************************************************************************
inline int cdevClassDefinition::addAttribute ( cdevElementDefinition * element )
	{
	return addElement(attributes, element);
	}
	
// *****************************************************************************
// * cdevClassDefinition::addMessage    :
// *	This method will add a message to the messages element definition list.
// *****************************************************************************
inline int cdevClassDefinition::addMessage
	( char * eleName, cdevServiceDefinition * service, char ** serviceData, int nItems )
	{
	return addElement(messages, eleName, service, serviceData, nItems);
	}

// *****************************************************************************
// * cdevClassDefinition::addMessage  :
// *	This method will add a message to the messages element definition list.
// *****************************************************************************
inline int cdevClassDefinition::addMessage ( cdevElementDefinition * element )
	{
	return addElement(messages, element);
	}
		
// *****************************************************************************
// * cdevClassDefinition::isA  :
// *	This method will ascend the class hierarchy attempting to locate a 
// *	class with the name specified in className.  It will return TRUE if this
// *	class is or inherits from the specified className, or FALSE if not.
// *****************************************************************************
inline int cdevClassDefinition::isA ( char * className )
	{
	int result;
	if(strcmp(name, className)==0)	result = 1;
	else if(parent!=NULL)           result = parent->isA(className);
	else                            result = 0;
	return result;
	}

// *****************************************************************************
// * cdevClassDefinition::asciiDump  :
// *	This method dumps the contents of the cdevClassDefinition to the user
// *	specified file descriptor.
// *****************************************************************************
inline void cdevClassDefinition::asciiDump ( FILE * fp )
	{
	if(name)
		{
		fprintf(fp, "\nclass %s", name);
		if(parent) fprintf(fp, " : %s", parent->name);
		fprintf(fp, "\n\t{\n");

		if(verbs!=NULL) verbs->asciiDumpList(fp, "verbs");
		if(attributes!=NULL) attributes->asciiDumpList(fp, "attributes");
		if(messages!=NULL) messages->asciiDumpList(fp, "messages");

		fprintf(fp, "\t}\n");
		}
	}


// *****************************************************************************
// * cdevClassDefinition::asciiDumpInstances  :
// *	This method dumps the instance data of the class definition to the 
// *	caller specified file descriptor.
// *****************************************************************************
inline void cdevClassDefinition::asciiDumpInstances ( FILE * fp )
	{
	if(deviceHead!=NULL)
		{
		cdevDeviceDefinition * device = deviceHead;
		
		fprintf(fp, "\n%s : \n", name);
		while(device!=NULL)
			{
			if(device->getName()!=device->getSubstituteName())
				{
				fprintf(fp, "\t%s {%s}\n", device->getName(), device->getSubstituteName());
				}
			else fprintf(fp, "\t%s\n", device->getName());
			device = device->getNext();
			}
		fprintf(fp, "\t;\n");
		}
	}


// *****************************************************************************
// * cdevClassDefinition::asciiDumpRedirector  :
// *	This method dumps the contents of the redirection table that is used
// *	to route messages to their intended service.
// *****************************************************************************
inline void cdevClassDefinition::asciiDumpRedirector ( FILE * fp )
	{
	StringHashIterator iter(&redirector);
	cdevRedirectorDefinition * def;	

	fprintf(fp, "\n/* Redirection table for class %s */\n", name);
	fprintf(fp, "class %s\n\t{\n\tmessages\n\t\t{\n", name);
	for(iter.first(); 
	   (def = (cdevRedirectorDefinition *)iter.data())!=NULL;
	   iter++) 
	   	{
	   	def->asciiDump(fp);
		}
	fprintf(fp, "\t\t}\n\t}\n");
	}


// *****************************************************************************
// * cdevClassDefinition::createRedirectionTable :
// *	This method will read the verbs, attributes and messages and will 
// *	generate a redirection table that will allow the cdevDirectoryTable
// *	to route requests to the correct service using the proper serviceData.
// *****************************************************************************
inline void cdevClassDefinition::createRedirectionTable ( void )
	{
	cdevRedirectorDefinition * def;
	cdevElementDefinition ** msgPtr, ** vrbPtr, **atrPtr;
	int                      msgCnt,    vrbCnt,   atrCnt;
	int                      msgIdx,    vrbIdx,   atrIdx;
	
	// *********************************************************************
	// * Get a pointer to all of the verbs that are in use by the class.
	// *********************************************************************
	getElements(cdevDirectoryTable::MESSAGE,   msgPtr, msgCnt);
	getElements(cdevDirectoryTable::VERB,      vrbPtr, vrbCnt);
	getElements(cdevDirectoryTable::ATTRIBUTE, atrPtr, atrCnt);
	
	// *********************************************************************
	// * Process through all of the messages and add them to the 
	// * redirection list first.  Display a warning if they are already
	// * present.
	// *********************************************************************
	for(msgIdx=0; msgIdx<msgCnt; msgIdx++)
		{
		if(msgPtr[msgIdx]->getName() && msgPtr[msgIdx]->getService())
			{
			if(redirector.find(msgPtr[msgIdx]->getName())!=NULL)
				{
				cdevReportError(CDEV_SEVERITY_INFO, "CDEV Directory", NULL,
						"Message %s already defined for class %s",
						msgPtr[msgIdx]->getName(), name);
				}
			else	{
				def = new cdevRedirectorDefinition(strdup(msgPtr[msgIdx]->getName()), *msgPtr[msgIdx]);
				redirector.insert(def->getName(), def);
				}
			}
		}

	// *********************************************************************
	// * Walk through the verb list and combine them with each attribute... 
	// * For each verb that has a service associated with it, use the verbs
	// * element definition... If the verb does not have a service, then
	// * use the attributes element definition.
	// *********************************************************************
	for(vrbIdx=0; vrbIdx<vrbCnt; vrbIdx++)
		{
		if(vrbPtr[vrbIdx]->getName())
			{
			// *****************************************************
			// * Using the verbs service.
			// *****************************************************
			if(vrbPtr[vrbIdx]->getService())
				{
				for(atrIdx=0; atrIdx<atrCnt; atrIdx++)
					{
					if(atrPtr[atrIdx]->getName()!=NULL)
						{
						char * temp = new char[strlen(vrbPtr[vrbIdx]->getName())+strlen(atrPtr[atrIdx]->getName())+2];
						sprintf(temp, "%s %s", vrbPtr[vrbIdx]->getName(), atrPtr[atrIdx]->getName());
						if(redirector.find(temp)!=NULL)
							{
							cdevReportError(CDEV_SEVERITY_INFO, "CDEV Directory", NULL,
								"Message %s already defined for class %s",
								temp, name);
							delete temp;
							}
						else	{
							def = new cdevRedirectorDefinition(temp, *vrbPtr[vrbIdx]);
							redirector.insert(def->getName(), def);
							}
						}
					}
				}
			// *****************************************************
			// * Using the attributes service.
			// *****************************************************
			else for(atrIdx=0; atrIdx<atrCnt; atrIdx++)
				{
				if(atrPtr[atrIdx]->getName() && atrPtr[atrIdx]->getService())
					{
					char * temp = new char[strlen(vrbPtr[vrbIdx]->getName())+strlen(atrPtr[atrIdx]->getName())+2];
					sprintf(temp, "%s %s", vrbPtr[vrbIdx]->getName(), atrPtr[atrIdx]->getName());
					if(redirector.find(temp)!=NULL)
						{
						cdevReportError(CDEV_SEVERITY_INFO, "CDEV Directory", NULL,
								"Message %s already defined for class %s",
								temp, name);
						delete temp;
						}
					else	{
						def = new cdevRedirectorDefinition(temp, *atrPtr[atrIdx]);
						redirector.insert(def->getName(), def);
						}
					}
				else	{
					cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
							"Neither the verb nor the message for \"%s %s\" has a service",
							vrbPtr[vrbIdx]->getName(), atrPtr[atrIdx]->getName());
					}
				}
			}
		}

	// *********************************************************************
	// * Release the memory used to store the pointers.
	// *********************************************************************
	if(msgPtr) delete msgPtr;
	if(vrbPtr) delete vrbPtr;
	if(atrPtr) delete atrPtr;
	}
	

// *****************************************************************************
// * cdevClassDefinition::getElements :
// * 	This method allows the caller to retrieve a list of all element 
// *	definitions of a particular type from all subclasses of the current 
// *	class.
// *****************************************************************************
inline int cdevClassDefinition::getElements ( cdevDirectoryTable::ElementType type,
					      cdevElementDefinition        ** &def, 
					      int                             &nItems )
	{
	StringHash            tempHash;
	cdevClassDefinition * classDef = this;
	
	def    = NULL;
	nItems = 0;
	
	do
		{
		cdevElementDefinition * el = 
			type==cdevDirectoryTable::VERB?classDef->verbs:
			(type==cdevDirectoryTable::ATTRIBUTE?classDef->attributes:
			classDef->messages);

		for(; el!=NULL; el = el->getNext())
			{
			if(tempHash.find(el->getName())==NULL)
				{
				tempHash.insert(el->getName(), el);
				nItems++;
				}
			}
		classDef = classDef->parent;
		} while(classDef!=NULL);

	if(nItems!=0)
		{
		StringHashIterator iter(&tempHash);

		def    = new cdevElementDefinition * [nItems];
		nItems = 0;

		for(iter.first(); iter.key()!=NULL; iter++)
			{
			def[nItems++] = (cdevElementDefinition *)iter.data();
			}
		}

	return nItems;
	}					


// *****************************************************************************
// * cdevClassDefinition::getName :
// * This method allows the caller to retrieve the name of the class.
// *****************************************************************************
inline char * cdevClassDefinition::getName ( void )
	{
	return name;
	}

 
// *****************************************************************************
// * cdevClassDefinition:: :
// * 	This method allows the caller to retrieve the directory table that
// *	owns this device.
// *****************************************************************************
inline cdevDirectoryTable & cdevClassDefinition::getDirectory ( void )
	{
	return directory;
	}

 
// *****************************************************************************
// * cdevClassDefinition::getParent :
// * 	This method allows the caller to retrieve the class definition that
// *	this class inherits from.
// *****************************************************************************
inline cdevClassDefinition * cdevClassDefinition::getParent ( void )
	{
	return parent;
	}
 
 
// *****************************************************************************
// * cdevClassDefinition::getVerbs :
// * 	This method allows the caller to retrieve the lement definition list 
// *	containing all of the classes verbs.
// *****************************************************************************
inline cdevElementDefinition * cdevClassDefinition::getVerbs ( void )
	{
	return verbs;
	}

 
// *****************************************************************************
// * cdevClassDefinition::getAttributes :
// * 	This method allows the caller to retrieve the element definition list
// *	containing all of the classes verbs.
// *****************************************************************************
inline cdevElementDefinition * cdevClassDefinition::getAttributes ( void )
	{
	return attributes;
	}


 
// *****************************************************************************
// * cdevClassDefinition::getMessages :
// * 	This method allows the caller to retrieve the element definition list
// *	containing all of the classes messages.
// *****************************************************************************
inline cdevElementDefinition * cdevClassDefinition::getMessages ( void )
	{
	return messages;
	}

 
// *****************************************************************************
// * cdevClassDefinition::getDevices :
// * 	This method allows the caller to retrieve the device definition list
// *	containing all of the instances of the class.
// *****************************************************************************
inline cdevDeviceDefinition * cdevClassDefinition::getDevices ( void )
	{
	return deviceHead;
	}

 
// *****************************************************************************
// * cdevClassDefinition::getRedirector :
// * 	This method allows the caller to retrieve the redirection list for
// *	the class.
// *****************************************************************************
inline StringHash & cdevClassDefinition::getRedirector ( void )
	{
	return redirector;
	}

 
// *****************************************************************************
// * cdevClassDefinition::getNext :
// * 	This method allows the caller to retrieve the next class definition in
// *	the list.
// *****************************************************************************
inline cdevClassDefinition * cdevClassDefinition::getNext ( void )
	{
	return next;
	}

 
// *****************************************************************************
// * cdevClassDefinition::setNext :
// * 	This method allows the caller to set the next class definition in the
// *	list.
// *****************************************************************************
inline void cdevClassDefinition::setNext ( cdevClassDefinition * Next )
	{
	next = Next;
	}
 

#endif /* _CDEV_CLASS_DEFINITION_H_ */
