static char DmGuiObject_id[] = "$Header: DmGuiObject.cc,v 1.8 97/10/03 09:29:26 oreilly Exp $";


/*
$Log:	DmGuiObject.cc,v $
Revision 1.8  97/10/03  09:29:26  09:29:26  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.7  97/09/10  21:06:43  21:06:43  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.6  97/09/10  08:48:44  08:48:44  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.5  97/08/12  14:37:53  14:37:53  oreilly (Thomas C. O'Reilly)
Popup menu to plot object's DmObjects

Revision 1.4  97/03/20  12:29:10  12:29:10  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.3  96/10/28  09:11:58  09:11:58  oreilly (Thomas C. O'Reilly)
*** empty log message ***

Revision 1.2  96/07/22  10:41:54  10:41:54  oreilly (Thomas C. O'Reilly)
First external release

*/
#include <Vk/VkInfoDialog.h>
#include <Vk/VkPopupMenu.h>
#include <Vk/VkErrorDialog.h>
#include "DmGuiObject.h"
#include "DmGuiApp.h"
#include "DmErrno.h"
#include "ExternalApp.h"
#include "DmInfoWindow.h"

inline DmGuiApp *DmGuiObject::getDmGuiApp() 
{
  return (DmGuiApp *)theApplication;
}

VkPopupMenu *DmGuiObject::_popupMenu = NULL;

DmGuiObject::DmGuiObject(const char *name, Widget parent) : 
  VkComponent(name)
{
  _consumePeriod = 1000;
  _providePeriod = DM_ASYNC;
  _consumeEnabled = _provideEnabled = FALSE;
  _peer = NULL;
  _stickyConsumer = True;
  
  getDmGuiApp()->registerDmGuiObject(this);
}


DmGuiObject::~DmGuiObject()
{
  // Remove from Telemetry handlers
  DmGuiApp *app = getDmGuiApp();
  app->unregisterConsumer(this);
  
  // Delete DmObjects
  SListIter<DmObject *> providerDmObjects(_providerDmObjectList);
  
  providerDmObjects.reset();
  while (providerDmObjects())
    providerDmObjects.remove();

  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);
  consumerDmObjects.reset();
  while (consumerDmObjects())
    consumerDmObjects.remove();

  app->unregisterDmGuiObject(this);
}


void DmGuiObject::exit(int status)
{
  ((DmGuiApp *)theApplication)->exit(status);
} 


int DmGuiObject::setConsumePeriod(int millisec)
{
  DmGuiApp *app = getDmGuiApp();
  MBool debug = FALSE;

  if (!validMillisec(millisec))
    return -1;  

  _consumePeriod = millisec;

  dprintf(stderr, "setConsumePeriod(): set to %d millisec\n", millisec);
  if (app->registerConsumer(this, millisec) == -1)
    return -1;

  /* Make sure that consumers are started here (even though they've just
     been created). This is because many DmGuiObject constructors call
     updateGui() to initialize display. */
  startConsume();
  
  return 0;
}


MBool DmGuiObject::validMillisec(int millisec)
{
  if (millisec <= 0 && millisec != Aperiodic &&
      millisec != AperiodicGuaranteed)
    return FALSE;
  else
    return TRUE;
}


inline Period DmGuiObject::millisecToPeriod(int millisec)
{
  if (millisec == Aperiodic)
    return DM_ASYNC;

  else if (millisec == AperiodicGuaranteed)
    return DM_STATIC;

  else if (millisec <= 0)
  {
    // Bad rate
    return 0;
  }
  else
  {
    return millisec * 1000;
  }
}


int DmGuiObject::addDmObject(DmObject *dmObject, DmRoleFlags role)
{
  char errorBuf[errorMsgSize];
  
  if (!(role & DmProvideFlag) && !(role & DmConsumeFlag))
  {  
    // Must be consumer and/or provider 
    setError("DmGuiObject::createDmObject() - invalid role flags");
    return -1;
  }
  
  if (role & DmProvideFlag)
  {
    _providerDmObjectList.append(dmObject);

    /* If provider, it's a provider for its entire lifetime */
    if (startProvide() == -1)
      return -1;
  }
  
  if (role & DmConsumeFlag)
    _consumerDmObjectList.append(dmObject);
  
  if (dmObject->error())
  {
    char errorBuf[errorMsgSize];
    dmObject->errorMsg(errorBuf);
    setError(errorBuf);
    return -1;
  }
  
  return 0;
}


