#include <values.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/Label.h>
#include <Xm/ScrollBar.h>
#include <Vk/VkInfoDialog.h>
#include "Camera.h"
#include "TiburonApp.h"
#include "camera.h"
#include "DmErrno.h"

#define dprintf if (debug) fprintf

const int Camera::ZoomMinimum = 0;
const int Camera::ZoomMaximum = 1023;
const int Camera::FocusMinimum = 0;
const int Camera::FocusMaximum = 1023;
const int Camera::IrisMinimum = 0;
const int Camera::IrisMaximum = 1023;

Camera::Camera(const char *name, Widget parent, 
	       const char *dmPrefix, Period period)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(_name, xmFormWidgetClass, parent,
				 NULL);
  
  installDestroyHandler();

  _interface = NULL;
  
  Widget nameLabel = 
    XtVaCreateManagedWidget("name", xmLabelWidgetClass, _baseWidget, 
			    XmNtopAttachment, XmATTACH_FORM,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
  
  _useIndicator = 
    XtVaCreateManagedWidget("inUse", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, nameLabel,
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_FORM,
			    NULL);
  
  XtSetMappedWhenManaged(_useIndicator, False);

  _grabRelease = 
    XtVaCreateManagedWidget("grabRelease", xmToggleButtonWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _useIndicator,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);
			    
  XtAddCallback(_grabRelease, XmNvalueChangedCallback,
		&Camera::grabReleaseCallback, (XtPointer )this);

  const int sliderSize = 400;
  
  _zoom = 
    XtVaCreateManagedWidget("zoom", xmScrollBarWidgetClass, _baseWidget,
			    XmNorientation, XmVERTICAL, 
			    XmNsliderSize, sliderSize, 
			    XmNminimum, ZoomMinimum,
			    XmNmaximum, ZoomMaximum + sliderSize,
			    XmNprocessingDirection, XmMAX_ON_TOP,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _grabRelease,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(_zoom, XmNdragCallback, 
		&Camera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_zoom, XmNvalueChangedCallback, 
		&Camera::valueChangedCallback,
		(XtPointer )this);

  _focus = 
    XtVaCreateManagedWidget("focus", xmScrollBarWidgetClass, _baseWidget,
			    XmNorientation, XmVERTICAL,
			    XmNsliderSize, sliderSize,  
			    XmNminimum, FocusMinimum,
			    XmNmaximum, FocusMaximum + sliderSize,
			    XmNprocessingDirection, XmMAX_ON_TOP,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _grabRelease,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _zoom,
			    NULL);

  XtAddCallback(_focus, XmNdragCallback, 
		&Camera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_focus, XmNvalueChangedCallback, 
		&Camera::valueChangedCallback,
		(XtPointer )this);

  _iris = 
    XtVaCreateManagedWidget("iris", xmScrollBarWidgetClass, _baseWidget,
			    XmNorientation, XmVERTICAL,
			    XmNsliderSize, sliderSize,  
			    XmNminimum, IrisMinimum,
			    XmNmaximum, IrisMaximum + sliderSize,
			    XmNprocessingDirection, XmMAX_ON_TOP,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _grabRelease,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _focus,
			    NULL);

  XtAddCallback(_iris, XmNdragCallback, 
		&Camera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_iris, XmNvalueChangedCallback, 
		&Camera::valueChangedCallback,
		(XtPointer )this);

  Widget zoomLabel =
    XtVaCreateManagedWidget("zoomLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _zoom,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _zoom,
			    NULL);

  _rawZoomSetpoint = new DynamicLabel("zoomSetpoint", _baseWidget,
				      XmALIGNMENT_BEGINNING);

  XtVaSetValues(_rawZoomSetpoint->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, zoomLabel,
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _zoom,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, _focus,
		NULL);
  
  _rawZoomSetpoint->show();
  
  _rawZoomValue = new DynamicLabel("zoomValue", _baseWidget,
				XmALIGNMENT_BEGINNING);

  XtVaSetValues(_rawZoomValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _rawZoomSetpoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _zoom,
		NULL);

  _rawZoomValue->show();
  
  Widget focusLabel =
    XtVaCreateManagedWidget("focusLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _focus,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _focus,
			    NULL);

  _rawFocusSetpoint = new DynamicLabel("focusSetpoint", _baseWidget,
				   XmALIGNMENT_BEGINNING);

  XtVaSetValues(_rawFocusSetpoint->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, focusLabel,
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _focus,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, _iris,
		NULL);

  _rawFocusSetpoint->show();


  _rawFocusValue = new DynamicLabel("focusValue", _baseWidget,
				XmALIGNMENT_BEGINNING);

  XtVaSetValues(_rawFocusValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _rawFocusSetpoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _focus,
		NULL);

  _rawFocusValue->show();


  Widget irisLabel =
    XtVaCreateManagedWidget("irisLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _iris,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _iris,
			    NULL);
  
  
  _rawIrisValue = new DynamicLabel("irisValue", _baseWidget,
				   XmALIGNMENT_BEGINNING);

  XtVaSetValues(_rawIrisValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _rawFocusSetpoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, irisLabel,
		NULL);

  _rawIrisValue->show();

  _zoomAngle = new DynamicLabel("zoomMagnification", _baseWidget,
				XmALIGNMENT_BEGINNING);

  XtVaSetValues(_zoomAngle->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _rawZoomValue->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _zoom,
		NULL);

  _zoomAngle->show();

  _focalDistance = new DynamicLabel("focalDistance", _baseWidget,
				    XmALIGNMENT_BEGINNING);

  XtVaSetValues(_focalDistance->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _rawFocusValue->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _focus,
		NULL);

  _focalDistance->show();

  
  Widget fovLabel =
    XtVaCreateManagedWidget("fovLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _zoomAngle->baseWidget(),
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _zoom,
			    NULL);

  _fovWidth = new DynamicLabel("fovWidth", _baseWidget, XmALIGNMENT_BEGINNING);

  XtVaSetValues(_fovWidth->baseWidget(),
		XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNtopWidget, fovLabel,
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, fovLabel,
		NULL);

  _fovWidth->show();

  if (!dmPrefix)
  {
    setError("No dmPrefix specified");
    return;
  }

  char itemName[MaxDmNameLen];
  strcpy(itemName, dmPrefix);
  if (itemName[strlen(itemName)-1] != '.')
    strcat(itemName, ".");

  _dmPrefix = strdup(itemName);
  
  char *namePtr = itemName + strlen(itemName);

  _zoomMode = ZoomPosition;
  _interface = new CameraInterface(_zoomMode, dmPrefix);

  if (setConsumePeriod(period) == -1)
    return;

  _focusing = _zooming = _irising = False;
  
  TiburonApp *app = (TiburonApp *)theApplication;

  // Get colors for "unused" controls
  app->okColors(&_unusedForeground, &_unusedBackground);

  // Get colors for "used" controls
  app->faultColors(&_usedForeground, &_usedBackground);

  _prevUsed = (MBool )999;
  _prevZoomSetpoint = _prevFocusSetpoint = MAXSHORT;
  _prevZoomValue = _prevFocusValue = _prevIrisValue = MAXSHORT;
  _prevFieldWidthValue = _prevFocalDistanceValue = _prevZoomAngleValue = -999.;

  _zoomModeDetector = 
    new DmInputDetector("zoomModeDetector", parent,
			_interface->dmItems()->zoomMode, this);

  activateControls(False);
  updateFromPeer(_zoomModeDetector, NULL);

  checkUsage();

  updateGui();
}


Camera::~Camera()
{
  free(_dmPrefix);
  delete _zoomModeDetector;
  delete _interface;
  delete _rawZoomSetpoint;
  delete _rawZoomValue;
  delete _rawFocusSetpoint;
  delete _rawFocusValue;
  delete _rawIrisValue;
  delete _zoomAngle;
  delete _focalDistance;
  fprintf(stderr, "Deleting fovWidth...\n");
  delete _fovWidth;
  fprintf(stderr, "Done deleting fovWidth...\n");
}


void Camera::updateGui(DM_Item handle)
{
  char errorBuf[errorMsgSize];
  char buf[100];
  DM_Time t;
  Errno err;
  Int16 setpoint;
  Int16 value;
  MBool debug = FALSE;
  Flt64 fltValue;

  if (_interface->zoomSetpoint(&setpoint) == -1) {
    setError("Camera::updateGui(), error reading zoom setpoint"); 
    return;
  }

  dprintf(stderr, 
	  "Camera::updateGui() - %s; "
	  "prevZoom=%d, setpoint=%d, zoomMode=%d\n",
	  _name, _prevZoomSetpoint, setpoint, _zoomMode);

  if (setpoint != _prevZoomSetpoint)
  { 
    sprintf(buf, "%-d", setpoint);
    if (_zoomMode == ZoomRate)
      strcat(buf, "R");
    
    _rawZoomSetpoint->setText(buf);

    if (!_zooming && _zoomMode == ZoomPosition)
    {
      // If user is not moving slider, move it to reflect external change 
      dprintf(stderr, "Camera::updateGui() - set zoom position\n");	
      XtVaSetValues(_zoom, XmNvalue, setpoint, NULL);
    }
    _prevZoomSetpoint = setpoint;
  }

  if (_interface->zoom(&value) == -1) {
    setError("Camera::updateGui(), error reading zoom value"); 
    return;
  }

  if (value != _prevZoomValue)
  { 
    sprintf(buf, "%-d", value);
    _rawZoomValue->setText(buf);

    if (!_zooming && _zoomMode == ZoomRate)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_zoom, XmNvalue, value, NULL);

    _prevZoomValue = value;
  }
  
  if (_interface->focusSetpoint(&value) == -1) {
    setError("Camera::updateGui(), error reading focus setpoint"); 
    return;
  }

  if (value != _prevFocusSetpoint)
  {
    sprintf(buf, "%-d", value);
    _rawFocusSetpoint->setText(buf);

    if (!_focusing)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_focus, XmNvalue, value, NULL);
    
    _prevFocusSetpoint = value;
  }

  if (_interface->focus(&value) == -1) {
    setError("Camera::updateGui(), error reading focus value"); 
    return;
  }

  if (value != _prevFocusValue)
  {
    sprintf(buf, "%-d", value);
    _rawFocusValue->setText(buf);
    _prevFocusValue = value;
  }
  
  if (_interface->iris(&value) == -1) {
    setError("Camera::updateGui(), error reading iris value"); 
    return;
  }

  if (value != _prevIrisValue)
  {
    sprintf(buf, "%-d", value);
    _rawIrisValue->setText(buf);

    _prevIrisValue = value;

    if (!_irising)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_iris, XmNvalue, value, NULL);
  }

  if (_interface->fieldWidth(&fltValue) == -1) {
    setError("Camera::updateGui(), error reading fieldWidth value"); 
    return;
  }

  if (fltValue != _prevFieldWidthValue)
  {
    sprintf(buf, "%-3.1f cm", fltValue);
    _fovWidth->setText(buf);

    _prevFieldWidthValue = fltValue;
  }

  if (_interface->focalDistance(&fltValue) == -1) {
    setError("Camera::updateGui(), error reading focalDistance value"); 
    return;
  }

  if (fltValue != _prevFocalDistanceValue)
  {
    sprintf(buf, "%3.1f cm", fltValue);
    _focalDistance->setText(buf);

    _prevFocalDistanceValue = fltValue;
  }


  if (_interface->zoomAngle(&fltValue) == -1) {
    setError("Camera::updateGui(), error reading zoomAngle value"); 
    return;
  }

  if (fltValue != _prevZoomAngleValue)
  {
    sprintf(buf, "%-.1f deg", fltValue);
    _zoomAngle->setText(buf);

    _prevZoomAngleValue = fltValue;
  }

  checkUsage();
}


