#include <time.h>
#include <strings.h>
#include <netdb.h>
#include <socket.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/Separator.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/ScrolledW.h>
#include <Vk/VkApp.h>
#include <Vk/VkComponent.h>
#include <Vk/VkSimpleWindow.h>
#include "DmGuiApp.h"
#include "DmGuiObject.h"
#include "DmErrno.h"
#include "DmControlSource.h"
#include "IviewMonitor.h"
#include "DmInputDetector.h"
#include "DynamicLabel.h"

class ControlSourceStatus : public DmGuiObject
{
  public:
  
  ControlSourceStatus(const char *name, Widget parent, 
		      IviewMonitor *monitor, int id);

  ~ControlSourceStatus();
  
  const char *className()
  {
    return "ControlSourceStatus";
  }

  virtual void updateGui(DM_Item dmItem = NULL);
  
  MBool clientCheck();
  
  virtual void afterRealizeHook();
  
  protected:

  void showParameters(Boolean show);
  
  unsigned short _id;
  
  Widget _paramMgr;
  Widget _owner; 
  Widget _controlName;
  Widget _axes;
  Widget _portCameraReq, _portLightReq, _stbdCameraReq, _stbdLightReq;
  Widget _portCamera, _portLight, _stbdCamera, _stbdLight;
  DynamicLabel *_requestTime;

  IviewMonitor *_monitor;
  DmControlSource *_controlSource;
  DmControlSource::AperiodicRequest _prevRequest;
  DmControlSource::Message _prevMessage;
  
  MBool _inUse;
  MBool _updatedGui;
}; 


class MonitorGui : public VkComponent
{
  public:
  
  MonitorGui(const char *name, Widget parent);
  ~MonitorGui();
  
  protected:

  void update();
  
  ControlSourceStatus *_status[MaxControlSources];
  
  XtIntervalId _statusTimer;

  IviewMonitor *_monitor;

  void startTimer();
  
  private:
  
  static void timerCallback(XtPointer clientData, XtIntervalId *id);

};


class MonitorWindow: public VkSimpleWindow 
{
  public:

  MonitorWindow ( const char *name );
  
  ~MonitorWindow ();
  
  virtual const char* className()
  {
    return "MonitorWindow";
  }

  protected:
  MonitorGui *_gui;
};


