/* -------------------------------------------------------------------------- */
/* -                   Astronomical CCD Camera Control                      - */
/* -                         Motif User Interface                           - */
/* -                         INDI Client for XmCCD                          - */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* Copyright (c) 2004-2012 John Kielkopf                                      */
/* kielkopf@louisville.edu                                                    */
/*                                                                            */
/* This file is part of XmCCD.                                                */
/*                                                                            */
/* Distributed under the terms of the General Public License (see LICENSE)    */ 
/*                                                                            */
/* Date: April 16, 2012                                                       */
/* Version: 4.0                                                               */
  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <netinet/in.h>
#include <netdb.h>

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/ToggleB.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h> 
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>

#include <xpa.h>
#include "lilxml.h"
#include "indiapi.h"
#include "xmccd.h"

  

/* User interface */

XtAppContext context;
XmStringCharSet char_set=XmSTRING_DEFAULT_CHARSET;

Widget menu_bar;
Widget file_menu;
Widget file_script_item;
Widget file_exit_item;

Widget set_menu;
Widget set_image_exposure_item;
Widget set_image_number_item;
Widget set_frame_count_multi_item;
Widget set_region_item;
Widget set_ccd_temperature_item;

Widget fits_menu;
Widget fits_name_item;
Widget fits_telescope_item;
Widget fits_instrument_item;
Widget fits_target_item;

Widget filter_menu;
Widget filter_item[10];

Widget mode_menu;
Widget mode_single_item;
Widget mode_focus_item;
Widget mode_multi_item;

Widget type_menu;
Widget type_light_item;
Widget type_dark_item;
Widget type_bias_item;
Widget type_flat_item;

Widget processing_menu;
Widget processing_transfer_toggle;
Widget processing_scripting_toggle;

Widget toplevel;



Widget file_script_dialog;
Widget set_image_exposure_dialog;
Widget set_image_number_dialog;
Widget set_frame_count_multi_dialog;
Widget set_region_dialog;
Widget set_ccd_temperature_dialog;
Widget fits_name_dialog;
Widget fits_target_dialog;
Widget fits_telescope_dialog;
Widget fits_instrument_dialog;

Widget button[3][3];
Widget command[3];
Widget label_info[6];
Widget show_info[6];
Widget form;
Widget sep_btns;
Widget sep_status;

Widget make_menu();              /* creates a menu on the menu bar */
Widget make_menu_item();         /* adds an item into the menu */
Widget make_menu_item_toggle();  /* adds a toggle button item into the menu */

/* labels for the buttons top to bottom then left to right */

static char btn_name[][6] = {
  "NoOp", 
  "East",
  "NoOp",
  "North",
  "NoOp",
  "South",
  "NoOp",
  "West",
  "NoOp"
};

static char cmd_name[][30] = {
  "Expose",
  "Interrupt",
  "Region"
};


static char infotext[][24] = {
  "Region",
  "Exposure",
  "Temperature",
  "File name",
  "Filter",
  "Status"
};

static char infodata[][24] = {
  "-",
  "-",
  "-",
  "-",
  "-",
  "Camera not ready"
};


/* Array of dynamic labels for the filters */ 

static char filter_label[10][32] = {
  " (1)              ",
  " (2)              ",
  " (3)              ",
  " (4)              ",
  " (5)              ",
  " (6)              ",  
  " (7)              ",  
  " (8)              ",
  " (9)              ", 
  "(10)              "
 };


void create_menus(Widget menu_bar);      /* Create all menus            */
void menuCB();                           /* All pull-down menus         */
void file_script_dialogCB();             /* Get script file name        */

void set_image_exposure_dialogCB();      /* Exposure time dialog        */
void set_image_number_dialogCB();        /* Image number dialog         */
void set_frame_count_multi_dialogCB();   /* Multi frame count dialog    */
void set_region_dialogCB();              /* Region dialog               */
void set_ccd_temperature_dialogCB();     /* Temperature dialog          */
void fits_name_dialogCB();               /* Set base name dialog        */
void fits_target_dialogCB();             /* Set target dialog           */
void fits_telescope_dialogCB();          /* Set telescope dialog        */
void fits_instrument_dialogCB();         /* Set instrument dialog       */

void buttonCB();                         /* The paddle buttons          */
void commandCB();                        /* Command buttons             */

void create_menus(Widget menu_bar);      /* Creates all menus for this program  */
void menuCB();                           /* Callback for all pull-down menus    */

/* User interface polling */

unsigned long poll_interval = POLLMS;      
void poll_interval_handler(XtPointer client_data_ptr, XtIntervalId *client_id);            
XtIntervalId poll_interval_id;
XtPointer poll_interval_data_ptr;
XtInputId indi_id;

/* Parameter io */

void input_image_exposure();              /* Image exposure time     */
void input_image_number();               /* Image number            */
void input_frame_count_multi();          /* Multi frame count       */
void input_region();                     /* Image region            */
void input_ccd_temperature();            /* CCD temperature         */
void input_filter();                     /* Filter                  */

/* Parameter reporting */

void set_status_buffer(void);
void update_exposure_display();     /* Display image exposure time  */
void update_region_display();       /* Display image region         */
void update_temperature_display();  /* Display temperature control  */
void update_file_name_display();    /* Display last file name       */
void update_filter_display();       /* Display filter number        */
void update_filter_menu(int i);     /* Update drop down menu entry  */
void update_status_display();       /* Display status buffer string */
void update_all_displays();         /* Display all buffers          */

/* Interface with ds9 */

void start_ds9();                    /* Initiate ds9 display              */
void update_ds9_image(char *name);   /* XPA to ds9:  read file name       */
int  get_ds9_region();               /* xpa from ds9: send me the region  */

/* Buffers */

char buf[256];                   /* General purpose character buffer */
char status_buf[24];             /* Short buffer for status string */

/* Indi functions */

void indiCB (XtPointer client, int *fdp, XtInputId *idp);

static int launch_indi_command (XMLEle *root, char errmsg[]);
static int set_indi_number (XMLEle *root, char errmsg[]);
static int set_indi_text (XMLEle *root, char errmsg[]);
static int set_indi_switch (XMLEle *root, char errmsg[]);
static int set_indi_light (XMLEle *root, char errmsg[]);
static int set_indi_blob (XMLEle *root, char errmsg[]);
static int set_indi_message (XMLEle *root, char errmsg[]);

int send_indi_number (FILE *fp, char *dev, char *prm, double value);
int send_indi_text (FILE *fp, char *dev, char *prm, char *txt);
int send_indi_switch (FILE *fp, char *dev, char *prm, int state);

FILE *indi_fp;
int indi_socket;

LilXML *lillp;

char host_def[] = "localhost";   /* Default host name */
char *host = host_def;           /* Working host name */

int port = INDIPORT;		 /* Working port number */

int connect_indi(void);
int disconnect_indi(void);

/* Internal flags and counters */

int  diagnostics = DIAGNOSTICS;
int  acquire = SINGLE;
int  frame = LIGHT;
int  sequence = 0;
int  frame_count_multi = FRAMECOUNTMULTI;
int  transfer = NOCOPY;
int  image_number = 1;
int  scripting = FALSE;
char base_name[64]; 
char file_name[64];
char filter_name[24];

/* Fits header strings available through INDI */

char fits_target[64];
char fits_telescope[64];
char fits_instrument[64];
char ccd_script[64];

/* Camera variables */

unsigned short *image_data;
char   camera_device[64];
char   camera_set_prm[1024];

int    image_w = 1024;
int    image_h = 1024;
double image_exposure = 1.0;
int    region;
int    region_x;
int    region_y; 
int    region_w;
int    region_h;

/* Temperature control */

double setpoint;
double temperature;  
double cooler_power;  
int    cool;

/* Filter variables */

int    filter_max = FILTER_MAX;
int    filter_max_gui = FILTER_MAX;   
int    filter = FILTER_START;
int    filter_wheel;