void DmGuiObject::deleteDmObject(
				 DmObject *dmObject
				 )
{
  /* When any contained DmObject is deleted, we must unregister the
     DmGuiObject from the telemetry handlers */
  DmGuiApp *app = getDmGuiApp();
  app->unregisterConsumer(this);

  /* Remove from internal DmObject lists (may not actually be on both lists,
   but try anyway) */

  DmObject **dmObj;

  SListIter<DmObject *> providerDmObjects(_providerDmObjectList);

  providerDmObjects.reset();
  while (dmObj = providerDmObjects())
  {
    if (*dmObj == dmObject)
    {
      providerDmObjects.remove();
      break;
    }
  }
  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);

  consumerDmObjects.reset();
  while (dmObj = consumerDmObjects())
  {
    if (*dmObj == dmObject)
    {
      consumerDmObjects.remove();
      break;
    }
  }

  delete dmObject;
}

int DmGuiObject::startConsume()
{
  char errorBuf[errorMsgSize];
  DmObject **dmObject;
  Errno errno;
  char prefix[512];
  Boolean debug = False;
 
  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);

  consumerDmObjects.reset();
  while (dmObject = consumerDmObjects())
  {
    /* To avoid unnecessary network traffic, first determine if object
       is a consumer before starting it. */
    DmItemSts status;
    

    if ((errno = 
	 dm_item_status((*dmObject)->getHandle(), &status)) != SUCCESS)
    {
      sprintf(prefix, 
	      "DmGuiObject::startProvide() dm_itemStatus() failed for "
	      "object %s\n",
	      (*dmObject)->getName());

      sprintDmError(errno, prefix, errorBuf);
      setError(errorBuf);
    }
    else
    {
      if (status.is_cnsmr_period != NO_PERIOD)
      {
	// Item is already consuming - skip it
        dprintf(stderr, 
		"DmGuiObject::startConsume() - item \"%s\" is already "
		"consuming", (*dmObject)->getName());
	
        dm_item_status_free(&status);
        continue;
      }
      dm_item_status_free(&status);
    }

    if ((errno = 
	 (*dmObject)->startConsume(millisecToPeriod(_consumePeriod))) 
	!= SUCCESS)
    {
      char prefix[100];
      sprintf(prefix, "DmGuiObject::startConsume() failed for object %s\n",
	      (*dmObject)->getName());
      
      sprintDmError(errno, prefix, errorBuf);
      setError(errorBuf);
    }
    /* Need stopping place for debugger */
    int dummy = 1;
  }
  _consumeEnabled = TRUE;
  if (error())
    return -1;
  else
    return 0;
}


int DmGuiObject::startProvide()
{
  Boolean debug = False;
  char errorBuf[errorMsgSize];
  DmObject **dmObject;
  Errno errno;
  char prefix[100];

  SListIter<DmObject *> providerDmObjects(_providerDmObjectList);

  providerDmObjects.reset();
  while (dmObject = providerDmObjects())
  {
    if ((*dmObject)->isProvider())
      /* Already providing */
      continue;
    
    if ((errno = (*dmObject)->startProvide(_providePeriod, MultiProviders)) 
	!= SUCCESS)
    {
      sprintf(prefix, "DmGuiObject::startProvide() failed for object %s\n",
	      (*dmObject)->getName());
      
      sprintDmError(errno, prefix, errorBuf);
      setError(errorBuf);
    }
  }
  if (error())
    return -1;

  _provideEnabled = TRUE;
  enableUserInput();
  
  return 0;
}