ControlSourceStatus::ControlSourceStatus(const char *name, Widget parent,
					 IviewMonitor *monitor, int id)
  : DmGuiObject(name, parent)
{
  _baseWidget = XtVaCreateWidget(name, xmRowColumnWidgetClass, parent,
				 XmNorientation, XmHORIZONTAL, NULL);
  
  installDestroyHandler();
  
  _inUse = -999;
  
  _monitor = monitor;

  _id = id;

  _controlSource = _monitor->controlSource[_id];
  
  char buf[100];
  sprintf(buf, "%02d", id);
  
  XmString xmstr = XmStringCreateSimple(buf);
  
  Widget label = XtVaCreateManagedWidget("id", xmLabelWidgetClass, _baseWidget,
					 XmNlabelString, xmstr, NULL);
  
  XmStringFree(xmstr);

  _owner = 
    XtVaCreateManagedWidget("owner", xmLabelWidgetClass, _baseWidget, NULL);

  _paramMgr = 
    XtVaCreateWidget("paramMgr", xmRowColumnWidgetClass, _baseWidget, 
		     XmNorientation, XmHORIZONTAL, NULL);

  _controlName = 
    XtVaCreateManagedWidget("name", xmLabelWidgetClass, _paramMgr, NULL);

  _axes = 
    XtVaCreateManagedWidget("axes", xmLabelWidgetClass, _paramMgr, NULL);

  Widget mgr;
  
  mgr = XtVaCreateWidget("portLightMgr", xmRowColumnWidgetClass, _paramMgr,
			 XmNorientation, XmVERTICAL, NULL);
  
  _portLightReq = 
    XtVaCreateManagedWidget("portLightReq", xmLabelWidgetClass, mgr,
			    NULL);

  _portLight = 
    XtVaCreateManagedWidget("portLight", xmLabelWidgetClass, mgr,
			    NULL);

  XtManageChild(mgr);
  
  mgr = XtVaCreateWidget("portCameraMgr", xmRowColumnWidgetClass, _paramMgr,
			 XmNorientation, XmVERTICAL, NULL);

  _portCameraReq = 
    XtVaCreateManagedWidget("portCameraReq", xmLabelWidgetClass, mgr,
			    NULL);

  _portCamera = 
    XtVaCreateManagedWidget("portCamera", xmLabelWidgetClass, mgr,
			    NULL);

  XtManageChild(mgr);

  mgr = XtVaCreateWidget("stbdLightMgr", xmRowColumnWidgetClass, _paramMgr,
			 XmNorientation, XmVERTICAL, NULL);

  _stbdLightReq = 
    XtVaCreateManagedWidget("stbdLightReq", xmLabelWidgetClass, mgr,
			    NULL);

  _stbdLight = 
    XtVaCreateManagedWidget("stbdLight", xmLabelWidgetClass, mgr,
			    NULL);

  XtManageChild(mgr);

  mgr = XtVaCreateWidget("stbdCameraMgr", xmRowColumnWidgetClass, _paramMgr,
			 XmNorientation, XmVERTICAL, NULL);

  _stbdCameraReq = 
    XtVaCreateManagedWidget("stbdCamera", xmLabelWidgetClass, mgr,
			    NULL);

  _stbdCamera = 
    XtVaCreateManagedWidget("stbdCamera", xmLabelWidgetClass, mgr,
			    NULL);

  XtManageChild(mgr);

  _requestTime = new DynamicLabel("requestTime", _paramMgr);
  _requestTime->show();
  
  XtManageChild(_paramMgr);
  
  DM_Item dmItem = _controlSource->aperiodicRequestItem();
  DmObject *dmObj = new DmObject(&dmItem, 1);

  if (addDmObject(dmObj, DmConsumeFlag) == -1)
    return;

  dmItem = _controlSource->messageItem();
  dmObj = new DmObject(&dmItem, 1);

  if (addDmObject(dmObj, DmConsumeFlag) == -1)
    return;

  dmItem = _controlSource->statusItem();
  dmObj = new DmObject(&dmItem, 1);

  if (addDmObject(dmObj, DmConsumeFlag) == -1)
    return;
  
  if (setConsumePeriod(AperiodicGuaranteed) == -1)
    return;
  

  bzero((void *)&_prevRequest, sizeof(DmControlSource::AperiodicRequest));
  bzero((void *)&_prevMessage, sizeof(DmControlSource::Message));
  _updatedGui = FALSE;
  
  updateGui();
}


ControlSourceStatus::~ControlSourceStatus()
{
  delete _requestTime;
}


MBool ControlSourceStatus::clientCheck()
{
  MBool inUse;
  _controlSource->hasClient(&inUse);

  if (!inUse)
  {
    if (_inUse)
    {
      // Previously used - update
      XmString xmstr = XmStringCreateSimple("none");
      XtVaSetValues(_owner, XmNlabelString, xmstr, NULL);
      showParameters(False);
    }
  }
  else
    showParameters(True);
 
  _inUse = inUse;

  return inUse;
}