/* GUI main starts here */

int main(int argc, char *argv[])
{
  Arg al[10];
  int ac;
  unsigned long x,y,btn_number,cmd_number;
  int info_number;
  int startup;
  
  EventMask mask;

  sprintf(camera_device,"ccd");
    
  /* Make contact with the server */

  startup = connect_indi();
  if (startup == FALSE)
  {
    fprintf(stderr,"INDI server is not available\n");
    exit(1);
  }
  
  /* Create parser context */
  
  if (lillp)
  {
    delLilXML (lillp);
  }
  lillp = newLilXML();  
    
  /* Start ds9 for image display */
  
  start_ds9();
   
  /* Create the toplevel shell */

  toplevel = XtAppInitialize(&context,"",NULL,0,&argc,argv,
      NULL,NULL,0);
  
  /* Set the default size of the window */ 

  ac=0;
  XtSetArg(al[ac],XmNwidth,400); ac++;
  XtSetArg(al[ac],XmNheight,700); ac++;
  XtSetValues(toplevel,al,ac);

  /* Create a form widget */

  ac=0;
  form=XmCreateForm(toplevel,"form",al,ac);
  XtManageChild(form);


  /* Create the menu bar and attach it to the form */
  
  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_FORM); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++;
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;
  menu_bar=XmCreateMenuBar(form,"menu_bar",al,ac);
  XtManageChild(menu_bar);
  
  /* Locations of buttons are in percent of window dimension */

  /* Set up the handpaddle buttons, add event handlers, attach to form */
  /* There are 9 places but only 4 are used */
  
  for (x=0; x<3; x++)
  {
    for (y=0; y<3; y++)
    {
      btn_number=3*x+y;
      if ((btn_number==1) | (btn_number==3) | (btn_number==5) | (btn_number==7) ) 
      {
        ac=0;
        XtSetArg(al[ac],XmNlabelString,
            XmStringCreate(btn_name[btn_number],char_set)); ac++;
        XtSetArg(al[ac],XmNleftAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNleftPosition,27+x*16); ac++;
        XtSetArg(al[ac],XmNrightAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNrightPosition,41+x*16); ac++;
        XtSetArg(al[ac],XmNtopAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNtopPosition,9+y*9); ac++;
        XtSetArg(al[ac],XmNbottomAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNbottomPosition, 17+y*9); ac++;
        button[x][y]=XmCreatePushButton(form,"label",al,ac);
        mask = ButtonReleaseMask | ButtonPressMask;
        XtAddEventHandler(button[x][y],mask,False,
            buttonCB,(XtPointer) btn_number);              
        XtManageChild(button[x][y]);
      }
    }
  }

  /* Create a separator widget and attach it to the form */

  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNtopPosition,42); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++; 
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;  
  XtSetArg(al[ac],XmNbottomAttachment,XmATTACH_NONE); ac++;  
  sep_btns=XmCreateSeparator(form,"sep",al,ac);
  XtManageChild(sep_btns);   
 
  /* Set up the information label widgets and attach them to the form */

  for  (x=0; x<2; x++)
  {
    for (y=0; y<3; y++)
    {
      info_number=2*y+x;
      ac=0;
      XtSetArg(al[ac],XmNlabelString,
        XmStringCreate(infotext[info_number],char_set)); ac++;
      XtSetArg(al[ac],XmNleftAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNleftPosition,5+x*50); ac++;
      XtSetArg(al[ac],XmNrightAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNrightPosition,45+x*50); ac++;
      XtSetArg(al[ac],XmNtopAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNtopPosition,47+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,49+y*6); ac++;
      label_info[info_number]=XmCreateLabel(form,"label",al,ac);
      XtManageChild(label_info[info_number]); 
    }
  }

  /* Set up the information data widgets and attach them to the form */

  for  (x=0; x<2; x++)
  {
    for (y=0; y<3; y++)
    {
      info_number=2*y+x;
      ac=0;
      XtSetArg(al[ac],XmNlabelString,
        XmStringCreate(infodata[info_number],char_set)); ac++;
      XtSetArg(al[ac],XmNleftAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNleftPosition,5+x*50); ac++;
      XtSetArg(al[ac],XmNrightAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNrightPosition,45+x*50); ac++;
      XtSetArg(al[ac],XmNtopAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNtopPosition,49+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,51+y*6); ac++;
      show_info[info_number]=XmCreateLabel(form,"label",al,ac);
      XtManageChild(show_info[info_number]); 
    }
  }

  /* Create a separator widget and attach it to the form */

  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNtopPosition,70); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++; 
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;  
  XtSetArg(al[ac],XmNbottomAttachment,XmATTACH_NONE); ac++;      
  sep_status=XmCreateSeparator(form,"sep",al,ac);
  XtManageChild(sep_status);   

  /* Set up the 3 command buttons and attach them to the form */

  for (y=0; y<3; y++)
  {
    cmd_number=y;
    ac=0;
    XtSetArg(al[ac],XmNlabelString,
      XmStringCreate(cmd_name[cmd_number],char_set)); ac++;
    XtSetArg(al[ac],XmNleftAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNleftPosition,20); ac++;
    XtSetArg(al[ac],XmNrightAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNrightPosition,80); ac++;
    XtSetArg(al[ac],XmNtopAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNtopPosition,74+y*8); ac++;
    XtSetArg(al[ac],XmNbottomAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNbottomPosition, 80+y*8); ac++;
    command[y]=XmCreatePushButton(form,"label",al,ac);
    XtManageChild(command[y]);
    XtAddCallback(command[y],XmNactivateCallback,
      commandCB,(XtPointer) cmd_number);
  }

  /* Create prompt dialogs and leave them unmanaged until needed */


  
  /* Set script file name */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter remote script file name: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Remote script file name selection",char_set)); ac++;
  file_script_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(file_script_dialog, XmNokCallback, 
    file_script_dialogCB, (int *) OK);
  XtAddCallback(file_script_dialog, XmNcancelCallback, 
    file_script_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (file_script_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Image exposure */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter time (s): ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Exposure",char_set)); ac++;
  set_image_exposure_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_image_exposure_dialog, XmNokCallback, set_image_exposure_dialogCB, (int *) OK);
  XtAddCallback(set_image_exposure_dialog, XmNcancelCallback, set_image_exposure_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_image_exposure_dialog, XmDIALOG_HELP_BUTTON));
 
  /* Image number */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter new image number: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Number",char_set)); ac++;
  set_image_number_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_image_number_dialog, XmNokCallback, set_image_number_dialogCB, (int *) OK);
  XtAddCallback(set_image_number_dialog, XmNcancelCallback, set_image_number_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_image_number_dialog, XmDIALOG_HELP_BUTTON));

  /* Frame count multi */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter maximum multiframe count: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Multiframe Maximum",char_set)); ac++;
  set_frame_count_multi_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_frame_count_multi_dialog, XmNokCallback, set_frame_count_multi_dialogCB, (int *) OK);
  XtAddCallback(set_frame_count_multi_dialog, XmNcancelCallback, set_frame_count_multi_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_frame_count_multi_dialog, XmDIALOG_HELP_BUTTON));
  
  /* Image region */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter image x,y,w,h : ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Region",char_set)); ac++;
  XtSetArg(al[ac], XmNokLabelString,
    XmStringCreateLtoR("On",char_set)); ac++;
  XtSetArg(al[ac], XmNhelpLabelString,
    XmStringCreateLtoR("Off",char_set)); ac++;
  set_region_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_region_dialog, XmNokCallback, set_region_dialogCB, (int *) REGION_ON);
  XtAddCallback(set_region_dialog, XmNcancelCallback, set_region_dialogCB, (int *) CANCEL);
  XtAddCallback(set_region_dialog, XmNhelpCallback, set_region_dialogCB, (int *) REGION_OFF);

  /* Temperature */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter temperature (C): ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("CCD Temperature Setpoint",char_set)); ac++;
  XtSetArg(al[ac], XmNokLabelString,
    XmStringCreateLtoR("On",char_set)); ac++;
  XtSetArg(al[ac], XmNhelpLabelString,
    XmStringCreateLtoR("Off",char_set)); ac++;
  set_ccd_temperature_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_ccd_temperature_dialog, XmNokCallback, set_ccd_temperature_dialogCB, (int *) COOL_ON);
  XtAddCallback(set_ccd_temperature_dialog, XmNcancelCallback, set_ccd_temperature_dialogCB, (int *) CANCEL);
  XtAddCallback(set_ccd_temperature_dialog, XmNhelpCallback, set_ccd_temperature_dialogCB, (int *) COOL_OFF);

  /* Set base image file name */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Base for image file names: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image file name entry",char_set)); ac++;
  fits_name_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_name_dialog, XmNokCallback, fits_name_dialogCB, 
    (int *) OK);
  XtAddCallback(fits_name_dialog, XmNcancelCallback, fits_name_dialogCB, 
    (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_name_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set target */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header TARGET: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header target entry",char_set)); ac++;
  fits_target_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_target_dialog, XmNokCallback, fits_target_dialogCB, 
    (int *) OK);
  XtAddCallback(fits_target_dialog, XmNcancelCallback, 
    fits_target_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_target_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set telescope */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header TELESCOPE: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header telescope entry",char_set)); ac++;
  fits_telescope_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_telescope_dialog, XmNokCallback, 
    fits_telescope_dialogCB, (int *) OK);
  XtAddCallback(fits_telescope_dialog, XmNcancelCallback, 
    fits_telescope_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_telescope_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set instrument */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header INSTRUMENT: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header instrument entry",char_set)); ac++;
  fits_instrument_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_instrument_dialog, XmNokCallback, 
    fits_instrument_dialogCB, (int *) OK);
  XtAddCallback(fits_instrument_dialog, XmNcancelCallback, 
    fits_instrument_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_instrument_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Create the menubar */

  create_menus(menu_bar); 
    
  /* Add the server as input to the user interface */
  
  indi_id = XtAppAddInput (context, indi_socket, (XtPointer)XtInputReadMask,
    indiCB, NULL);
    
  /* Request INDI information */
  
  fprintf (indi_fp, "<getProperties version='%g'/>\n", INDIV);    
  
  /* Update the displays with available data */
  
  update_all_displays();  

  /* Disable menu items not used because of hardware limitations */

  if ( filter_max <= 1 )
  {
    XtSetSensitive(filter_menu,False);
  }   

  /* Service initial contact with INDI server to set display parameters */

  poll_interval_id = XtAppAddTimeOut(context,
    poll_interval,poll_interval_handler,poll_interval_data_ptr);  

    
  /* Start the user interface */
  
  XtRealizeWidget(toplevel);
  XtAppMainLoop(context);

  exit(0);
}


/* Add an item into a menu in the menubar */   

Widget make_menu_item(char *item_name, unsigned long client_data, Widget menu)
{
  int ac;
  Arg al[10];
  Widget item;

  ac = 0;
  XtSetArg(al[ac], XmNlabelString,
    XmStringCreateLtoR(item_name,char_set)); ac++;      
  item=XmCreatePushButton(menu,item_name,al,ac);
  XtManageChild(item);
  XtAddCallback (item,XmNactivateCallback,menuCB,(int *) client_data);      
  XtSetSensitive(item,True);
  return(item);
}


/* Add a toggle item into a menu in the menubar */ 

Widget make_menu_item_toggle(char *item_name, unsigned long client_data, Widget menu)
{
  int ac;
  Arg al[10];
  Widget item;

  ac = 0;
  XtSetArg(al[ac], XmNlabelString,
      XmStringCreateLtoR(item_name,char_set)); ac++; 
  item=XmCreateToggleButton(menu,item_name,al,ac);
  XtManageChild(item);
  XtAddCallback (item,XmNvalueChangedCallback,menuCB,(XtPointer) client_data);      
  XtSetSensitive(item,True);
  return(item);
}

/* Create a menu on the menu bar */

Widget make_menu(char *menu_name, Widget menu_bar)
{
  int ac;
  Arg al[10];
  Widget menu, cascade;

  menu=XmCreatePulldownMenu(menu_bar,menu_name,NULL,0);
  ac=0;
  XtSetArg(al[ac],XmNsubMenuId,menu);  ac++;
  XtSetArg(al[ac],XmNlabelString,
    XmStringCreateLtoR(menu_name,char_set)); ac++;
  cascade=XmCreateCascadeButton(menu_bar,menu_name,al,ac);      
  XtManageChild (cascade); 
  return(menu);
}


/* Create all the menubar entries organized into submenus*/

void create_menus(Widget menu_bar)
{
  int ac;
  Arg al[10];
  int i;
     
  /* Create the file menu */
  file_menu = make_menu("File",menu_bar);
  file_script_item = make_menu_item("Script",FILESCRIPT,file_menu);
  file_exit_item = make_menu_item("Exit",FILEEXIT,file_menu);
  
  /* Create the set menu */
  set_menu = make_menu("Set",menu_bar);
  set_image_exposure_item = make_menu_item("Image exposure",SETIMAGEEXPOSURE,set_menu);
  set_image_number_item = make_menu_item("Image number",SETIMAGENUMBER,set_menu);
  set_frame_count_multi_item = make_menu_item("Multiframe count",SETFRAMECOUNTMULTI,set_menu);
  set_region_item = make_menu_item("Image region",SETREGION,set_menu);
  set_ccd_temperature_item = make_menu_item("CCD temperature",SETCCDTEMPERATURE,set_menu);

  fits_menu = make_menu("FITS",menu_bar);
  fits_name_item = make_menu_item("Base name",FITSNAME,fits_menu);
  fits_target_item = make_menu_item("Target",FITSTARGET,fits_menu);
  fits_telescope_item = make_menu_item("Telescope",FITSTELESCOPE,fits_menu);
  fits_instrument_item = make_menu_item("Instrument",FITSINSTRUMENT,fits_menu);
  
  /* Create the filter menu */
  filter_menu = make_menu("Filter",menu_bar);
  for ( i=0; i<filter_max; i++ )
  {
    filter_item[i] = make_menu_item_toggle(filter_label[i],FILTER1+i,filter_menu);
  }
   
  /* Add radio behavior to the filter menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(filter_menu,al,ac);

  /* Create a toggle button mode menu */
  mode_menu = make_menu("Mode",menu_bar);
  mode_single_item = make_menu_item_toggle("Single",MODESINGLE,mode_menu);
  mode_focus_item = make_menu_item_toggle("Focus",MODEFOCUS,mode_menu);
  mode_multi_item  = make_menu_item_toggle("Multi",MODEMULTI,mode_menu);
     
  /* Add radio behavior to the mode menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(mode_menu,al,ac);
  
  /* Set an initial mode_menu button state */
  XmToggleButtonSetState(mode_single_item, True, False);
           
  /* Create a toggle button image type menu */
  type_menu = make_menu("Type",menu_bar);
  type_light_item = make_menu_item_toggle("Light",TYPELIGHT,type_menu);
  type_dark_item = make_menu_item_toggle("Dark",TYPEDARK,type_menu);
  type_bias_item = make_menu_item_toggle("Bias",TYPEBIAS,type_menu);
  type_flat_item = make_menu_item_toggle("Flat",TYPEFLAT,type_menu);
  
       
  /* Add radio behavior to the type menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(type_menu,al,ac);
  
  /* Set the initial type_menu button */
  XmToggleButtonSetState(type_light_item, True, False);
  
  /* Create the processing menu */
  processing_menu = make_menu("Processing", menu_bar);
  processing_transfer_toggle = make_menu_item_toggle("Transfer", PROCTRANSFER, processing_menu);
  processing_scripting_toggle = make_menu_item_toggle("Scripting", PROCSCRIPTING, processing_menu);
  
}
/* Callback on menubar action sorts requests based on client_data */

void menuCB(Widget w, int client_data, XmAnyCallbackStruct *call_data)
{
  int i;

  if (client_data==FILEEXIT)   
  {
    free(image_data);
    exit(0);
  }
 
  if (client_data==FILESCRIPT)   
  {
    XtManageChild(file_script_dialog);
  }

  
  if (client_data==SETIMAGEEXPOSURE)
  {
    XtManageChild(set_image_exposure_dialog);
  }
    
  if (client_data==SETIMAGENUMBER) 
  {
    XtManageChild(set_image_number_dialog);
  }   
  
  if (client_data==SETFRAMECOUNTMULTI) 
  {
    XtManageChild(set_frame_count_multi_dialog);
  }  
  
  
  if (client_data==SETREGION)
  {
    XtManageChild(set_region_dialog);
  }
  
  if (client_data==SETCCDTEMPERATURE) 
  {
    XtManageChild(set_ccd_temperature_dialog);
  }

  if (client_data==FITSNAME)
  {
    XtManageChild(fits_name_dialog);
  } 

  if (client_data==FITSTARGET)
  {
    XtManageChild(fits_target_dialog);
  } 

  if (client_data==FITSTELESCOPE)
  {
    XtManageChild(fits_telescope_dialog);
  }
  
  if (client_data==FITSINSTRUMENT)
  {
    XtManageChild(fits_instrument_dialog);
  }

  for (i=0; i<filter_max; i++)
  {
    if ( ( client_data == (FILTER1 + i)) && (filter_max > i) )
    {
      if( XmToggleButtonGetState(filter_item[i]) )
      {
        filter = i + 1;
        sprintf(camera_set_prm,"filter.selection");
        send_indi_number (indi_fp, camera_device, camera_set_prm, 
          (double) filter); 
      }
    }
  }

  if (client_data==MODESINGLE)
  {
    if( XmToggleButtonGetState(mode_single_item) )
    {
      acquire = SINGLE;
      sprintf(camera_set_prm,"acquire.single");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }
  
  if (client_data==MODEFOCUS)
  {
    if( XmToggleButtonGetState(mode_focus_item) )
    {
      acquire = FOCUS;
      sprintf(camera_set_prm,"acquire.focus");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }
      
  if (client_data==MODEMULTI)
  {
    if( XmToggleButtonGetState(mode_multi_item) )
    {
      acquire = MULTI;
      sprintf(camera_set_prm,"acquire.multi");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }
  
  if (client_data==TYPELIGHT)
  {
    if( XmToggleButtonGetState(type_light_item) )
    {
      frame = LIGHT;
      sprintf(camera_set_prm,"frame.light");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }
  if (client_data==TYPEDARK)
  {
    if( XmToggleButtonGetState(type_dark_item) )
    {
      frame = DARK;
      sprintf(camera_set_prm,"frame.dark");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }
  if (client_data==TYPEBIAS)
  {
    if( XmToggleButtonGetState(type_bias_item) )
    {
      frame = BIAS;
      sprintf(camera_set_prm,"frame.bias");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }  
  if (client_data==TYPEFLAT)
  {
    if( XmToggleButtonGetState(type_flat_item) )
    {
      frame = FLAT;
      sprintf(camera_set_prm,"frame.flat");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
  }  
  if (client_data==PROCTRANSFER)
  { 
    if( XmToggleButtonGetState(processing_transfer_toggle) )
    {
      transfer = TRUE;
      sprintf(camera_set_prm,"image_transfer.network");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      sprintf(camera_set_prm,"image_transfer.none");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
      sprintf(camera_set_prm,"image_transfer.indi");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
    }
    else
    {
      transfer = FALSE;
      sprintf(camera_set_prm,"image_transfer.network");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);      
      sprintf(camera_set_prm,"image_transfer.none");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      sprintf(camera_set_prm,"image_transfer.indi");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
    }
  }
  if (client_data==PROCSCRIPTING)
  { 
    if( XmToggleButtonGetState(processing_scripting_toggle) )
    {
      scripting = TRUE;
      sprintf(camera_set_prm,"script.run");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
    }
    else
    {
      scripting = FALSE;
      sprintf(camera_set_prm,"script.run");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);      
    }      
  } 
    
  /* Take no action on cancel */
  
  if (client_data==CANCEL); 

}


/* Dialog for user input of script file name */

void file_script_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(ccd_script,s,64);
      ccd_script[63]='\0';
      XtFree(s);
      sprintf(camera_set_prm,"script.ccd_script");
      send_indi_text (indi_fp, camera_device, camera_set_prm, ccd_script);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}
  
  

/* Dialog for user input of file base name */

void fits_name_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(base_name,s,17);
      base_name[16]='\0';
      XtFree(s);
      sprintf(camera_set_prm,"archive.base_name");
      send_indi_text (indi_fp, camera_device, camera_set_prm, base_name);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}


/* Dialog for fits target entry */

void fits_target_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_target,s,17);
      fits_target[16]='\0';
      XtFree(s);
      sprintf(camera_set_prm,"target.fits_target");
      send_indi_text (indi_fp, camera_device, camera_set_prm, fits_target);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for fits telescope entry */

void fits_telescope_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_telescope,s,17);
      fits_telescope[16]='\0';
      XtFree(s);
      sprintf(camera_set_prm,"telescope.fits_telescope");
      send_indi_text (indi_fp, camera_device, camera_set_prm, fits_telescope);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of fits file header instrument entry */

void fits_instrument_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_instrument,s,17);
      fits_instrument[16] = '\0';
      XtFree(s);
      sprintf(camera_set_prm,"instrument.fits_instrument");
      send_indi_text (indi_fp, camera_device, camera_set_prm, fits_instrument);            
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}