void Camera::updateFromPeer(DmGuiObject *peer, XtPointer clientData)
{
  DmInt16Object *dmObject;
  Widget control;
  DynamicLabel *label;
  char errorBuf[errorMsgSize];
  char buf[100];
  DM_Time t;
  Errno err;
  Int16 value;
  Boolean debug = True;

  if (peer == _zoomModeDetector)
  {
    if (_interface->zoomMode(&_zoomMode) == -1)
    {
      fprintf(stderr, "Camera::updateFromPeer() - failed reading zoom mode\n");
      return;
    }
    dprintf(stderr, "Camera::updateFromPeer() - zoomMode = %d\n", _zoomMode);
  }
  
  return;
}


void Camera::setZoom(unsigned value)
{
  Boolean debug = False;
  dprintf(stderr, "Camera::setZoom() - value=%d\n", value);

  if (!_zooming)
    startControl(ZoomControl);
  
  Int16 zoomValue = (Int16 )value;
  
  if (_interface->setZoom(zoomValue) == -1) {
    fprintf(stderr, "Camera::setZoom() - _interface->setZoom() failed\n");
  }
}


void Camera::setFocus(unsigned value)
{
  Boolean debug = False;

  if (!_focusing)
    startControl(FocusControl);
  
  Int16 focusValue = (Int16 )value;
  
  if (_interface->setFocus(focusValue) == -1) {
    fprintf(stderr, "Camera::setFocus() - _interface->setFocus() failed\n");
  }
}