void ControlSourceStatus::updateGui(DM_Item dmItem)
{
  MBool inUse;
  Boolean debug = True;
  
  if (!(inUse = clientCheck()))
    return;
  
  showParameters(True);

  char buf[100];

  DmControlSource::AperiodicRequest request;
  DmControlSource::Message msg;

  unsigned short devices;

  _controlSource->readRequest(&request);

  _controlSource->readMessage(&msg);    

  XmString xmstr;

  if (!_updatedGui || request.hostAddr != _prevRequest.hostAddr || 
      request.pid != _prevRequest.pid)
  {
    struct in_addr addr;
    addr.s_addr = request.hostAddr;

    char hostname[256];
    
    if (addr.s_addr)
    {
      struct hostent *hostEntry = 
	gethostbyaddr((char *)&addr, sizeof(in_addr), AF_INET);

      strcpy(hostname, hostEntry->h_name);
    }
    else
    {
      sprintf(hostname, "???");
    }

    char *ptr;
    
    // Remove domain name stuff
    if ((ptr = strchr(hostname, '.')) != NULL)
    {
      *ptr = '\0';
    }
    
    sprintf(buf, "%s:%d", hostname, request.pid);
    xmstr = XmStringCreateSimple(buf);
    XtVaSetValues(_owner, XmNlabelString, xmstr, NULL);
    XmStringFree(xmstr);
  }
  
  if (!_updatedGui || strcmp(request.controlName, _prevRequest.controlName))
  {
    sprintf(buf, "%s", request.controlName);
    xmstr = XmStringCreateSimple(buf);
    XtVaSetValues(_controlName, XmNlabelString, xmstr, NULL);
    XmStringFree(xmstr);
  }
  
  if (!_updatedGui || request.axisMask != _prevRequest.axisMask)
  {
    sprintf(buf, "");
    char *ptr = buf;

    if (request.axisMask & IviewPan)
    {
      sprintf(ptr, "P ");
      ptr += strlen(ptr);
    }
    if (request.axisMask & IviewPanRate)
    {
      sprintf(ptr, "Pr ");
      ptr += strlen(ptr);
    }

    if (request.axisMask & IviewTilt)
    {
      sprintf(ptr, "T ");
      ptr += strlen(ptr);
    }

    if (request.axisMask & IviewTiltRate)
    {
      sprintf(ptr, "Tr ");
      ptr += strlen(ptr);
    }
  
    if (request.axisMask & IviewZoom)
    {
      sprintf(ptr, "Z ");
      ptr += strlen(ptr);
    }

    if (request.axisMask & IviewZoomRate)
    {
      sprintf(ptr, "Zr ");
      ptr += strlen(ptr);
    }
  
    if (request.axisMask & IviewFocus)
    {
      sprintf(ptr, "F ");
      ptr += strlen(ptr);
    }

    if (request.axisMask & IviewFocusRate)
    {
      sprintf(ptr, "Fr ");
      ptr += strlen(ptr);
    }
  
    if (request.axisMask & IviewIris)
    {
      sprintf(ptr, "I ");
      ptr += strlen(ptr);
    }

    if (request.axisMask & IviewIrisRate)
    {
      sprintf(ptr, "Ir ");
      ptr += strlen(ptr);
    }
    
    xmstr = XmStringCreateSimple(buf);
    XtVaSetValues(_axes, XmNlabelString, xmstr, NULL);
    XmStringFree(xmstr);
  }
  
  if (!_updatedGui || request.deviceMask != _prevRequest.deviceMask)
  {
    Boolean map;
  
    if (request.deviceMask & IviewPortLight)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_portLightReq, map);
  
    if (request.deviceMask & IviewPortCamera)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_portCameraReq, map);

    if (request.deviceMask & IviewStbdLight)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_stbdLightReq, map);

    if (request.deviceMask & IviewStbdCamera)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_stbdCameraReq, map);
  }

  if (!_updatedGui || msg.deviceMask != _prevMessage.deviceMask)
  {
    Boolean map;
  
    if (msg.deviceMask & IviewPortLight)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_portLight, map);
  
    if (msg.deviceMask & IviewPortCamera)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_portCamera, map);

    if (msg.deviceMask & IviewStbdLight)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_stbdLight, map);

    if (msg.deviceMask & IviewStbdCamera)
      map = True;
    else
      map = False;

    XtSetMappedWhenManaged(_stbdCamera, map);
  }

  char *type;
  
  double t = _controlSource->requestTime();
  int sec = (int )t;
  struct tm *timePtr = gmtime((const time_t *)&sec);
  strftime(buf, sizeof(buf), "%j:%H:%M:%S", timePtr);
  _requestTime->setText(buf);
  
  
  bcopy((void *)&request, (void *)&_prevRequest, 
	sizeof(DmControlSource::AperiodicRequest));

  bcopy((void *)&msg, (void *)&_prevMessage, sizeof(DmControlSource::Message));
  
  _inUse = inUse;
  _updatedGui = TRUE;
}