/* Dialog for user input of image exposure time */

void set_image_exposure_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      image_exposure = atof(s);
      if ( image_exposure < MINEXP )
      {
        image_exposure = MINEXP;
      }
      if ( image_exposure > MAXEXP )
      {
        image_exposure = MAXEXP;
      }
      sprintf(camera_set_prm,"image_exposure.time");
      send_indi_number (indi_fp, camera_device, camera_set_prm, image_exposure);
      update_exposure_display();
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);

}

/* Dialog for user input of image number */
/* Should add warnings here about possibly overwriting existing files */
/* This allows for changing number sequence during a run */

void set_image_number_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;
  int new_number;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      new_number = (int) atof(s);
      if ( new_number < 2 )
      {
        new_number = 1;
      }
      image_number = new_number;
      sprintf(camera_set_prm,"image.number");
      send_indi_number (indi_fp, camera_device, camera_set_prm, image_number);
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of frame count multi */

void set_frame_count_multi_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct *call_data;
{    
  char *s;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      frame_count_multi = (int) atof(s);
      if ( frame_count_multi < 2 )
      {
        frame_count_multi = 2;
      }
      sprintf(camera_set_prm,"frame.multi");
      send_indi_number (indi_fp, camera_device, camera_set_prm, frame_count_multi);
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}