void Camera::setIris(unsigned value)
{
  Boolean debug = False;
  dprintf(stderr, "Camera::setIris() - value=%d\n", value);

  if (!_irising)
    startControl(IrisControl);
  
  Int16 irisValue = (Int16 )value;
  
  if (_interface->setIris(irisValue) == -1) {
    fprintf(stderr, "Camera::setIris() - _interface->setIris() failed\n");
  }
}


void Camera::updateSetpoint(Widget w, unsigned value)
{
  char buf[100];
  sprintf(buf, "%-d", value);
  
  if (w == _zoom)
  {
    _rawZoomSetpoint->setText(buf);
  }
  else if (w == _focus)
  {
    _rawFocusSetpoint->setText(buf);
  }
  else if (w == _iris)
  {
  }
}


void Camera::grabInterface()
{
  if (_interface->grab() == -1) {
    // Grab failed
    activateControls(False);
    XmToggleButtonSetState(_grabRelease, False, False);
  }
  else {
    // Successfully grabbed interface
    activateControls(True);
  }
}


void Camera::releaseInterface()
{
  _interface->release();
  activateControls(False);
}


void Camera::valueChangingCallback(Widget w, XtPointer clientData,
				    XtPointer callData)
{
  Camera *obj = (Camera *)clientData;

  if (w == obj->_zoom)
    obj->setZoom(((XmScrollBarCallbackStruct *)callData)->value);

  else if (w == obj->_focus)
    obj->setFocus(((XmScrollBarCallbackStruct *)callData)->value);

  else if (w == obj->_iris)
    obj->setIris(((XmScrollBarCallbackStruct *)callData)->value);
}