void ControlSourceStatus::showParameters(Boolean show)
{
  XtSetMappedWhenManaged(_paramMgr, show);
}


void ControlSourceStatus::afterRealizeHook()
{
  updateGui();
}


MonitorGui::MonitorGui(const char *name, Widget parent)
{
  int nColumns = 5;
  
  _baseWidget = XtVaCreateWidget(name, xmFormWidgetClass, parent,
				 NULL);				 


  _monitor = new IviewMonitor();

  Widget mgr;
  
  mgr = XtVaCreateWidget("header", xmRowColumnWidgetClass, _baseWidget,
			 XmNorientation, XmHORIZONTAL,
			 XmNleftAttachment, XmATTACH_FORM,
			 XmNtopAttachment, XmATTACH_FORM,
			 XmNrightAttachment, XmATTACH_FORM,
			 NULL);

  XtVaCreateManagedWidget("idLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("ownerLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("nameLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("axesLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("portLightLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("portCameraLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("stbdLightLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("stbdCameraLabel", xmLabelWidgetClass, mgr, NULL);

  XtVaCreateManagedWidget("requestTimeLabel", xmLabelWidgetClass, mgr, NULL);

  XtManageChild(mgr);
  
  Widget scrolledWindow = 
    XtVaCreateWidget("scrolledWindow", xmScrolledWindowWidgetClass, _baseWidget,
		     XmNscrollingPolicy, XmAUTOMATIC,
		     XmNscrollBarDisplayPolicy, XmAS_NEEDED,
		     XmNtopAttachment, XmATTACH_WIDGET,
		     XmNtopWidget, mgr,
		     XmNleftAttachment, XmATTACH_FORM,
		     XmNrightAttachment, XmATTACH_FORM,
		     XmNbottomAttachment, XmATTACH_FORM,
		     NULL);

  mgr = 
    XtVaCreateWidget("mgr", xmRowColumnWidgetClass, scrolledWindow,
		     NULL);
  
  for (int id = 0; id < MaxControlSources; id++)
  {
    Widget frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, mgr,
					   NULL);
    
    _status[id] = new ControlSourceStatus("status", frame, _monitor, id);
    _status[id]->show();
  }

  XtManageChild(mgr);
  XtManageChild(scrolledWindow);
  
  startTimer();
}


MonitorGui::~MonitorGui()
{
  for (int id = 0; id < MaxControlSources; id++)
    delete _status[id];
}


void MonitorGui::update()
{
  for (int id = 0; id < MaxControlSources; id++)
    _status[id]->clientCheck();
}


void MonitorGui::startTimer()
{
  if (!_statusTimer)
  {
    _statusTimer = 
      XtAppAddTimeOut(theApplication->appContext(), 1000,
		      &MonitorGui::timerCallback, (XtPointer )this);  
  }
}


Void MonitorGui::timerCallback(XtPointer clientData, XtIntervalId *id)
{
  MonitorGui *obj = (MonitorGui *)clientData;

  obj->update();
  obj->_statusTimer = NULL;
  obj->startTimer();
}


MonitorWindow::MonitorWindow(const char *name) :
  VkSimpleWindow(name)
{
  
  _gui = new MonitorGui("monitorGui", mainWindowWidget());

  _gui->show();

  addView(_gui);
}


MonitorWindow::~MonitorWindow ()
{
  // Empty
}



int main ( int argc, char **argv )
{
  DmGuiApp *app = new DmGuiApp("IviewMonitor", &argc, argv);
	  
  MonitorWindow *window = new MonitorWindow("monitorWindow");
	  
  window->show();

  app->startTelemetryHandlers();
  
  app->run();
}