/* Dialog for user input of image region */

void set_region_dialogCB(w,client_data,call_data)
    Widget w;
    int client_data;
    XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  int in_x, in_y, in_w, in_h;

  switch (client_data)
  {
    case REGION_ON:

      /* Set the switch on */
      
      region = TRUE;
      sprintf(camera_set_prm,"image_region_control.option");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE); 
      XmStringGetLtoR(call_data->value,char_set,&s);
      if (sscanf(s, "%d,%d,%d,%d", &in_x, &in_y, &in_w, &in_h) == 4)
      {
        
        /* Test and clip subimage boundaries */
        
        if (in_x < 1)
        {
          in_x = 1;
        }
        if (in_x > image_w )
        {
          in_x = image_w;
        }       
        if (in_y < 1)
        {
          in_y = 1;
        }
        if (in_y > image_h)
        {
          in_y = image_h;
        }       
        if (in_w < 1)
        {
          in_w = 1;
        }
        if (in_w > image_w)
        {
          in_w = image_w;
        }      
        if (in_h < 1)
        {
          in_h = 1;
        }
        if (in_h > image_h)
        {
          in_h = image_h;
        }      
        if (in_x + in_w > image_w + 1)
        {
          in_w = image_w - in_x + 1;
        }
        if (in_y + in_h > image_h + 1)
        {
          in_h = image_h - in_y + 1;
        }           
        region_x = in_x;
        region_y = in_y;
        region_w = in_w;
        region_h = in_h;
        
        sprintf(camera_set_prm,"image_region.x");
        send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_x);
        sprintf(camera_set_prm,"image_region.y");
        send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_y);
        sprintf(camera_set_prm,"image_region.w");
        send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_w);
        sprintf(camera_set_prm,"image_region.h");
        send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_h);                       
      }
      XtFree(s); 
      break;

    case REGION_OFF:
      
      /* Set the switch off */
      
      region = FALSE;
      sprintf(camera_set_prm,"image_region_control.option");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE); 

      region_x = 1;
      region_y = 1;
      region_w = image_w;
      region_h = image_h;
      
      sprintf(camera_set_prm,"image_region.x");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_x);
      sprintf(camera_set_prm,"image_region.y");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_y);
      sprintf(camera_set_prm,"image_region.w");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_w);
      sprintf(camera_set_prm,"image_region.h");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_h);                       

      break;  

    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of ccd temperature setpoint */

