#include <values.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Xm/ScrollBar.h>
#include <Vk/VkInfoDialog.h>
#include "MainCamera.h"
#include "PanasonicWVSetup.h"
#include "TiburonApp.h"
#include "camera.h"
#include "DmErrno.h"
#include "IviewMonitor.h"

#define dprintf if (debug) fprintf

const int MainCamera::ZoomMinimum = 0;
const int MainCamera::ZoomMaximum = 1023;
const int MainCamera::FocusMinimum = 0;
const int MainCamera::FocusMaximum = 1023;
const int MainCamera::IrisMinimum = 0;
const int MainCamera::IrisMaximum = 1023;

MainCamera::MainCamera(const char *name, Widget parent, 
		       const char *dmPrefix, IviewDevice cameraDevice,
		       int period, IviewMonitor *monitor)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(_name, xmFormWidgetClass, parent,
				 NULL);
  
  installDestroyHandler();

  _statusTimer = NULL;
  _iviewMsgDetector = NULL;
  _irisModeDetector = NULL;
  _iviewControl = 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);

  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, _useIndicator,
			    XmNleftAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(_zoom, XmNdragCallback, 
		&MainCamera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_zoom, XmNvalueChangedCallback, 
		&MainCamera::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, _useIndicator,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _zoom,
			    NULL);

  XtAddCallback(_focus, XmNdragCallback, 
		&MainCamera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_focus, XmNvalueChangedCallback, 
		&MainCamera::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, _useIndicator,
			    XmNleftAttachment, XmATTACH_WIDGET,
			    XmNleftWidget, _focus,
			    NULL);

  XtAddCallback(_iris, XmNdragCallback, 
		&MainCamera::valueChangingCallback,
		(XtPointer )this);

  XtAddCallback(_iris, XmNvalueChangedCallback, 
		&MainCamera::valueChangedCallback,
		(XtPointer )this);

  _zoomLabel =
    XtVaCreateManagedWidget("zoomLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _zoom,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _zoom,
			    NULL);

  _zoomSetPoint = new DynamicLabel("zoomSetPoint", _baseWidget,
				   XmALIGNMENT_BEGINNING);

  XtVaSetValues(_zoomSetPoint->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _zoomLabel,
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _zoom,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, _focus,
		NULL);
  
  _zoomSetPoint->show();
  
  _zoomValue = new DynamicLabel("zoomValue", _baseWidget,
				XmALIGNMENT_BEGINNING);

  XtVaSetValues(_zoomValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _zoomSetPoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _zoom,
		NULL);

  _zoomValue->show();
  
  _focusLabel =
    XtVaCreateManagedWidget("focusLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _focus,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _focus,
			    NULL);

  _focusSetPoint = new DynamicLabel("focusSetPoint", _baseWidget,
				   XmALIGNMENT_BEGINNING);

  XtVaSetValues(_focusSetPoint->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _focusLabel,
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _focus,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, _iris,
		NULL);

  _focusSetPoint->show();


  _focusValue = new DynamicLabel("focusValue", _baseWidget,
				XmALIGNMENT_BEGINNING);

  XtVaSetValues(_focusValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _focusSetPoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _focus,
		NULL);

  _focusValue->show();


  _irisLabel =
    XtVaCreateManagedWidget("irisLabel", xmLabelWidgetClass, _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _iris,
			    XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
			    XmNleftWidget, _iris,
			    NULL);
  
  
  _irisValue = new DynamicLabel("irisValue", _baseWidget,
				   XmALIGNMENT_BEGINNING);

  XtVaSetValues(_irisValue->baseWidget(),
		XmNtopAttachment, XmATTACH_WIDGET,
		XmNtopWidget, _focusSetPoint->baseWidget(),
		XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget, _irisLabel,
		NULL);

  _irisValue->show();
  

  Widget button = 
    XtVaCreateManagedWidget("settings", xmPushButtonWidgetClass, 
			    _baseWidget,
			    XmNtopAttachment, XmATTACH_WIDGET,
			    XmNtopWidget, _zoomValue->baseWidget(),
			    XmNleftAttachment, XmATTACH_FORM,
			    XmNrightAttachment, XmATTACH_FORM,
			    NULL);

  XtAddCallback(button, XmNactivateCallback, 
		&MainCamera::setupCallback, (XtPointer )this);
  
  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);

  strcpy(namePtr, ZOOM_SET_POINT_DM);

  _dmZoomSetPoint = new DmInt16Object(itemName);

  if (addDmObject(_dmZoomSetPoint, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, ZOOM_POSITION_DM);
  _dmZoomValue = new DmInt16Object(itemName);
  if (addDmObject(_dmZoomValue, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, INTEGRATED_FOCUS_SET_POINT_DM);
  _dmFocusSetPoint = new DmInt16Object(itemName);
  if (addDmObject(_dmFocusSetPoint, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, FOCUS_POSITION_DM);
  _dmFocusValue = new DmInt16Object(itemName);
  if (addDmObject(_dmFocusValue, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, IRIS_VALUE_DM);
  _dmIrisValue = new DmInt16Object(itemName);
  if (addDmObject(_dmIrisValue, DmConsumeFlag) == -1)
    return;

  strcpy(namePtr, IRIS_MODE_DM);
  _dmIrisMode = new DmInt16Object(itemName);
  _irisModeDetector = new DmInputDetector("irisModeDetector", parent,
					  _dmIrisMode, this);
  
  strcpy(namePtr, ZOOM_CONTROL_MODE_DM);
  _dmZoomMode = new DmInt16Object(itemName);
  _zoomModeDetector = new DmInputDetector("zoomModeDetector", parent,
					  _dmZoomMode, this);
  

  if (setConsumePeriod(period) == -1)
    return;

  _dialog = new PanasonicWVDialog("mainCameraDialog", _dmPrefix);

  _camera = cameraDevice;
  
  char controlName[MaxControlNameLen];
  sprintf(controlName, "tmacs-%s", name);
  _iviewControl = new IviewControl(IviewFocus | IviewZoom | IviewIris, TRUE,
				   controlName);

  if (_iviewControl->error())
  {
    char errorBuf[errorMsgSize];
    _iviewControl->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  char errorBuf[errorMsgSize];
  
  DM_Item dmItem = _iviewControl->msgItem();
  DmObject *msgObj = new DmObject(&dmItem, 1);
  if (msgObj->error())
  {
    msgObj->errorMsg(errorBuf);
    setError(errorBuf);
    return;
  }

  _iviewMsgDetector = new DmInputDetector("iviewMsgDetector", parent,
					  msgObj, this);
  
  _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;
  
  _devices = 0;

  _monitor = monitor;
  
  updateFromPeer(_irisModeDetector, NULL);
  updateFromPeer(_zoomModeDetector, NULL);

  updateGui();
  checkIviewStatus();
  
  startTimer();
}


MainCamera::~MainCamera()
{
  free(_dmPrefix);
  delete _irisModeDetector;
  delete _iviewMsgDetector;
  delete _iviewControl;
}


void MainCamera::updateGui(DM_Item handle)
{
  char errorBuf[errorMsgSize];
  char buf[100];
  DM_Time t;
  Errno err;
  Int16 setPoint;
  Int16 value;
  MBool debug = FALSE;
  
  if ((err = _dmZoomSetPoint->read(&setPoint, &t)) != SUCCESS)
  {
    sprintDmError(err, "MainCamera::updateGui(), reading zoom setpoint", 
		  errorBuf);
    setError(errorBuf);
    return;
  }

  dprintf(stderr, 
	  "MainCamera::updateGui() - %s; "
	  "prevZoom=%d, setPoint=%d, zoomMode=%d\n",
	  _name, _prevZoomSetPoint, setPoint, _zoomMode);

  if (setPoint != _prevZoomSetPoint)
  { 
    sprintf(buf, "%-d", setPoint);
    if (_zoomMode == Rate)
      strcat(buf, "R");
    
    _zoomSetPoint->setText(buf);

    if (!_zooming && _zoomMode == Position)
    {
      // If user is not moving slider, move it to reflect external change 
      dprintf(stderr, "MainCamera::updateGui() - set zoom position\n");	
      XtVaSetValues(_zoom, XmNvalue, setPoint, NULL);
    }
    _prevZoomSetPoint = value;
  }

  if ((err = _dmZoomValue->read(&value, &t)) != SUCCESS)
  {
    sprintDmError(err, "MainCamera::updateGui(), reading zoom", errorBuf);
    setError(errorBuf);
    return;
  }

  if (value != _prevZoomValue)
  { 
    sprintf(buf, "%-d", value);
    _zoomValue->setText(buf);

    if (!_zooming && _zoomMode == Rate)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_zoom, XmNvalue, value, NULL);

    _prevZoomValue = value;
  }
  
  if ((err = _dmFocusSetPoint->read(&value, &t)) != SUCCESS)
  {
    sprintDmError(err, "MainCamera::updateGui(), reading focus setpoint", 
		  errorBuf);

    setError(errorBuf);
    return;
  }

  if (value != _prevFocusSetPoint)
  {
    sprintf(buf, "%-d", value);
    _focusSetPoint->setText(buf);

    if (!_focusing)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_focus, XmNvalue, value, NULL);
    
    _prevFocusSetPoint = value;
  }

  if ((err = _dmFocusValue->read(&value, &t)) != SUCCESS)
  {
    sprintDmError(err, "MainCamera::updateGui(), reading focus", errorBuf);
    setError(errorBuf);
    return;
  }

  if (value != _prevFocusValue)
  {
    sprintf(buf, "%-d", value);
    _focusValue->setText(buf);
    _prevFocusValue = value;
  }
  
  if ((err = _dmIrisValue->read(&value, &t)) != SUCCESS)
  {
    sprintDmError(err, "MainCamera::updateGui(), reading iris", errorBuf);
    setError(errorBuf);
    return;
  }
  if (value != _prevIrisValue)
  {
    sprintf(buf, "%-d", value);
    _irisValue->setText(buf);

    _prevIrisValue = value;

    if (!_irising)
      // If user is not moving slider, move it to reflect external change 
      XtVaSetValues(_iris, XmNvalue, value, NULL);
  }
}


void MainCamera::updateFromPeer(DmGuiObject *peer, XtPointer clientData)
{
  DmInt16Object *dmObject;
  Widget control;
  DynamicLabel *label;
  char errorBuf[errorMsgSize];
  char buf[100];
  DM_Time t;
  Errno err;
  Int16 value;
  MBool debug = TRUE;
  
  if (peer == _irisModeDetector)
  {
    if ((err = _dmIrisMode->read(&value, &t)) != SUCCESS)
    {
      sprintDmError(err, "MainCamera::updateFromPeer()", errorBuf);
      setError(errorBuf);
      return;
    }

    if (value == PanasonicWVSetup::ManualIris)
    {
      XtSetSensitive(_iris, True);
      _autoIris = False;
    }
    else if (value == PanasonicWVSetup::AutoIris)
    {
      XtSetSensitive(_iris, False);
      _autoIris = True;
    }
  }
  else if (peer == _zoomModeDetector)
  {
    if ((err = _dmZoomMode->read(&value, &t)) != SUCCESS)
    {
      sprintDmError(err, "MainCamera::updateFromPeer()", errorBuf);
      setError(errorBuf);
      return;
    }

    _zoomMode = value;

  }
  
  else if (peer == _iviewMsgDetector)
  {
    IviewMessage message;
    
    _iviewControl->getMsg(&message);

    if (message.deviceMask & _camera)
    {
      // We have camera
      setUseIndicator(False);
    }

    if (message.serverStarting)
    {
      _iviewControl->initialize();
    }
    dprintf(stderr, "MainCamera::updateFromPeer() - msg.deviceMask = %d\n",
	    message.deviceMask);
    
    _devices = message.deviceMask;
  }
  
  return;
}


void MainCamera::setZoom(unsigned value)
{
  Boolean debug = False;
  dprintf(stderr, "MainCamera::setZoom() - value=%d\n", value);

  if (!_zooming)
    startControl(ZoomControl);
  
  float zoomValue = (float )value / ZoomMaximum;
  dprintf(stderr, "setZoom() - value=%d, %.3f\n", value, zoomValue);
  
  _iviewControl->setZoom(zoomValue);
}


void MainCamera::setFocus(unsigned value)
{
  Boolean debug = False;

  if (!_focusing)
    startControl(FocusControl);
  
  float focusValue = (float )value / FocusMaximum;
  dprintf(stderr, "setFocus() - value=%d, %.3f\n", value, focusValue);
  
  _iviewControl->setFocus(focusValue);
}


void MainCamera::setIris(unsigned value)
{
  Boolean debug = False;
  dprintf(stderr, "MainCamera::setIris() - value=%d\n", value);

  if (!_irising)
    startControl(IrisControl);
  
  float irisValue = (float )value / IrisMaximum;
  dprintf(stderr, "setIris() - value=%d, %.3f\n", value, irisValue);
  
  _iviewControl->setIris(irisValue);
}


void MainCamera::updateSetPoint(Widget w, unsigned value)
{
  char buf[100];
  sprintf(buf, "%-d", value);
  
  if (w == _zoom)
  {
    _zoomSetPoint->setText(buf);
  }
  else if (w == _focus)
  {
    _focusSetPoint->setText(buf);
  }
  else if (w == _iris)
  {
  }
}


void MainCamera::valueChangingCallback(Widget w, XtPointer clientData,
				    XtPointer callData)
{
  MainCamera *obj = (MainCamera *)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 MainCamera::valueChangedCallback(Widget w, XtPointer clientData,
				       XtPointer callData)
{
  MainCamera *obj = (MainCamera *)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);
  }
}


void MainCamera::setupCallback(Widget w, XtPointer clientData,
			       XtPointer callData)
{
  MainCamera *obj = (MainCamera *)clientData;
  obj->_dialog->setTitle(obj->name());
  obj->_dialog->post(NULL, NULL, NULL,
		     NULL,
		     (XtPointer )obj->_dialog);

  obj->_dialog->setup->updateGui();
}


void MainCamera::checkIviewStatus()
{
  int controlId;
  pid_t pid;
  
  MBool inUse = _monitor->hasControl(_camera, 
				     IviewFocus | IviewZoom | IviewIris, 
				     &controlId,
				     &pid);

  if (inUse && controlId == _iviewControl->id())
    // Not used by external source
    inUse = False;

  if (inUse != _prevUsed)
  {
    setUseIndicator(inUse);
    _prevUsed = inUse;
  }
}



int MainCamera::startControl(MainCamera::ControlType controlType)
{
  Boolean debug = False;
  
  switch (controlType)
  {
    case FocusControl:
    _focusing = True;
    break;
    
    case ZoomControl:
    _zooming = True;
    break;
    
    case IrisControl:
    _irising = True;
    break;
  }
  
  if (!(_devices & _camera))
  {
    /* We don't currently have control of camera; get it */
    int status;
    _devices = _camera;
    dprintf(stderr, "MainCamera::startControl() - setDevices()\n");
    
    if ((status = _iviewControl->setDevices(_devices, _camera, 10)) != 
	IviewSuccess && status != IviewTimedOut)
    {
      if (status == IviewNotStarted)
      {
	theInfoDialog->postAndWait("Iview not responding");
	return -1;
      }
    }
  }
  
  stopTimer();
  return 0;
}



int MainCamera::stopControl(MainCamera::ControlType controlType)
{
  switch (controlType)
  {
    case FocusControl:
    _focusing = False;
    break;
    
    case ZoomControl:
    _zooming = False;
    break;
    
    case IrisControl:
    _irising = False;
    break;
  }
  
  int status;

  startTimer();

  return 0;
}


void MainCamera::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);
  }
}