int DmGuiObject::stopConsume(Boolean checkSticky)
{
  Boolean debug = False;
  
  char errorBuf[errorMsgSize];
  DmObject **dmObject;
  Errno errno;
  
  DmGuiApp *app = (DmGuiApp *)theApplication;
  
  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);

  consumerDmObjects.reset();
  int n = 0;
  while (dmObject = consumerDmObjects())
  {
    dprintf(stderr, "dmObject #%d\n", n++);
    
    if (checkSticky && app->hasStickyConsumer((*dmObject)->getHandle()))
    {
      // Don't stop this consumer, as another DmGuiObject is consuming it
      dprintf(stderr, "\"%s\": DmObject \"%s\" has a sticky consumer\n\n",
              name(), (*dmObject)->getName());

      continue;
    }

    if ((*dmObject)->isConsumer() &&
	(errno = (*dmObject)->stopConsume()) != SUCCESS &&
	errno != EDM_NOTCONSUMER)
    {
      char prefix[100];
      sprintf(prefix, "DmGuiObject::stopConsume() failed for object %s\n",
	      (*dmObject)->getName());
      
      sprintDmError(errno, prefix, errorBuf);
      setError(errorBuf);
    }
  }
  _consumeEnabled = FALSE;

  if (error())
    return -1;
  else
    return 0;
}


int DmGuiObject::stopProvide()
{  
  char errorBuf[errorMsgSize];
  DmObject **dmObject;
  Errno errno;

  SListIter<DmObject *> providerDmObjects(_providerDmObjectList);

  providerDmObjects.reset();
  while (dmObject = providerDmObjects())
  {
    if ((errno = (*dmObject)->stopProvide()) != SUCCESS)
    {
      char prefix[100];
      sprintf(prefix, "DmGuiObject::stopProvide() failed for object %s\n",
	      (*dmObject)->getName());
      
      sprintDmError(errno, prefix, errorBuf);
      setError(errorBuf);
    }
  }
  _provideEnabled = FALSE;
  disableUserInput();
  if (error())
    return -1;
  else
    return 0;
}


int DmGuiObject::startDm()
{
  if (startConsume() == -1)
    return -1;
  
  if (startProvide() == -1)
    return -1;
  
  return 0;
}


int DmGuiObject::stopDm()
{
  if (stopConsume() == -1)
    return -1;
  if (stopProvide() == -1)
    return -1;
  
  return 0;
}


Boolean DmGuiObject::consumes(DM_Item dmItem)
{
  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);
  
  consumerDmObjects.reset();
  DmObject **dmObject;
  
  while (dmObject = consumerDmObjects())
  {
    if ((*dmObject)->getHandle() == dmItem)
      return True;
  }

  return False;
}


void DmGuiObject::eventHandler(Widget w,
			       XtPointer clientData,
			       XEvent *event)
{
  if (_popupMenu)
    delete _popupMenu;
  
  DmGuiObject *obj = (DmGuiObject *)clientData;

  _popupMenu = new VkPopupMenu(obj->_baseWidget, 
			       DmGuiObjectPopupName);
  
  obj->addOptionItems();
  
  obj->postOptions(event);
}


void DmGuiObject::postOptions(XEvent *event)
{
  _popupMenu->show(event);
}


void DmGuiObject::addOptionItems()
{
  char buf[MaxDmNameLen];
  
  DmObject **dmObject;
  
  SListIter<DmObject *> consumerDmObjects(_consumerDmObjectList);

  consumerDmObjects.reset();
  while (dmObject = consumerDmObjects())
  {
    sprintf(buf, "Plot %s", (*dmObject)->getName());
    _popupMenu->addAction(buf,
			  &DmGuiObject::plotDmItemCallback,
			  (XtPointer )(*dmObject)->getName()); 
  }
}


int DmGuiObject::loadPreferences(DynArray<Preference *> *preferences, 
				 char *errorBuf)
{
  return 0;
}


int DmGuiObject::savePreferences(DynArray<Preference *> *preference, 
				 char *errorBuf)
{
  return 0;
}


void DmGuiObject::plotDmItemCallback(Widget w,
				     XtPointer clientData,
				     XtPointer callData)
{
  char *dmName = (char *)clientData;

  DmInfoWindow *dmInfoWindow = 
    new DmInfoWindow("dmInfoWindow", dmName, True);
  
  dmInfoWindow->show();
}


void DmGuiObject::setStickyConsumer(Boolean sticky)
{
  _stickyConsumer = sticky;
}