void set_ccd_temperature_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;

  switch (client_data)
  {
    case COOL_ON:
      XmStringGetLtoR(call_data->value,char_set,&s);
      setpoint=atof(s);
      XtFree(s); 
      cool = TRUE;        
      sprintf(camera_set_prm,"temperature_control.setting");
      send_indi_number (indi_fp, camera_device, camera_set_prm, setpoint);
      sprintf(camera_set_prm,"cooler.power");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;
    case COOL_OFF:
      cool = FALSE;
      sprintf(camera_set_prm,"cooler.power");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
      break;    
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}


/* Callback function for the radio buttons that select guide grid function */

/* Callback function for the 9 grid buttons */

void buttonCB(w,client_data,call_event)
  Widget w;
  int client_data;
  XEvent *call_event;
{
  int n;
  int et = call_event->type;
  int dn,up;
   
  /* identify the button number n = 3*x+y */
  n=client_data;

  /* identify the action */
  
  dn = et == ButtonPress;
  up = et == ButtonRelease;
      
  /* respond to the request */
  

  if(dn)
  {
    switch (n) 
    {
   
      /* NoOp */
      case 0:
      break;

      /* E */
      case 1: 
        sprintf(camera_set_prm,"relay.east");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;

      /* NoOp */
      case 2: 
      break;

      /* N */
      case 3: 
        sprintf(camera_set_prm,"relay.north");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;

      /* NoOp */
      case 4:
      break;

      /* S */
      case 5: 
        sprintf(camera_set_prm,"relay.south");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;

      /* NoOp */
      case 6:
      break;

      /* W */
      case 7:
        sprintf(camera_set_prm,"relay.west");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;

      /* NoOp */
      case 8: 
      break;    

    }
  }      
        
  if(up)
  {     
    /* wait for the telescope to respond before accepting another cycle */

    usleep(100000);
  }


}

/* Callback for the camera command buttons */

void commandCB(Widget w, int client_data, XmAnyCallbackStruct *call_data)
{
  
  int n, cmd_number, region_flag;

  /* Identify the button */
  cmd_number = client_data;

  /* Respond to the request */
  /* Test ccd state, set flags, execute request */
  n = cmd_number;

  switch (n) 
  {

    /* Expose */
    case 0:
      
      /* Ask the indiserver to start the sequence */
      sequence = 1;
      sprintf(camera_set_prm,"sequence.run");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
      break;
      
    /* Interrupt */
    case 1:

      /* Stop the sequence */
      sequence = 0;
      
      /* Ask the indiserver to stop the sequence */
      sprintf(camera_set_prm,"sequence.run");
      send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
      break;

    /* Capture region parameters from ds9 */
    case 2:
      region_flag = get_ds9_region();

      if ( region_flag >= 3 )
      {
        /* Test and clip boundaries */
        if (region_x < 1)
        {
          region_x = 1;
        }
        if (region_x > image_w )
        {
          region_x = image_w;
        }       
        if (region_y < 1)
        {
          region_y = 1;
        }
        if (region_y > image_h)
        {
          region_y = image_h;
        }       
        if (region_w < 1)
        {
          region_w = 1;
        }
        if (region_w > image_w)
        {
          region_w = image_w;
        }      
        if (region_h < 1)
        {
          region_h = 1;
        }
        if (region_h > image_h)
        {
          region_h = image_h;
        }      
        if (region_x + region_w > image_w + 1)
        {
          region_w = image_w - region_x + 1;
        }
        if (region_y + region_h > image_h + 1)
        {
          region_h = image_h - region_y + 1;
        }           
        region = TRUE;
        sprintf(camera_set_prm,"image_region_control.option");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, TRUE);
        
      }
      else
      {
        region = FALSE;
        sprintf(camera_set_prm,"image_region_control.option");
        send_indi_switch (indi_fp, camera_device, camera_set_prm, FALSE);
        
        region_x = 1;
        region_y = 1;
        region_w = image_w;
        region_h = image_h;
      }
      sprintf(camera_set_prm,"image_region.x");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_x);
      sprintf(camera_set_prm,"image_region.y");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_y);
      sprintf(camera_set_prm,"image_region.w");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_w);
      sprintf(camera_set_prm,"image_region.h");
      send_indi_number (indi_fp, camera_device, camera_set_prm, (double) region_h);                    
      update_all_displays();
      break;
  }
  update_all_displays();
}


 
/* Update imaging ccd exposure time in the window */

void update_exposure_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;

  sprintf(buf,"%4.3lf",image_exposure);
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[1],al,ac);   
  XmStringFree(xmstr);
}  
   

/* Update image region selection in the window */

void update_region_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;

  if ( region == TRUE )
  {
  sprintf(buf,"%d %d %d %d [On]",
    region_x, region_y, region_w, region_h);
  }
  else
  {
  sprintf(buf,"%d %d %d %d [Off]",
    region_x, region_y, region_w, region_h);
  }
  
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[0],al,ac);   
  XmStringFree(xmstr);
} 



/* Update ccd temperature in the window                  */
/* Values must have been previously read from the camera */

void update_temperature_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;
  if (cool == 1)
  {
    sprintf(buf,"%3.1lf [%3.1lf (On)]", temperature,setpoint);
  }
  else
  {
    sprintf(buf,"%3.1lf [%3.1lf (Off)]", temperature,setpoint); 
  }
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[2],al,ac);   
  XmStringFree(xmstr);
} 

/* Update the file name from INDI in the main window */

void update_file_name_display()
{
  XmString xmstr;
  Arg al[10];
  int ac; 
    
  xmstr = XmStringCreateLtoR(file_name,char_set);
    
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[3],al,ac);   
  XmStringFree(xmstr);
}       

/* Update filter data in the main window */

void update_filter_display()
{
  XmString xmstr;
  Arg al[10];
  int ac; 
  
  xmstr = XmStringCreateLtoR(filter_name,char_set);
  
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[4],al,ac);   
  XmStringFree(xmstr);
} 

/* Update filter name in the drop down menu */

void update_filter_menu(int i)
{
  XmString xmstr;
  Arg al[10];
  int ac; 

  sprintf(filter_label[i],"%s",filter_name);
  xmstr = XmStringCreateLtoR(filter_name,char_set);
  
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(filter_item[i],al,ac);   
  XmStringFree(xmstr);
} 

/* Update status in the window */