// Called when user is done with slider
void Camera::valueChangedCallback(Widget w, XtPointer clientData,
				  XtPointer callData)
{
  Boolean debug = False;
  dprintf(stderr, "Camera::valueChangedCallback()\n");

  Camera *obj = (Camera *)clientData;
  if (w == obj->_zoom)
  {
    obj->setZoom(((XmScrollBarCallbackStruct *)callData)->value);
    obj->stopControl(ZoomControl);
  }
  else if (w == obj->_focus)
  {
    obj->setFocus(((XmScrollBarCallbackStruct *)callData)->value);
    obj->stopControl(FocusControl);
  }
  else if (w == obj->_iris)
  {
    obj->setIris(((XmScrollBarCallbackStruct *)callData)->value);
    obj->stopControl(IrisControl);
  }

  obj->updateGui();
}


void Camera::grabReleaseCallback(Widget w, XtPointer clientData,
				 XtPointer callData)
{
  Camera *obj = (Camera *)clientData;
  XmToggleButtonCallbackStruct *cb = 
    (XmToggleButtonCallbackStruct *)callData;
  
  if (cb->set)
    obj->grabInterface();
  else
    obj->releaseInterface();
}



int Camera::startControl(Camera::ControlType controlType)
{
  Boolean debug = False;
  
  switch (controlType)
  {
    case FocusControl:
    _focusing = True;
    break;
    
    case ZoomControl:
    _zooming = True;
    break;
    
    case IrisControl:
    _irising = True;
    break;
  }
  
  return 0;
}



int Camera::stopControl(Camera::ControlType controlType)
{
  updateGui();

  switch (controlType)
  {
    case FocusControl:
    _focusing = False;
    break;
    
    case ZoomControl:
    _zooming = False;
    break;
    
    case IrisControl:
    _irising = False;
    break;
  }
  
  return 0;
}


void Camera::checkUsage()
{
  Boolean debug = False;

  if (_interface->inUse()) {
    // Mark as "in use"
    setUseIndicator(True);
    XmToggleButtonSetState(_grabRelease, False, False);
    XtSetSensitive(_grabRelease, False);
  }
  else {
    setUseIndicator(False);
    XtSetSensitive(_grabRelease, True);
  }
}


void Camera::setUseIndicator(Boolean inUse)
{
  if (inUse)
  {
    XtVaSetValues(_useIndicator, 
		  XmNforeground, _usedForeground,
		  XmNbackground, _usedBackground, 
		  NULL);

    if (XtIsRealized(_useIndicator))
      XtMapWidget(_useIndicator);
  }
  else
  {
    XtVaSetValues(_useIndicator, 
		  XmNforeground, _unusedForeground,
		  XmNbackground, _unusedBackground, 
		  NULL);

    if (XtIsRealized(_useIndicator))
      XtUnmapWidget(_useIndicator);
  }
}


void Camera::activateControls(Boolean sensitive)
{
  XtSetSensitive(_zoom, sensitive);
  XtSetSensitive(_focus, sensitive);
  XtSetSensitive(_iris, sensitive);
}


int Camera::startConsume()
{
  Boolean debug = False;
  dprintf(stderr, "Camera::startConsume()\n");
  
  return DmGuiObject::startConsume();
}


int Camera::stopConsume()
{
  Boolean debug = False;
  dprintf(stderr, "Camera::stopConsume()\n");
  
  return DmGuiObject::stopConsume();
}



void Camera::exposeCallback(Widget w, XtPointer clientData,
			    XEvent *event)
{
  XExposeEvent *exposeEvent = (XExposeEvent *)event;

  if (exposeEvent->count != 0)
    // More expose events coming
    return;

  Boolean debug = False;
  dprintf(stderr, "Camera::exposeCallback()\n"); 

  Camera *obj = (Camera *)clientData;
}
