#include <values.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Vk/VkApp.h>
#include "PowerSwitch.h"
#include "powerDM.h"


#define dprintf if (debug) fprintf

XtResource PowerSwitch::_resources[] = 
{
  {
    "onForeground",
    "OnForeground",
    XtRPixel,
    sizeof(Pixel),
    XtOffset(PowerSwitch *, _onForeground),
    XmRString,
    "black"
  },

  {
    "onBackground",
    "OnBackground",
    XtRPixel,
    sizeof(Pixel),
    XtOffset(PowerSwitch *, _onBackground),
    XmRString,
    "green"
  },

  {
    "faultForeground",
    "FaultForeground",
    XtRPixel,
    sizeof(Pixel),
    XtOffset(PowerSwitch *, _faultForeground),
    XmRString,
    "white"
  },

  {
    "faultBackground",
    "FaultBackground",
    XtRPixel,
    sizeof(Pixel),
    XtOffset(PowerSwitch *, _faultBackground),
    XmRString,
    "red"
  }
};


PowerSwitch::PowerSwitch(const char *name, Widget parent, 
			 const char *switchName, int period) :
  DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent, NULL);
  installDestroyHandler();
  getResources(_resources, XtNumber(_resources));
  
  _statusDetector = NULL;
  
  _button = 
    XtVaCreateManagedWidget(switchName, xmPushButtonWidgetClass, 
			    _baseWidget, 
			    XmNrecomputeSize, False,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
  
  XtAddCallback(_button, XmNactivateCallback, 
		&PowerSwitch::callback, (XtPointer )this);
  
  _currentLabel = 
    XtVaCreateManagedWidget("xxxxxx", xmLabelWidgetClass,
			    _baseWidget,
			    XmNrecomputeSize, False,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _button,
			    NULL);
			    
  char itemName[MaxDmNameLen];
  char errorBuf[errorMsgSize];
  
  strcpy(itemName, POWER_DM_PREFIX);
  strcat(itemName, switchName);
  strcat(itemName, SWITCH_REQUEST);
  _dmRequest = new DmEnumObject(itemName);
  if (_dmRequest->error())
  {
    _dmRequest->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (addDmObject(_dmRequest, DmProvideFlag) == -1)
    return;

  strcpy(itemName, POWER_DM_PREFIX);
  strcat(itemName, switchName);
  strcat(itemName, SWITCH_STATUS);
  _dmStatus = new DmEnumObject(itemName);
  if (_dmStatus->error())
  {
    _dmStatus->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  _statusDetector = 
    new DmInputDetector("status", parent, _dmStatus, this);
  
  strcpy(itemName, POWER_DM_PREFIX);
  strcat(itemName, switchName);
  strcat(itemName, SWITCH_CURRENT);
  
  _dmCurrent = new DmInt16Object(itemName);
  if (_dmCurrent->error())
  {
    _dmCurrent->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (addDmObject(_dmCurrent, DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(period) == -1)
    return;

  _status = SWITCH_NO_POWER;
  _current = MAXSHORT;
  
  updateGui();
  updateFromPeer(this, NULL);
}


PowerSwitch::~PowerSwitch()
{
  if (_statusDetector)
    delete _statusDetector;
}


void PowerSwitch::updateFromPeer(DmGuiObject *peer, void *clientData)
{
  Boolean debug = False;
  dprintf(stderr, "PowerSwitch::updateFromPeer()\n");

  DM_Time t;
  char errorBuf[errorMsgSize];
  
  // Read status
  if (_dmStatus->read(&_status, &t) != SUCCESS)
  {
    _dmStatus->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  if (_status == SWITCH_NO_POWER)
    XtSetSensitive(_button, False);
  else
    XtSetSensitive(_button, True);

  // Set button colors appropriate to switch status
  Pixel foreground, background;
  Pixel topColor, bottomColor, selectColor, borderColor;

  switch (_status)
  {  
    case SWITCH_NO_POWER:
    case SWITCH_OFF:
    XtVaGetValues(_baseWidget,
		  XmNforeground, &foreground,
		  XmNbackground, &background,
		  NULL);
    break;
    
    case SWITCH_ON:
    foreground = _onForeground;
    background = _onBackground;
    break;
    
    case SWITCH_POWER_ERROR:
    foreground = _faultForeground;
    background = _faultBackground;
    break;

    default:
    return;
  }

  Colormap colormap;
  XtVaGetValues(_baseWidget, XmNcolormap, &colormap, NULL);
  
  XmGetColors(XtScreen(_baseWidget), colormap, background, &borderColor, 
	      &topColor, &bottomColor, &selectColor);
  
  XtVaSetValues(_button, 
		XmNbackground, background, 
		XmNforeground, foreground,
		XmNtopShadowColor, topColor,
		XmNbottomShadowColor, bottomColor,
		XmNselectColor, selectColor,
		XmNarmColor, selectColor,
		XmNborderColor, borderColor,
		NULL);
}



void PowerSwitch::updateGui(DM_Item handle)
{
  DM_Time t;
  char errorBuf[errorMsgSize];
  Int16 current;
  
  // Read current
  if (_dmCurrent->read(&current, &t) != SUCCESS)
  {
    _dmCurrent->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  if (current != _current)
  {
    char buf[100];
    sprintf(buf, "%4.1fA", current / 1000.);
    XmString xmstr = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET);
    XtVaSetValues(_currentLabel, XmNlabelString, xmstr, NULL);
    XmStringFree(xmstr);
    _current = current;
  }
}


void PowerSwitch::callback(Widget w, 
			   XtPointer clientData, XtPointer callData)
{
  PowerSwitch *obj = (PowerSwitch *)clientData;
  obj->toggleState();
}


void PowerSwitch::toggleState()
{
  DM_Time t;
  char errorBuf[errorMsgSize];
  Nat32 newStatus;
  
  // Read present status
  if (_dmStatus->read(&_status, &t) != SUCCESS)
  {
    _dmStatus->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
  switch (_status)
  {
    case SWITCH_NO_POWER:
    // Not allowed to set state when there is no power
    XBell(theApplication->display(), 50);
    return;
    
    break;
    
    case SWITCH_OFF:
    newStatus = SWITCH_ON;
    break;
    
    default:
    newStatus = SWITCH_OFF;
  }

  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  
  if (_dmRequest->write(newStatus, &t) != SUCCESS)
  {
    _dmRequest->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }
  
}  


MotorSwitch::MotorSwitch(const char *name, Widget parent, 
			 const char *switchName, int period) :
  PowerSwitch(name, parent, switchName, period)
{
  _enableButton = 
    XtVaCreateManagedWidget("enable", xmPushButtonWidgetClass, 
			    _baseWidget, 
			    XmNrecomputeSize, False,
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNbottomAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _currentLabel,
			    NULL);
  
  XtAddCallback(_enableButton, XmNactivateCallback, 
		&MotorSwitch::enableCallback, (XtPointer )this);
}



MotorSwitch::~MotorSwitch()
{
}


void MotorSwitch::updateFromPeer(DmGuiObject *peer, void *clientData)
{
  Boolean debug = False;
  dprintf(stderr, "MotorSwitch::updateFromPeer()\n");
  
  PowerSwitch::updateFromPeer(peer, clientData);
  if (_status == SWITCH_ON)
  {
    XtSetSensitive(_enableButton, True);
  }
  else
  {
    XtSetSensitive(_enableButton, False);
  }
}


void MotorSwitch::enableCallback(Widget w, 
				 XtPointer clientData, XtPointer callData)  
{
}