void update_status_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;
  set_status_buffer();
  xmstr = XmStringCreateLtoR(status_buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[5],al,ac);   
  XmStringFree(xmstr);
}   
 
/* Update all display buffers */

void update_all_displays()
{
  update_status_display();
  update_temperature_display();
  update_filter_display();
  update_exposure_display();
  update_region_display();
}   



/* User input of image ccd exposure time */

void input_image_exposure() 
{
  XtManageChild(set_image_exposure_dialog);
}

/* User input of multiframe count max */

void input_frame_count_multi() 
{
  XtManageChild(set_frame_count_multi_dialog);
}

/* User input of ccd temperature setpoint */

void input_setpoint() 
{
  XtManageChild(set_ccd_temperature_dialog);
}




/* Set panel status buffer based on information from the user interface */

void set_status_buffer(void)
{
  
  /* Set the display message */
     
  if (sequence == 1)
  {
    if(acquire == SINGLE)
    {
      if(frame == DARK)
      {
        sprintf(status_buf,"Single dark");
      }
      else
      {
        sprintf(status_buf,"Single");
      }  
    }
    else if(acquire == FOCUS)
    {
      if(frame == DARK)
      {
        sprintf(status_buf,"Focus dark");
      }
      else
      {
        sprintf(status_buf,"Focus");
      }               
    }
    else if(acquire == MULTI)
    {
      if(frame == DARK)
      {
        sprintf(status_buf,"Multiple dark");
      }
      else
      {
        sprintf(status_buf,"Multiple");
      } 
    }
    else 
    {
      sprintf(status_buf,"Image");
    }             
  }  
  else
  {
    sprintf(status_buf,"Camera ready");
  }
}

/* Talk to the camera server once for an update of its status */

void camera (void)
{
  static int inited;
    
  if (inited != TRUE)
  {
    inited = TRUE;
    
    /* Send the default image exposure time to the INDI server */

    sprintf(camera_set_prm,"image_exposure.time");
    send_indi_number (indi_fp, camera_device, camera_set_prm, image_exposure);
    
    /* The server will respond to this with updated camera parameters */

    update_all_displays();
  }
}

void poll_interval_handler(XtPointer client_data_ptr, XtIntervalId *client_id) 
{
    
  /* Adjust the camera interface appearance to use recent INDI parameters */ 
  
  camera();
            
  /* Start the timer again if needed by uncommenting the following.  */ 
  /* It would then  return to this handler after poll_interval. */

  /*
     poll_interval_id = XtAppAddTimeOut(context, poll_interval,
       poll_interval_handler, poll_interval_data_ptr);
  */
}  




/* Start ds9 as a new process */

void start_ds9(void)
{
  strcpy(buf, "ds9 &");
  system(buf);
}

       
/* Use xpa to read last region from ds9 and set region parameters */
/* Returns number of coordinates that have been read              */
/* A box will return 4 and a circle will return 3                 */
/* Coordinates are with respect to the lower left corner          */

int get_ds9_region(void)
{  
  #define NXPA 10
  int i, j, nresponses;
  int buffer_lengths[NXPA]={0};
  char *data_buffer[NXPA]; 
  char *server_names[NXPA];
  char *server_messages[NXPA];
  char *regionptr;
  double xc,yc,dx,dy;
  
  j=0;
  nresponses = XPAGet(NULL, "ds9", "regions", NULL, data_buffer, 
    buffer_lengths, server_names, server_messages, NXPA);
  
  for(i=0; i < nresponses; i++)
  {
    if( server_messages[i] == NULL )
    {
          
      /* Process data_buffer[i] contents */
    
      /* Format:                             */
      /*   circle(x_center,y_center,radius)  */
      /*   box(x_center,y_center,dx,dy)      */
      /*   relative to lower left            */ 
      
      regionptr=strrchr(data_buffer[i], '(' );
      if(regionptr!=NULL)
      {
        regionptr++;
        j=sscanf(regionptr,"%lf,%lf,%lf,%lf",
          &xc,&yc,&dx,&dy);
        if (j == 4)  
        {
          region_x = (int) (xc - dx/2);
          region_y = (int) (yc - dy/2);
          region_w = (int) (dx);
          region_h = (int) (dy);
        }
        if (j == 3)  
        {
          region_x = (int) (xc - dx);
          region_y = (int) (yc - dx);
          region_w = (int) (dx + dx);
          region_h = region_w;
        }        
      }           
    }
    else
    {
    
      /* Process error messages */
    
      fprintf(stderr,"%s (%s)\n", server_messages[i], server_names[i]);
    
    }
    if( server_names[i] )
    {  
      free( server_names[i] );
    }
    if( server_messages[i] )
    { 
     free( server_messages[i] );  
    }
  }
  return j;
}


/* Indi client utilities */

/* Connect to the server */
/* Return TRUE if connection established and FALSE otherwise */

