#include <malloc.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/RowColumn.h>
#include <Xm/ToggleB.h>
#include <Xm/Label.h>
#include <Vk/VkErrorDialog.h>
#include "ValveSwitchMap.h"
#include "DmErrno.h"
#include "TiburonApp.h"
#include "new_consoleDM.h"

/*
ValveSwitchMap allows user to select valve set which is mapped to
an external switch (e.g. joystick trigger). 
*/
ValveSwitchMap::ValveSwitchMap(const char *name, 
			       Widget parent,
			       int nColumns,
			       const char *dmItemName,
			       int nValves,
			       ValveControl **valves)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, 
				 XmNresizePolicy, XmRESIZE_GROW,
				 XmNfractionBase, 5,
				 NULL);

  installDestroyHandler();

  
  Widget frame = 
    XtVaCreateManagedWidget("frame", xmFrameWidgetClass, _baseWidget, 
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);

  
  Widget mgr = 
    XtVaCreateWidget("mgr", xmRowColumnWidgetClass, frame,
		     NULL);


//  Widget label = XtVaCreateManagedWidget("label", xmLabelWidgetClass,
//					 mgr, NULL);

  _nValves = nValves;

  int i;

  menu = new VkOptionMenu(mgr, "menu");

  for (i = 0; i < _nValves; i++)
  {
    menu->addAction(valves[i]->name(),
		     &ValveSwitchMap::toggleCallback, 
		     (XtPointer )this);
  }

  menu->addAction("none", &ValveSwitchMap::toggleCallback, 
		   (XtPointer )this);
  
  XtManageChild(mgr);

  char errorBuf[errorMsgSize];
  
  _dmExternSwitchOpen = new DmBooleanObject(RIGHT_T_SWITCH_UP_STATE_DM);
  if (_dmExternSwitchOpen->error())
  {
    _dmExternSwitchOpen->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  _dmExternSwitchClose = new DmBooleanObject(RIGHT_T_SWITCH_DOWN_STATE_DM);  
  if (_dmExternSwitchClose->error())
  {
    _dmExternSwitchClose->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  _valves = valves;

  for (i = 0; i < _nValves; i++)
  {
    valves[i]->addCallback(ValveControl::startingValveCallback, this,
			   (VkCallbackMethod )&ValveSwitchMap::
			   startingValve);

    valves[i]->addCallback(ValveControl::stoppedValveCallback, this,
			   (VkCallbackMethod )&ValveSwitchMap::
			   stoppedValve);
  }
  
  
  _connectedValve = NULL;

  _dmConnectedValve = new DmInt16Object(dmItemName);
  if (_dmConnectedValve->error())
  {
    _dmConnectedValve->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (addDmObject(_dmConnectedValve, DmProvideFlag | DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(Aperiodic) == -1)
    return;
  
  updateGui();
}


ValveSwitchMap::~ValveSwitchMap()
{
}


void ValveSwitchMap::updateGui(DM_Item handle)
{
  Errno errno;
  char errorBuf[512];
  DM_Time t;
  Int16 valveNo;
  
  if ((errno = _dmConnectedValve->read(&valveNo, &t)) != SUCCESS)
  {
    sprintDmError(errno, "ValveSwitchMap::updateGui()", errorBuf);
    setError(errorBuf);
    return;
  }

  if (valveNo >= 0 && valveNo < _nValves)
  {
    menu->set(valveNo);
    _connectedValve = _valves[valveNo];
  }
  else
  {
    // No valve is connected
    menu->set(_nValves);
    _connectedValve = NULL;      
  }
}


int ValveSwitchMap::connect(ValveControl *valve)
{
  if (valve == NULL)
    return 0;
  
  Errno errno;
  char errorBuf[512];
  DM_Item dmItem;
  
  dmItem = valve->dmOpen()->getHandle();
  
  if (dm_get_connect(dmItem) == dmItem) 
  {
    // Not yet connected
    if ((errno = dm_connect(_dmExternSwitchOpen->getHandle(), 
			    valve->dmOpen()->getHandle())) != SUCCESS)
    {
      sprintDmError(errno, "Valve::connectExternSwitch()", 
		    errorBuf);
      theErrorDialog->postAndWait(errorBuf);
      return -1;
    }
  }
  
  dmItem = valve->dmClose()->getHandle();

  if (dm_get_connect(dmItem) == dmItem)
  { 
    if ((errno = dm_connect(_dmExternSwitchClose->getHandle(), 
			    valve->dmClose()->getHandle())) != SUCCESS)
    {
      sprintDmError(errno, "Valve::connectExternSwitch()", 
		    errorBuf);

      theErrorDialog->postAndWait(errorBuf);
      return -1;
    }
  }

  return 0;
}


int ValveSwitchMap::disconnect(ValveControl *valve)
{
  if (valve == NULL)
    return 0;

  if (valve != _connectedValve)
    return 0;
  
  Errno errno;
  char errorBuf[512];
  
  if ((errno = dm_disconnect(valve->dmOpen()->getHandle())) != SUCCESS &&
      errno != EDM_NOTCONNECTED)
  {
    sprintDmError(errno, "Valve::disconnectExternSwitch()", 
		  errorBuf);

    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }
  
  if ((errno = dm_disconnect(valve->dmClose()->getHandle())) != SUCCESS &&
      errno != EDM_NOTCONNECTED)
  {
    sprintDmError(errno, "Valve::disconnectExternSwitch()", 
		  errorBuf);

    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }

  return 0;
}


int ValveSwitchMap::reconnect()
{
  return connect(_connectedValve);
}


int ValveSwitchMap::writeConnectedValveNo(Int16 valveNo)
{
  char errorBuf[512];
  Errno errno;
  DM_Time t;

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if ((errno = _dmConnectedValve->write(valveNo, &t)) != SUCCESS)
  {
    sprintDmError(errno, 
		  "ValveSwitchMap::writeConnectedValveNo()",
		  errorBuf); 

    theErrorDialog->postAndWait(errorBuf);
    return -1;
  }
  
  return 0;
}


void ValveSwitchMap::startingValve(VkCallbackObject *obj,
					     void *clientData,
					     void *callData)
{
  // User is starting valve from GUI
  ValveControl *valve = (ValveControl *)callData;
  
  // First need to disconnect ExternSwitch, if connected
  if (valve == _connectedValve)
    disconnect(valve);
}


void ValveSwitchMap::stoppedValve(VkCallbackObject *obj,
					    void *clientData,
					    void *callData)
{
  // User is finished using valve from GUI
  reconnect();
}


void ValveSwitchMap::toggle(Widget w)
{
  Boolean debug = ((TiburonApp *)theApplication)->debug();

  char *wName = XtName(w);
  dprintf(stderr, "ValveSwitchMap::toggle() - widget %s\n", wName);

  // Disconnect previously-connected valve (if any)
  if (_connectedValve)
  {
    dprintf(stderr, "ValveSwitchMap::toggle() - disconnecting valve %s\n", 
	    _connectedValve->name());

    if (disconnect(_connectedValve) == 0)
    {
      _connectedValve = NULL;
      writeConnectedValveNo(-1);
    }
  }
  
  // Connect requested valve
  for (int i = 0; i < _nValves; i++)
  {
    if (!strcmp(wName, _valves[i]->name()))
    {
      // User requests to connect valve 
      _connectedValve = _valves[i];
	
      dprintf(stderr, "ValveSwitchMap::toggle() - connect valve #%d\n", i);

      if (connect(_connectedValve) == 0)
      {
	writeConnectedValveNo(i);
      }	    
    }
  }

  return;  
}


int ValveSwitchMap::enableMap(int valveNo)
{
  if (valveNo >= _nValves || valveNo < 0) 
    return -1;

  menu->activateItem(_valves[valveNo]->name());
  return 0;
}


int ValveSwitchMap::disableMap(int valveNo)
{
  if (valveNo >= _nValves || valveNo < 0) 
    return -1;

  menu->deactivateItem(_valves[valveNo]->name());
  return 0;
}



void ValveSwitchMap::toggleCallback(Widget w,
				    XtPointer clientData,
				    XtPointer callData)
{
  
  ValveSwitchMap *obj = (ValveSwitchMap *)clientData;
  obj->toggle(w);
}