int MainCamera::startConsume()
{
  Boolean debug = False;
  dprintf(stderr, "MainCamera::startConsume()\n");
  
  startTimer();
  return DmGuiObject::startConsume();
}


int MainCamera::stopConsume()
{
  Boolean debug = False;
  dprintf(stderr, "MainCamera::stopConsume()\n");
  
  stopTimer();
  return DmGuiObject::stopConsume();
}


void MainCamera::startTimer()
{
  if (!_statusTimer)
  {
    _statusTimer = 
      XtAppAddTimeOut(theApplication->appContext(), 1000,
		      &MainCamera::timerCallback, (XtPointer )this);  
  }
}



void MainCamera::stopTimer()
{
  if (_statusTimer)
  {
    XtRemoveTimeOut(_statusTimer);
    _statusTimer = NULL;
  }
}


Void MainCamera::timerCallback(XtPointer clientData, XtIntervalId *id)
{
  Boolean debug = False;
  dprintf(stderr, "*** MainCamera::timerCallback()\n");
  
  MainCamera *obj = (MainCamera *)clientData;

  obj->checkIviewStatus();
  obj->_statusTimer = NULL;
  obj->startTimer();
}



void MainCamera::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, "MainCamera::exposeCallback()\n"); 

  MainCamera *obj = (MainCamera *)clientData;

  // Start iview status check timer
  obj->startTimer();
}