int connect_indi(void)
{
  struct sockaddr_in serv_addr;
  struct hostent *hp;

  /* Lookup host address */
  hp = gethostbyname (host);

  /* Create a socket to the INDI server */
  (void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr =
    ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
  serv_addr.sin_port = htons(port);
  indi_socket = socket (AF_INET, SOCK_STREAM, 0);
  if (indi_socket < 0)
  {
    fprintf(stderr,"Unable to open server socket\n");
    return (0);
  }

  /* Connect to the server */
  connect (indi_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

  /* Open a file for writing to the server */
  indi_fp = fdopen (indi_socket, "w");
  
  if (indi_fp == NULL)
  {
    fprintf(stderr,"Unable to open server connections\n");
    return (0);    
  }
  
  return (1);
}


/* Disconnect the server */
/* Return server state TRUE or FALSE */

int disconnect_indi(void)
{
  
  /* Close socket and file pointer */
  if (indi_socket >= 0) 
  {
    fclose (indi_fp);     
    indi_fp = NULL;
    indi_socket = -1;
    return (0);
  }
  else
  {
    return (1);
  }    
}

 
/* Implement callback for an incoming INDI command */
/* Adapted from Elwood Downey's INDI code */

void indiCB (XtPointer client, int *fdp, XtInputId *idp)
{
  char ibuf[32];  
  char msg[1024];
  int i, nr;

  /* Read INDI command */

  nr = read (*fdp, ibuf, sizeof(ibuf));
  if (nr <= 0) 
  {
    if (nr < 0)
    {
      fprintf(stderr, "INDI input error\n");
    }
    else
    {
      fprintf(stderr, "INDI closed connection");
    }
      
    disconnect_indi();
    
    /* Clean up lilxml */
    if (lillp) 
    {
      delLilXML (lillp);
      lillp = NULL;
    }
    
    /* Remove input */
    if (indi_id) 
    {
      XtRemoveInput (indi_id);
      indi_id = 0;
    }
            
    return;
  }
     
  /* Process each character */
  
  for (i = 0; i < nr; i++) 
  {
    XMLEle *root = readXMLEle (lillp, (int)ibuf[i], msg);
    if (root) 
    {
            
      if (diagnostics == 1)
      {
        printf("New xml element\n");
        prXMLEle (stdout, root, 0); 
      }

      if (launch_indi_command (root, msg) < 0) 
      {
        fprintf (stderr, "INDI input error: %s\n", msg);
      } 
      delXMLEle (root);
    } 
    else if (msg[0]) 
    {
      fprintf (stderr, "INDI input: %s\n", msg);
    }
  }
}

/* Check incoming INDI tag and launch the command */
/* Returns 0 on success and -1 on error           */
/* Adapted from Elwood Downey's INDI code         */

int launch_indi_command (XMLEle *root, char *errmsg)
{
  static struct {
    char *cmdname;
     int (*fp)(XMLEle *root, char errmsg[]);
  } commands[] = {
      /* Must be sorted by cmdname */
      {"defBLOBVector",   set_indi_blob},
      {"defLightVector",  set_indi_light},
      {"defNumberVector", set_indi_number},
      {"defSwitchVector", set_indi_switch},
      {"defTextVector",   set_indi_text},
      {"message",         set_indi_message},
      {"newNumberVector", set_indi_number},
      {"newSwitchVector", set_indi_switch},
      {"newTextVector",   set_indi_text},
      {"setBLOBVector",   set_indi_blob},
      {"setLightVector",  set_indi_light},
      {"setNumberVector", set_indi_number},
      {"setSwitchVector", set_indi_switch},
      {"setTextVector",   set_indi_text},
  };

  char *tag = tagXMLEle(root);
  int u = XtNumber(commands)-1;
  int l = 0;
  int diff = -1;
  int m = -1;
  
  /* Binary search for known xml tag*/
  while (l <= u) 
  {
    m = (l+u)/2;
    diff = strcmp (tag, commands[m].cmdname);
    if (diff == 0)
    {
      break;
    }
    if (diff < 0)
    {
      u = m-1;
    }
    else
    {
      l = m+1;
    }
  }

  /* Launch the command for a valid tag*/
  if (diff == 0)
  {    
    ((*commands[m].fp) (root, errmsg));
    return (0);
  }
  
  /* An error generates a message and a flag */
  sprintf (errmsg, "Unrecognized INDI tag %s", tag);
  return (-1);
}

/* Set a new value for an indi number property */
static int set_indi_number (XMLEle *root, char errmsg[])
{
  XMLEle *ep;
  char *device;
  char *name;
  char *ep_name;

  device = findXMLAttValu (root, "device");
  if (strcmp(device,camera_device) != 0)
  {
    return (0);
  }
  
  name = findXMLAttValu (root, "name");
  if(strcmp(name,"image_exposure") == 0)
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      image_exposure = atof(pcdataXMLEle(ep));
      update_exposure_display();
    }
  }
  else if (strcmp(name,"temperature_control") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      setpoint = atof(pcdataXMLEle(ep));
      update_temperature_display();
    }
  }
  else if (strcmp(name,"camera_temperature") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      temperature = atof(pcdataXMLEle(ep));
      update_temperature_display();
    }
  }
  else if (strcmp(name,"ccd_system") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      ep_name = findXMLAttValu (ep, "name");
      if (strcmp(ep_name,"image_width") == 0)
      {
        image_w = atoi(pcdataXMLEle(ep));
        region_x = 1;
        region_w = image_w;
      }
      else if (strcmp(ep_name,"image_height") == 0)
      {
        image_h = atoi(pcdataXMLEle(ep));
        region_y = 1;
        region_h = image_h;
      }
      else if (strcmp(ep_name,"filter_max") == 0)
      {
        filter_max = atoi(pcdataXMLEle(ep));
      }                 
      if ( filter_max <= 1 )
      {
        XtSetSensitive(filter_menu,False);
      }
      update_region_display();
    }
  }  
  else if (strcmp(name,"image_region") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      ep_name = findXMLAttValu (ep, "name");
      if (strcmp(ep_name,"x") == 0)
      {
        region_x = atoi(pcdataXMLEle(ep));
      }
      else if (strcmp(ep_name,"y") == 0)
      {
        region_y = atoi(pcdataXMLEle(ep));
      }
      else if (strcmp(ep_name,"w") == 0)
      {
        region_w = atoi(pcdataXMLEle(ep));
      }
      else if (strcmp(ep_name,"h") == 0)
      {
        region_h = atoi(pcdataXMLEle(ep));
      }            
      update_region_display();
    }
  }
  else if (strcmp(name,"filter") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      filter = atoi(pcdataXMLEle(ep));
      update_filter_display();
      update_filter_menu(filter-1);      
    }
  }          
  else
  {  
    /*
    printf("set_indi_number %s\n",name);
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
       printf ("  %s\n", pcdataXMLEle(ep));
    */
  }  

  if (diagnostics == 2)
  {
    printf("filter = %d\n", filter);
    printf("image_region x y w h = %d %d %d %d \n",
      region_x, region_y, region_w, region_h);
    printf("temperature = %lf\n", temperature);
    printf("setpoint = %lf\n", setpoint);
    printf("image_exposure = %lf\n", image_exposure);   
  }
  return (0);
}

/* set a new value for an indi text property */
static int set_indi_text (XMLEle *root, char errmsg[])
{
  XMLEle *ep;
  char *name;
  name = findXMLAttValu (root, "name");
  
  if (strcmp(name,"filter") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(filter_name,pcdataXMLEle(ep));
      update_filter_display();
      update_filter_menu(filter-1);  
    }
  }
  else if (strcmp(name,"archive") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(base_name,pcdataXMLEle(ep));
    }
  }
  else if (strcmp(name,"file") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(file_name,pcdataXMLEle(ep));
      update_file_name_display();
    }
  }        
  else if (strcmp(name,"target") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(fits_target,pcdataXMLEle(ep));
    }
  }
  else if (strcmp(name,"telescope") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(fits_telescope,pcdataXMLEle(ep));
    }
  }
  else if (strcmp(name,"script") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      strcpy(ccd_script,pcdataXMLEle(ep));
    }
  }  
  
  return (0);
}

/* set a new value for an indi switch property */
static int set_indi_switch (XMLEle *root, char errmsg[])
{
  XMLEle *ep;
  char *device;
  char *name;
  char *ep_name;
  name = findXMLAttValu (root, "name");

  device = findXMLAttValu (root, "device");
  if (strcmp(device,camera_device) != 0)
  {
    return (0);
  }

  if (strcmp(name,"sequence") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      if (strcmp(pcdataXMLEle(ep),"Off") == 0)
      {
        sequence = 0;

        /* Enable expose button */
        XtSetSensitive(command[0],True);
      
        /* Enable menubar options */
        XtSetSensitive(set_menu,True);
        if ( filter_max > 1 )
        {
          XtSetSensitive(filter_menu,True);
        }
        XtSetSensitive(mode_menu,True);
        XtSetSensitive(type_menu,True);

        /* Update the exposure time displays */
        update_exposure_display();
        update_status_display();

      }
      else if (strcmp(pcdataXMLEle(ep),"On") == 0)
      {
        sequence = 1;
       
        /* Disable expose button */
        XtSetSensitive(command[0],False);
      
        /* Disable menubar options but allow guide change during sequence */
        XtSetSensitive(set_menu,False);
        XtSetSensitive(mode_menu,False);
        XtSetSensitive(type_menu,False);        
        
        /* Update the exposure time displays */
        update_exposure_display();
        update_status_display();

      }
    }
  }
  else if (strcmp(name,"script") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      if (strcmp(pcdataXMLEle(ep),"Off") == 0)
      {
        XmToggleButtonSetState(processing_scripting_toggle, False, False);
        scripting = FALSE;
      }
      else if (strcmp(pcdataXMLEle(ep),"On") == 0)
      {
        XmToggleButtonSetState(processing_scripting_toggle, True, False);
        scripting = TRUE;
      }
    }
  }   
  else if (strcmp(name,"cooler") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      if (strcmp(pcdataXMLEle(ep),"Off") == 0)
      {
        cool = 0;
        update_temperature_display();
      }
      else if (strcmp(pcdataXMLEle(ep),"On") == 0)
      {
        cool = 1;
        update_temperature_display();
      }
    }
  }  
  else if (strcmp(name,"image_region_control") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      if (strcmp(pcdataXMLEle(ep),"On") == 0)
      {
        region = TRUE;
      }
    }
  }  
  else if (strcmp(name,"frame") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      ep_name = findXMLAttValu (ep, "name");
      if (strcmp(ep_name,"dark") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          frame = DARK;
          XmToggleButtonSetState(type_dark_item, True, False);
          XmToggleButtonSetState(type_light_item, False, False);
        }
      }
      else if (strcmp(ep_name,"light") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          frame = LIGHT;
          XmToggleButtonSetState(type_dark_item, False, False);
          XmToggleButtonSetState(type_light_item, True, False);
        }
      }      
    }
  }
  else if (strcmp(name,"acquire") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      ep_name = findXMLAttValu (ep, "name");
      if (strcmp(ep_name,"single") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          acquire = SINGLE;
          XmToggleButtonSetState(mode_single_item, True, False);
          XmToggleButtonSetState(mode_focus_item, False, False);
          XmToggleButtonSetState(mode_multi_item, False, False);
        }
      }
      else if (strcmp(ep_name,"focus") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          acquire = FOCUS;
          XmToggleButtonSetState(mode_single_item, False, False);
          XmToggleButtonSetState(mode_focus_item, True, False);
          XmToggleButtonSetState(mode_multi_item, False, False);          
        }
      }
      else if (strcmp(ep_name,"multi") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          acquire = MULTI;
          XmToggleButtonSetState(mode_single_item, False, False);
          XmToggleButtonSetState(mode_focus_item, False, False);
          XmToggleButtonSetState(mode_multi_item, True, False);          
        }
      }           
    }
  }
  else if (strcmp(name,"image_transfer") == 0) 
  {
    for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    {
      ep_name = findXMLAttValu (ep, "name");
      if (strcmp(ep_name,"none") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          transfer = FALSE;
          XmToggleButtonSetState(processing_transfer_toggle, False, False);
        }
      }
      else if (strcmp(ep_name,"indi") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          transfer = FALSE;
          XmToggleButtonSetState(processing_transfer_toggle, False, False);
        }
      }
      else if (strcmp(ep_name,"network") == 0)
      {
        if (strcmp(pcdataXMLEle(ep),"On") == 0)
        {
          transfer = TRUE;
          XmToggleButtonSetState(processing_transfer_toggle, True, False);          
        }
      }     
    }
  }
  else
  {
    
    /* XmCCD received something unexpected */

  if (diagnostics >= 1)
    {
      printf("set_indi_switch %s\n",name);
      for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
        printf ("  %s\n", pcdataXMLEle(ep));
    }
  }

  /* More diagnostic messages */

  if (diagnostics == 2)
  {
    printf("acquire = %d\n", acquire);
    printf("frame = %d\n", frame);
    printf("sequence = %d\n", sequence);
    printf("transfer = %d\n", transfer);
    printf("scripting = %d\n", scripting);
    printf("region = %d\n", region);
  } 
   
  return (0);
}

/* set a new value for an indi light property */
static int set_indi_light (XMLEle *root, char errmsg[])
{
  XMLEle *ep;
  char *device;
  char *name;
  
  device = findXMLAttValu (root, "device");
  if (strcmp(device,camera_device) != 0)
  {
    return (0);
  }
  
  name = findXMLAttValu (root, "name");
  printf("set_indi_light %s\n",name);
  for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
    printf ("  %s\n", pcdataXMLEle(ep));
  return (0);
}

/* set a new value for an indi blob property */
static int set_indi_blob (XMLEle *root, char errmsg[])
{
  char *device;
  char *name;
  
  device = findXMLAttValu (root, "device");
  if (strcmp(device,camera_device) != 0)
  {
    return (0);
  }
  
  name = findXMLAttValu (root, "name");
  
  /* Blob images are not presently handled by xmccd2 */
    
  return (0);
}

/* display a message from the server */
static int set_indi_message (XMLEle *root, char errmsg[])
{
  printf("Indi message: %s\n",errmsg);
  return (0);
}


/* int send_indi_number (char *dev, char *prm, double value)             */

/* Derived from the command line programs get/setINDI.c by Elwood Downey */

/* Connects to an INDI server                                            */
/* Sends  property.elements=value to a device                            */
/* Returns (0) success,  (1) send error, (2)  trouble opening server     */
/* May fail without notice if the server is not running                  */

/* dev is the device                                                     */
/* prm is the p.e string                                                 */
/* value is the new value                                                */          


int send_indi_number (FILE *fp, char *dev, char *prm, double value)
{
  char  p[1024], e[2048];
  
  /* Parse parameter string to components */
  
  sscanf (prm, "%[^.].%[^.]",  p, e);
  
  /* Create and send an XML string                        */
  
  /* Sample format:                                       */
  /*                                                      */
  /* setINDI  ccd.ImageExposure.Seconds=2                 */
  /*                                                      */
  /* <newNumberVector device='ccd' name='ImageExposure'>  */
  /* <oneNumber name='Seconds'>'2'</oneNumber>            */
  /* </oneNumberVector>                                   */
  
  fprintf (fp, "<newNumberVector device='%s' name='%s'>\n",dev,p);
  fprintf (fp, "  <oneNumber name='%s'>%lf</oneNumber>\n",e,value);
  fprintf (fp, "</newNumberVector>\n");
  fflush (fp);
  if (feof(fp) || ferror(fp)) 
  {
    fprintf (stderr, "Send error\n");
    return 1;
  }
  return 0;
}

/* int send_indi_text (char *dev, char *prm, char *txt)                     */

/* Derived from the command line programs get/setINDI.c by Elwood Downey */

/* Connects to an INDI server                                            */
/* Sends  property.elements=value to a device                            */
/* Returns (0) success,  (1) send error, (2)  trouble opening server     */
/* May fail without notice if the server is not running                  */

/* dev is the device                                                     */
/* prm is the p.e string                                                 */
/* txt is the new string value                                           */


int send_indi_text (FILE *fp, char *dev, char *prm, char *txt)
{
  char  p[1024], e[2048];
  
  /* Parse parameter string to components */
  
  sscanf (prm, "%[^.].%[^.]",  p, e);
  
  /* Create and send an XML string                        */
  
  /* Sample format:                                       */
  /*                                                      */
  /* setINDI ccd.File.BaseName=newname                    */
  /*                                                      */
  /* <newTextVector device='ccd' name='File'>             */
  /* <oneText name='BaseName'>'newname'</oneText>         */
  /* </oneTextVector>                                     */
  
  fprintf (fp, "<newTextVector device='%s' name='%s'>\n",dev,p);
  fprintf (fp, "  <oneText name='%s'>%s</oneText>\n",e,txt);
  fprintf (fp, "</newTextVector>\n");
  fflush (fp);
  if (feof(fp) || ferror(fp)) 
  {
    fprintf (stderr, "Send error\n");
    return 1;
  }
  return 0;
}

/* int send_indi_switch (char *dev, char *prm, int state)                 */

/* Derived from the command line programs get/setINDI.c by Elwood Downey */

/* Connects to an INDI server                                            */
/* Sends  property.elements=value to a device                            */
/* Returns (0) success,  (1) send error, (2)  trouble opening server     */
/* May fail without notice if the server is not running                  */

/* dev is the device                                                     */
/* prm is the p.e string                                                 */
/* state is TRUE or FALSE for setting on or off                          */

int send_indi_switch (FILE *fp, char *dev, char *prm, int state)
{
  char  p[1024], e[2048];
  
  /* Parse parameter string to components */
  
  sscanf (prm, "%[^.].%[^.]",  p, e);
  
  /* Create and send an XML string                        */
  
  /* Sample format:                                       */
  /*                                                      */
  /* setINDI ccd.ImageBinning.2=On                        */
  /*                                                      */
  /* <newSwitchVector device='ccd' name='ImageBinning'>   */
  /* <oneSwitch name='2'>'On'</oneSwitch>                 */
  /* </oneSwitchVector>                                   */
  
  fprintf (fp, "<newSwitchVector device='%s' name='%s'>\n",dev,p);
  if (state == TRUE)
  {
    fprintf (fp, "  <oneSwitch name='%s'>On</oneSwitch>\n",e);
  }
  else
  {
    fprintf (fp, "  <oneSwitch name='%s'>Off</oneSwitch>\n",e);
  }
  fprintf (fp, "</newSwitchVector>\n");
  fflush (fp);
  if (feof(fp) || ferror(fp)) 
  {
    fprintf (stderr, "Send error\n");
    return 1;
  }
  return 0;
}

