/* -------------------------------------------------------------------------- */
/* -                   Astronomical CCD Camera Control                      - */
/* -                            XmTel CCD Server                            - */
/* -                                                                        - */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* Copyright (c) 2004-2021 John Kielkopf                                      */
/* kielkopf@louisville.edu                                                    */
/*                                                                            */
/* This file is part of XmCCD.                                                */
/*                                                                            */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* This program is based on stest.c distributed with xpa version 2.1.5        */
/* XPA and stest are                                                          */
/* Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory              */
/*                                                                            */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* Date: February 11, 2021                                                    */
/*   Version: 7.0                                                             */
/*                                                                            */
/* -------------------------------------------------------------------------- */

/* Dependencies from xmccd */  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>

#include "xpaccd.h"
#include "protocol.h"


/* Dependencies from xpa */

#define HAVE_STRING_H 1
#define HAVE_MALLOC_H 1
#define HAVE_STDLIB_H 1
#define HAVE_UNISTD_H 1
#define HAVE_SETJMP_H 1
#include "xpap.h"

#define MAX_FPS 10

extern char *optarg;
extern int optind;

XPA xpa1;
int  xpa_quiet=0;
int n=0;
size_t  save_bytes=-1;
char *save_buf=NULL;

char *mode="";
char name[SZ_LINE];
char xclass[SZ_LINE];


/* Files */

FILE *fp_config;                       /* Configuration file pointer */
static char *configfile;               /* Configuration file name */
void read_config(void);
 
/* Prototypes for xpatel callbacks  */

int send_status_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
          
int receive_status_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);        

int send_exposure_cb   (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
      
int receive_exposure_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);      

int send_interrupt_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len);
        
int receive_interrupt_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);    

int send_region_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
          
int receive_region_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);       

int send_process_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
        
int receive_process_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);      
           
int send_relay_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
          
int receive_relay_cb   (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);       

int send_filter_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
     
int receive_filter_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);   

int send_mode_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
      
int receive_mode_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);     

int send_type_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
      
int receive_type_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);    

int send_number_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
      
int receive_number_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);    

int send_multicount_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len);   

int receive_multicount_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);  

int send_temperature_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len);
   
int receive_temperature_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len); 

int send_file_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
      
int receive_file_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);     

int send_target_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
     
int receive_target_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);   

int send_telescope_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
    
int receive_telescope_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);  

int send_instrument_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len); 
   
int receive_instrument_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);  

/* Manage the camera */

void init_camera(void);
void camera(void);

/* 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                  */


/* 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 */


/* Internal flags and counters */

int  ccdflag = FALSE;
int  interruptflag = FALSE;
int  acquire = SINGLE;
int  frame = LIGHT;
int  phase = 0;
int  sequence = 0;
int  frame_count = 1;
int  frame_count_max = FRAMECOUNTMULTI;
int  frame_count_multi = FRAMECOUNTMULTI;
int  archive = FALSE;
int  image_number = 1;
int  scripting = FALSE;

char base_name[64]; 
char file_name[80];
char filter_name[64];


/* Fits header strings */

double fits_exposure;
char fits_date[64];
char fits_type[64];
char fits_target[64];
char fits_instrument[64];
double fits_temperature;
char fits_filter[64];
char fits_telescope[64];


/* Executable external script */

char ccd_script[64];

/* Time */

static struct timeval xtv;
time_t date_obs;


/* Functions from the camera protocol */

extern int GetCameraInfo(int *image_w, int *image_h);

extern int CaptureImage(int *phase,  unsigned short *data,
  int frame, double exposure, int regionflag, 
  int x, int y, int w, int h);
  
extern int CaptureEnd(int *phase);  
       
extern void WriteFits(char *filename, int w, int h, unsigned short *data, 
  double fits_duration, 
  char*  fits_type, 
  char*  fits_date, 
  char*  fits_target,
  char*  fits_instrument, 
  double fits_temperature,
  char*  fits_filter, 
  char*  fits_telescope);    
 
extern int OpenCamera(int device);     

extern int CloseCamera(int device);    

extern int GetCameraStatus(int *status);

extern int GetCameraTemperature(double *power, 
  double *temperature, double *setpoint);

extern int SetCameraTemperature(int state, double temperature);

extern int ActivateCameraRelay(int relay, double ontime);
 
extern int GetCameraFilterName(int new_filter_number, char *filter_name);

extern int SetCameraFilterName(int flag,  
  int new_filter_number, char *filter_name);

extern int SetCameraFilter(int new_filter_number);


/* Camera variables */

unsigned short *image_data;

double current_exposure;
int    image_w = 1024;
int    image_h = 1024;
double image_exposure = 1.;
double image_exposure_remaining = 1.;
int    regionflag = FALSE;
int    region_x = 1;
int    region_y = 1; 
int    region_w = 512;
int    region_h = 512;


/* Temperature control */

double setpoint = -10.;
double temperature = 25.;  
double cooler_power;  
int    cool = 1;


/* Filter functions */

int get_filter_name(int new_filter_number, char *filter_name);
int set_filter(int filter);
int set_filter_name(int flag,  
  int new_filter_number, char *new_filter_name);

/* Filter variables */

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

/* Array of dynamic labels for the filters */ 

static char filter_label[10][24] = {
  "                      ",
  "                      ",
  "                      ",
  "                      ",
  "                      ",
  "                      ",  
  "                      ",  
  "                      ",
  "                      ", 
  "                      "
 };


/* Array of default labels for the filters */ 

static char default_filter_label[10][24] = {
  "                      ",
  "                      ",
  "                      ",
  "                      ",
  "                      ",
  "                      ",  
  "                      ",  
  "                      ",
  "                      ", 
  "                      "
 };

/* External scripting */

int runscript(char *script);


/* Camera relay controls */

int relay;
double relay_ontime = RELAYONTIME;


/* Prototypes for xpaccd callbacks  */

int send_status_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len);

int receive_status_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len);



int main (int argc, char **argv)
{

  char tbuf[SZ_LINE];
  char cmd[SZ_LINE];
  int c;
  int delay=0;

  /* Process switch arguments */
  /* Telescope xpaserver would normally be started without arguments */
  
  /* Arguments recognized are: */
  
  /*   d int -- sets delay int seconds after starting bkgd cmd */
  /*   f cmd -- starts cmd in background */
  /*   q  -- runs quietly */
   
  
  *cmd = '\0';
  while ((c = getopt(argc, argv, "d:f:q")) != -1)
  {
    switch(c)
    {
    case 'd':
      delay = atoi(optarg);
      break;
    case 'f':
      snprintf(cmd, SZ_LINE, "%s %s &\n", argv[0], optarg);
      break;
    case 'q':
      xpa_quiet = TRUE;
      break;
    }
  }

  /* Initialize the camera */
  
  init_camera();

  /* Prepare to start the xpatel server */

  if( optind >= argc )
  {
    strcpy(name, "ccd");
  }
  else
  {
    strcpy(name, argv[optind]);
  }
  
  strcpy(xclass, name);
  cluc(xclass);

  /* Camera commands through xpa */
    
  /* Create the class ccd */
  
  snprintf(tbuf, SZ_LINE, "%s", name);
  xpa1 = XPACmdNew(xclass, tbuf);
  if( xpa1 == NULL )
  {
    fprintf(stderr, "Could not start ccd on xpa \n");
  }
  else
  {

    /* Add commands with their callbacks to ccd */
    
    XPACmdAdd(xpa1, "status", "on/off",
      send_status_cb, (void *)"status", mode, 
      receive_status_cb, (void *)"status", mode);

    XPACmdAdd(xpa1, "exposure", "exposure on/off [time]",
      send_exposure_cb, (void *)"exposure", mode, 
      receive_exposure_cb, (void *)"exposure", mode);
      
    XPACmdAdd(xpa1, "interrupt", "interrupt",
      send_interrupt_cb, (void *)"interrupt", mode, 
      receive_interrupt_cb, (void *)"interrupt", mode);

    XPACmdAdd(xpa1, "region", "region on/off [ds9/clear/select x y w h]",
      send_region_cb, (void *)"region", mode, 
      receive_region_cb, (void *)"region", mode);
      
    XPACmdAdd(xpa1, "process", "process on/off",
      send_process_cb, (void *)"process", mode, 
      receive_process_cb, (void *)"process", mode);
                 
    XPACmdAdd(xpa1, "relay", "relay mask time",
      send_relay_cb, (void *)"relay", mode, 
      receive_relay_cb, (void *)"relay", mode);
 
    XPACmdAdd(xpa1, "filter", "filter number",
      send_filter_cb, (void *)"filter", mode, 
      receive_filter_cb, (void *)"filter", mode);
 
    XPACmdAdd(xpa1, "mode", "mode single/focus/multi",
      send_mode_cb, (void *)"mode", mode, 
      receive_mode_cb, (void *)"mode", mode);
 
    XPACmdAdd(xpa1, "type", "type light/dark/bias/flat",
      send_type_cb, (void *)"type", mode, 
      receive_type_cb, (void *)"type", mode);

    XPACmdAdd(xpa1, "number", "image number",
      send_number_cb, (void *)"number", mode, 
      receive_number_cb, (void *)"number", mode);

    XPACmdAdd(xpa1, "multicount", "multicount number",
      send_multicount_cb, (void *)"multicount", mode, 
      receive_multicount_cb, (void *)"multicount", mode);

    XPACmdAdd(xpa1, "temperature", "temperature setpoint",
      send_temperature_cb, (void *)"temperature", mode, 
      receive_temperature_cb, (void *)"temperature", mode);

    XPACmdAdd(xpa1, "file", "file basename",
      send_file_cb, (void *)"file", mode, 
      receive_file_cb, (void *)"file", mode);

    XPACmdAdd(xpa1, "target", "target name",
      send_target_cb, (void *)"target", mode, 
      receive_target_cb, (void *)"target", mode);

    XPACmdAdd(xpa1, "telescope", "telescope  name",
      send_telescope_cb, (void *)"telescope", mode, 
      receive_telescope_cb, (void *)"telescope", mode);

    XPACmdAdd(xpa1, "instrument", "instrument name",
      send_instrument_cb, (void *)"instrument", mode, 
      receive_instrument_cb, (void *)"instrument", mode);

  }
    
    
  /* Start another background process if option -f cmd */
  
  if( *cmd != '\0' )
  {
    fprintf(stdout, "starting bkgd process: %s", cmd);
    system(cmd);
  }

  /* Delay if requested by option -d int  */
  
  if( delay )
  {
    fprintf(stdout, "starting delay of %d seconds before background process ...", delay);
    XPASleep(delay*1000);
    fprintf(stdout, " done\n");
  }
  
  fflush(stdout);
  fflush(stderr);


  /* Start select loop and monitor for requests */
   
  fprintf(stdout, "\nEntering xpaccd polling loop ...\n");
  
  /* When the telescope is ready telflag will be set TRUE */
  
  while (ccdflag)
  {
 
    /* Poll XPA for 250 milliseconds and process up to 1 request */
    
    XPAPoll(250,1);
    camera();
  }
    
  XPAFree(xpa1);

  /* Cleanup and exit */
  
  XPACleanup();
  if( save_buf ) xfree(save_buf);
  exit(0);
}



/* Telescope utility functions */


/* Start ds9 as a new process */

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

/* Use xpa to tell ds9 to read an image name */

void update_ds9_image(char *name)
{  
  #define NXPA 10
  int i, nresponses;
  int buffer_length = 0;
  char *data_buffer = NULL;
  char *server_names[NXPA];
  char *server_messages[NXPA];
  char ds9msg[64];
  sprintf(ds9msg, "file fits %s",name);
  
  nresponses = XPASet(NULL, "ds9", ds9msg, NULL, data_buffer,
    buffer_length, server_names, server_messages, NXPA);
  
  for(i=0; i < nresponses; i++)
  {
    if( server_messages[i] != NULL )
    {
    
      /* Process error messages */
    
      printf("%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]);  
    }
  }
}
       
/* 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          */
/* Sets regionflag                                                */

int get_ds9_region(void)
{  
  #define NXPA 10
  int i, j, nresponses;
  size_t 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);
          regionflag = TRUE;
        }
        if (j == 3)  
        {
          region_x = (int) (xc - dx);
          region_y = (int) (yc - dx);
          region_w = (int) (dx + dx);
          region_h = region_w;
          regionflag = TRUE;
        }
        if (regionflag == TRUE)
        {
        
          /* 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;
          }                   
        
        }        
      }           
    }
    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;
}


/* Camera start up */

void init_camera(void)
{
    
  int camera_status = FALSE;
  int filter_status = FALSE;
  int cooler_status = FALSE;
    
  if (ccdflag == TRUE)
  {
    return;
  }
      
  /* Initialize the driver for a generic USB camera */

  camera_status = OpenCamera(USB);

  /* Alert stderr to a problem and exit */
  
  if (camera_status != TRUE)
  {
    fprintf(stderr,"Camera did not respond to ccdflag request \n");
    exit(1);
  }
  
  /* Contact the camera and allow its driver to update parameters */
  
  camera_status = GetCameraInfo(&image_w, &image_h);
        
  /* Set base_name to default */
  
  strcpy(base_name,"image_");

  
  /* Initialize the fits headers */
  
  sprintf(fits_type, "                ");
  sprintf(fits_date, "                ");
  sprintf(fits_target, "                ");
  sprintf(fits_filter, "                ");
  sprintf(fits_instrument, "                ");
  sprintf(fits_telescope, "                ");
  
  /* Handle the configuration file to change defaults */
  
  configfile = (char *) malloc (MAXPATHLEN);
  strcpy(configfile,CONFIGFILE);  

  read_config();  
     
  /* Set the image sizes */

  region_x = 1;
  region_y = 1; 
  region_w = image_w;
  region_h = image_h;
          
  /* Select filter and save header information */
  
  filter_status = set_filter(filter);
  filter_status = get_filter_name(filter, filter_name);    
  
  fprintf(stderr,"Selected Filter %d: %s\n", filter, filter_name);

  sprintf(fits_filter,filter_name);
         
  /* Alert operator to absence of filter wheel or set filter wheel error */
  
  if (filter_status != TRUE)
  {
    fprintf(stderr,"Filter wheel did not respond to the request\n");
    filter_wheel = NO_FILTER_WHEEL;
  }

  cooler_status = GetCameraTemperature(&cooler_power, 
    &temperature, &setpoint);
  fits_temperature = temperature;
      
  /* Alert stderr to a temperature controller problem but do not exit*/
  
  if (cooler_status != TRUE)
  {
    fprintf(stderr,"Camera temperature control did not respond to the ccdflag request \n");
  }

  /* Set initial cooler temperature and turn on controller */

  setpoint = -10.;
  cool = TRUE;
  SetCameraTemperature(cool, setpoint); 
      
  /* Allocate memory for images */

  image_data = malloc((image_w*image_h)*sizeof(unsigned short));    
    
  /* Set empty string for file_name until the first image is stored */
  
  strcpy(file_name,"");

  /* Set temperature setpoint to default */
  
  setpoint = -10.;
  
  /* Alert stderr to a problem and exit */
  
  if (camera_status != TRUE)
  {
    fprintf(stderr,"The camera did not respond to the start request \n");
    exit(1);
  }
  ccdflag = TRUE;

}

/* Camera function called during the polling cycle                   */
/* Count cycles and find the temperature every minute                */
/* Start a sequence when the sequence flag is reset 1                */
/* Stop a sequence when the sequence flag is reset 0                 */
/* Start a new exposure when the phase is 0 while the sequence is 1  */
/* Update the time remaining in an ongoing exposure                  */
/* Restart the timer to call this function again                     */

void camera(void)
{
  static int tcount;
  int year, month, day;
  int hours, minutes, seconds, milliseconds;
  long int microseconds;
  
  time_t now;
  struct tm *g;

  /* Test to see if the camera is available */
  if (ccdflag != TRUE)
  {
    return;
  }

  /* Take the device temperature 15 seconds on a 250 ms polling cycle */      
  if ((tcount < 0) || (tcount > 60))
  {
    tcount = 1;
    GetCameraTemperature(&cooler_power,&temperature,&setpoint);
    fits_temperature = temperature;
  }
  tcount++;
           
  /*  Handle a request to interrupt a sequence */
  if ( sequence == 0 )
  {
    if (phase != 0)
    {
      /* End any exposure in progress */
      phase = 2;
      CaptureEnd(&phase); 
      phase=0;
      frame_count = 1;    
      image_exposure_remaining = image_exposure;
    }

    if (frame_count > 1)
    {
      frame_count = 1;
    
      image_exposure_remaining = image_exposure;
    }        
    interruptflag = FALSE;

  }
  
  /* Handle a request for a new exposure */  
  if ((sequence == 1) && ( phase == 0 )) 
  {               
    /* Set parameters for new sequence if needed */
    if (frame_count == 1)
    {
      /* Start a new sequence */
      if (acquire == SINGLE)
      {
        frame_count_max = 1;
      }
      else
      {
        frame_count_max = frame_count_multi;
      } 

      if (acquire == FOCUS)
      {
        archive = FALSE;
      }
      else
      {
        archive = TRUE;
      } 
    }
    
    /* Note the time it started and save as xtv */

    gettimeofday (&xtv, NULL); 
    
    /* Copy seconds part to convert to UTC */

    now = xtv.tv_sec;
    
    /* Copy microseconds part to handle milliseconds of UTC */

    microseconds = xtv.tv_usec;
    
    /* Convert the microseconds part to milliseconds */
    
    milliseconds = microseconds/1000;
    
    /* Convert unix time now to UTC */
    
    g = gmtime(&now);

    /* Copy values pointed to by g to variables we will use later */

    year = g->tm_year;
    year = year + 1900;
    month = g->tm_mon;
    month = month + 1;
    day = g->tm_mday;
    hours = g->tm_hour;
    minutes = g->tm_min;
    seconds = g->tm_sec;
    
    /* Create ascii string for fits header */
       
    sprintf(fits_date, "%4d-%02d-%02dT%02d:%02d:%02d.%03d",
      year,month,day,hours,minutes,seconds,milliseconds);

    if (frame != BIAS)
    {
      current_exposure = image_exposure;
    }
    else
    {
      current_exposure = 0.001;
    } 
    
    CaptureImage(&phase, image_data,
    frame, current_exposure, regionflag, 
    region_x, region_y, region_w, region_h);    
  }

  /* Update the remaining time if an exposure is in progress */
  /* Read the image into memory when done */
  if (phase == 1) 
  {
    struct timeval tv;
    double dt;

    /* Find total elapsed time since starting exposure */
    gettimeofday (&tv, NULL);
    dt = tv.tv_sec - xtv.tv_sec + (tv.tv_usec - xtv.tv_usec)/1e6;
    
    if (dt >= current_exposure) 
    {
      /* Exposure is ready to read */
      /* Read to memory */
      /* Reset the exposure time counter */
        
      image_exposure_remaining = image_exposure;

      CaptureImage(&phase, image_data,
        frame, current_exposure, regionflag, 
        region_x, region_y, region_w, region_h);
    } 
    else 
    {
      /* Exposure is not complete */
        
        image_exposure_remaining = current_exposure - dt;
              
    }
  }
  
  if (phase == 2) 
  {
    /* Write a file appropriate for the acquisition mode */
    
    /* Set the image type description */

    if ( frame == DARK )
    {
      sprintf(fits_type, "%s", "Dark Frame");
    }
    else if ( frame == LIGHT )
    {
      sprintf(fits_type, "%s", "Light Frame");
    }
    else if ( frame == BIAS )
    {
      sprintf(fits_type, "%s", "Bias Frame");
    }
    else if ( frame == FLAT )
    {
      sprintf(fits_type, "%s", "Flat Frame");
    }    
    else
    {
      sprintf(fits_type, "%s", "");
    }      

    if (archive)
    {
      /* Archive an ennumerated file */
      
      if ( frame == DARK )
      {
        sprintf(file_name, "%s%05d.fits", "dark_", image_number);
      }
      else if ( frame == LIGHT )
      {
        sprintf(file_name, "%s%05d.fits", base_name, image_number);
      }
      else if ( frame == BIAS )
      {
        sprintf(file_name, "%s%05d.fits", "bias_", image_number);
      }
      else if ( frame == FLAT )
      {
        sprintf(file_name, "%s%05d.fits", "flat_", image_number);
      }    
      else
      {
        sprintf(file_name, "%s%05d.fits", base_name, image_number);
      } 
      image_number++;    
  
    }
    else
    {
      /* Save a generic file */
      sprintf(file_name, "image.fits");    
    }

    
    if (frame != BIAS)
    {
      fits_exposure = image_exposure + 0.001;
    }
    else
    {
      fits_exposure = 0.001;  
    }

    if (regionflag == FALSE)
    {
      WriteFits(file_name, image_w, image_h, image_data, 
        fits_exposure, 
        fits_type, 
        fits_date, 
        fits_target,                                               
        fits_instrument,
        fits_temperature, 
        fits_filter, 
        fits_telescope);
      update_ds9_image(file_name);
    }
    else
    {
      WriteFits(file_name, region_w, region_h, image_data, 
        fits_exposure, 
        fits_type, 
        fits_date, 
        fits_target,                                               
        fits_instrument,
        fits_temperature, 
        fits_filter, 
        fits_telescope);
      update_ds9_image(file_name);
    }   
    
    /* Run a script if flag is set */
    
    if (scripting == TRUE)
    {
      if (runscript(ccd_script) == -1) 
      {
        fprintf(stderr,"Script returned system error flag\n");
      } 
      else 
      {
        fprintf(stderr,"Script started\n");
      }        
    }   
    
    /* Reset for the next exposure */

    image_exposure_remaining = image_exposure;        
    phase = 0;
    frame_count++;
    
    /* End the sequence when requested frames have been taken */

    if ( frame_count > frame_count_max )
    {
      sequence = 0;
      frame_count = 1;    
    }
  }   
}


/* Provide filter name for a filter number */

int get_filter_name(int new_filter_number, char new_filter_name[24])
{
  int rflag;
    
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    strcpy(filter_name,"Open"); 
    return(1);
  }
    
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = GetCameraFilterName(new_filter_number, new_filter_name);
    return(rflag);
  }
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }  
  
  if ( ( new_filter_number >=1 ) && ( new_filter_number <= filter_max ) )
  { 
    strcpy(new_filter_name, filter_label[new_filter_number - 1]);
    return(1);
  }
  return(0);
}

/* Replace a filter name with a new one for positive flag    */
/* Restore defaults for zero or negative flag                */
/* Return 1: success                                         */ 
/* Return 0: if filter wheel not present no action taken     */
/* Return 0: out of bounds filter_number no action taken     */
/* Use default filter name if incoming name is too long      */

int set_filter_name(int flag, 
  int new_filter_number, char *new_filter_name)
{
  int i;
  int rflag;
  
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    return(0);
  }
    
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = SetCameraFilterName(flag, new_filter_number, new_filter_name);
    return(rflag);
  }
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }    
    
  if ( flag > 0 )
  {
    
    /* Save new filter name */
            
    i = new_filter_number - 1;
    if ( (i >= 0) & (i < filter_max) )
    { 
      snprintf(filter_label[i], 63, "%s", new_filter_name);
    }
    
  }
  else
  {
    
    /* Restore defaults */
    
    filter_max = FILTER_MAX;
    
    if ( filter_max > 10 )
    {
      filter_max = 10;
    }  
  
    for (i=0; i< filter_max; i++)
    {
      strcpy(filter_label[i],default_filter_label[i]);  
    }
  }
  return(1);
}

/* Request filter change                                     */
/* Filter numbers are from 1 to filter_max                   */
/* Sends request to system script for generic filter support */

int set_filter(int filter)
{
  char cmdstr[256];
  /* Respond to request if the filter wheel is available */
  
  int rflag;
     
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    return(0);
  }
       
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = SetCameraFilter(filter);
    return(rflag);
  }  
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }  

  /* Execute external set_camera_filter */
  
  sprintf(cmdstr,"set_camera_filter %d >/dev/null",filter);
  system(cmdstr);

  /* Pause to allow time for wheel to set */
  
  usleep(5000000);
  
  return(1);

}


/* Run an external script */
/* Return system flag */

int runscript(char *script)
{
  int flag;
  char cmdstr[256];

  /* Run the external script by using a system command */

  sprintf(cmdstr,"%s &",script);   
  flag = system(cmdstr);

  return (flag);
}


/* Read and parse a configuration file */

void read_config(void)
{
  char configstr[121];
  char *configptr = configstr;
  int n;
  char filter_name[24];
    
  fp_config = fopen(configfile, "r");
  
  if ( fp_config == NULL )
  {
    fprintf(stderr,"New camera configuration not found.\n");
    fprintf(stderr,"Using default camera parameters.\n");
    return;
  }
  else
  {
    fprintf(stderr,"Camera parameters redefined.\n");
  }
   
  while ( configstr == fgets(configstr,80,fp_config) )
  {

    configptr = strstr(configstr,"ccd.frame_count_multi");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&frame_count_multi); 
        if (frame_count_multi < 2)
        {
          frame_count_multi = 2;
          fprintf(stderr,"Configuration error: attempt to set multi frame count < 2\n");
        }
        else
        {       
          fprintf(stderr,"Multi mode frame count: %d\n",frame_count_multi);
        }
      }  
    }
    
    configptr = strstr(configstr,"ccd.filter_wheel");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&filter_wheel); 
        if (filter_wheel > 2)
        {
          filter_wheel = 0;
          fprintf(stderr,"Configuration error: attempt to set filter wheel outside range\n");
        }
        else
        {       
          fprintf(stderr,"Filter wheel: %d\n",filter_wheel);
        }
      }  
    } 

    configptr = strstr(configstr,"ccd.filter_max");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&filter_max); 
        if (filter_max > filter_max_gui)
        {
          filter_max = 0;
          fprintf(stderr,"Configuration error: attempt to set max filter count above %d\n", filter_max_gui);
        }
        else if (filter_max < 0)
        {
          filter_max = 0;
          fprintf(stderr,"Configuration error: attempt to set max filter count < 0\n");       
        }
        else
        {
          fprintf(stderr,"Max filter count:  %d\n",filter_max);
        }
      }  
    } 
        
    configptr = strstr(configstr,"ccd.filter_01");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 1, filter_name);
        }
        fprintf(stderr,"Filter 1: %s\n",filter_name);
      }  
    }
  
    configptr = strstr(configstr,"ccd.filter_02");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 2, filter_name);
        }
        fprintf(stderr,"Filter 2: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_03");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';;
          set_filter_name(1, 3, filter_name);
        }
        fprintf(stderr,"Filter 3: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_04");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 4, filter_name);
        }
        fprintf(stderr,"Filter 4: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_05");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 5, filter_name);
        }
        fprintf(stderr,"Filter 5: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_06");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 6, filter_name);
        }
        fprintf(stderr,"Filter 6: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_07");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 7, filter_name);
        }
        fprintf(stderr,"Filter 7: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_08");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 8, filter_name);
        }
        fprintf(stderr,"Filter 8: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_09");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 9, filter_name);
        }
        fprintf(stderr,"Filter 9: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_10");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,64);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 64))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 10, filter_name);
        }
        fprintf(stderr,"Filter 10: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.base_name");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%s",base_name);
        fprintf(stderr,"Base name: %s\n",base_name);
      }  
    } 

    configptr = strstr(configstr,"ccd.fits_telescope");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(fits_telescope,configptr,64);
        n=strlen(fits_telescope);
        if ((n >0 ) && (n < 64))
        {  
          fits_telescope[n-1]='\0';
        }
        else
        {
          fits_telescope[0]='\0';
        }        
        fprintf(stderr,"FITS telescope: %s\n",fits_telescope);
      }  
    } 
    configptr = strstr(configstr,"ccd.ccd_script");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%s",ccd_script);
        fprintf(stderr,"External script %s\n",ccd_script);
      }  
    }
  
  }
  fclose(fp_config);

}

/* Command send/receive routines */

int send_status_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  if ( ccdflag == TRUE )
  {
    snprintf(tbuf, SZ_LINE, "%s", "on \n");
  }
  else
  {
    snprintf(tbuf, SZ_LINE, "%s", "off \n");
  }
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);

}
          
int receive_status_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)        
{

  /* Process paramlist */
  
  if( paramlist && *paramlist )
  {
    if( !strcmp(paramlist, "off") )
    {
      CloseCamera(USB);  
      ccdflag = FALSE;
    }
    else if ( !strcmp(paramlist, "on") )
    {
      init_camera();  
      ccdflag = TRUE;    
    }    
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}



int send_exposure_cb   (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  if ( sequence == 0 )
  {
    snprintf(tbuf, SZ_LINE, "off %lf %lf\n", image_exposure_remaining,
      image_exposure );
  }
  else
  {
    snprintf(tbuf, SZ_LINE, "on %lf %lf\n", image_exposure_remaining,
      image_exposure );
  }


 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
      
int receive_exposure_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)      
{
  char cmd[SZ_LINE];
  double new_exposure;
  int j;

  /* Process paramlist */
  
  /* Exposure input is on/off [time] */ 
  
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%s %lf", cmd, &new_exposure);
    
    if (j == 2)
    {
    
      if( !strcmp(cmd, "on") )
      {
        sequence = 1;
        image_exposure = new_exposure;
      }
      else if( !strcmp(cmd, "off") )
      {
        sequence = 0;
        image_exposure = new_exposure;
        image_exposure_remaining = new_exposure;
      }
      
    }
    else if (j == 1)
    {
    
      if( !strcmp(cmd, "on") )
      {
        sequence = 1;
      }
      else if( !strcmp(cmd, "off") )
      {
        sequence = 0;
      }
    
    }

  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}



int send_interrupt_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len)
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  if ( interruptflag == TRUE )
  {
    snprintf(tbuf, SZ_LINE, "on\n");
  }
  else
  {
    snprintf(tbuf, SZ_LINE, "off \n");
  }


 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
        
int receive_interrupt_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)    
{

  /* Process paramlist */
  
  /* Interrupt an exposure */ 
  
  if( paramlist && *paramlist )
  {  
    if( !strcmp(paramlist, "on") )
    {
      sequence = 0;
      interruptflag = TRUE;
    }
  }

  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_region_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  if (regionflag == TRUE)
  {
    snprintf(tbuf, SZ_LINE, "on %d %d %d %d\n", region_x, region_y,
    region_w, region_h);
  }
  else
  {
    snprintf(tbuf, SZ_LINE, "off %d %d %d %d\n", region_x, region_y,
    region_w, region_h);    
  }

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
          
int receive_region_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)       
{
  char cmd1[SZ_LINE];
  char cmd2[SZ_LINE];
  int x, y, w, h;
  int j;

  /* Process paramlist */
  
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%s %s %d %d %d %d", cmd1, cmd2, &x, &y, &w, &h);
    
    if (j == 1)
    {
    
      if( !strcmp(cmd1, "on") )
      {
        regionflag = TRUE;
      }
      else if( !strcmp(cmd1, "off") )
      {
        regionflag = FALSE;
      }
    }
    else if (j == 2)
    {
      if( !strcmp(cmd1, "on") )
      {
        regionflag = TRUE;
      }
      else if( !strcmp(cmd1, "off") )
      {
        regionflag = FALSE;
      }    
      if( !strcmp(cmd2, "clear") )
      {
        region_x = 1;
        region_y = 1;
        region_w = image_w;
        region_h = image_h;
        regionflag = FALSE;
      }
      else if( !strcmp(cmd2, "ds9") )
      {
        get_ds9_region();
      }    
    }
    else if (j == 6)
    {
      if( !strcmp(cmd1, "on") )
      {
        regionflag = TRUE;
      }
      else if( !strcmp(cmd1, "off") )
      {
        regionflag = FALSE;
      }    
      
      if( !strcmp(cmd2, "select") )
      {
        if (((x > 0) && ((x+w) < image_w)) && ((y > 0) && ((y+h) < image_h)))
        {
          region_x = x;
          region_y = y;
          region_w = w;
          region_h = h;
          regionflag = TRUE;
          
          /* 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;
          }                             
          
        }
      }    
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_process_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  if ( scripting == TRUE )
  {
    snprintf(tbuf, SZ_LINE, "on\n");
  }
  else
  {
    snprintf(tbuf, SZ_LINE, "off \n");
  }


 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);

}
        
int receive_process_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)      
{

  /* Process paramlist */
  
  /* Turn on post image scripting */ 
  if( paramlist && *paramlist )
  {  
    if( !strcmp(paramlist, "on") )
    {
      scripting = TRUE;
    }
    else if( !strcmp(paramlist, "off") )
    {
      scripting = FALSE;
    }
    
  }

  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);

}

int send_relay_cb  (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  snprintf(tbuf, SZ_LINE, "%d %lf\n", relay, relay_ontime );

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
          
int receive_relay_cb   (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)       
{
  int new_relay;
  double new_relay_ontime;
  int j;

  /* Process paramlist */
   
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%d %lf", &new_relay, &new_relay_ontime);
    
    if (j == 2)
    {    
      if( new_relay !=0 )
      {
        relay = new_relay;
        relay_ontime = new_relay_ontime;
        ActivateCameraRelay(relay, relay_ontime);
      }
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}


int send_filter_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
   
  snprintf(tbuf, SZ_LINE, "%d %s\n", filter, filter_name );

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
     
int receive_filter_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)   
{
  int new_filter;
  int j;

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%d", &new_filter);
    
    if (j == 1)
    {    
      filter = new_filter;
      set_filter(filter);
      get_filter_name(filter, filter_name);
      strncpy(fits_filter, filter_name, 64);
      fits_target[63] = '\0';
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_mode_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  if (acquire == SINGLE)
  {
    snprintf(tbuf, SZ_LINE, "single\n");
  }
  else if (acquire == FOCUS)
  {
    snprintf(tbuf, SZ_LINE, "focus\n");
  }
  else if (acquire == MULTI)
  {
    snprintf(tbuf, SZ_LINE, "multi\n");
  }
   
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}      

int receive_mode_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)     
{

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    if( !strcmp(paramlist, "single") )
    {
      acquire = SINGLE;
    }
    else if( !strcmp(paramlist, "focus") )
    {
      acquire = FOCUS;
    }
    else if( !strcmp(paramlist, "multi") )
    {
      acquire = MULTI;
    }
    
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}


int send_type_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  if (frame == LIGHT)
  {
    snprintf(tbuf, SZ_LINE, "light\n");
  }
  else if (frame == DARK)
  {
    snprintf(tbuf, SZ_LINE, "dark\n");
  }
  else if (frame == BIAS)
  {
    snprintf(tbuf, SZ_LINE, "bias\n");
  }
    
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
      
int receive_type_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)    
{

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    if( !strcmp(paramlist, "light") )
    {
      frame = LIGHT;
    }
    else if( !strcmp(paramlist, "dark") )
    {
      frame = DARK;
    }
    else if( !strcmp(paramlist, "bias") )
    {
      frame = BIAS;
    }
    
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}


int send_number_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  
  snprintf(tbuf, SZ_LINE, "%d\n", image_number);

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0); 
}
      
int receive_number_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)    
{
  int new_number;
  int j;

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%d", &new_number);
    
    if (j == 1)
    {    
      image_number = new_number;
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_multicount_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len)   
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  
  snprintf(tbuf, SZ_LINE, "%d\n", frame_count_multi);

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}

int receive_multicount_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)  
{
  int new_frame_count_multi;
  int j;

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%d", &new_frame_count_multi);
    
    if (j == 1)
    {    
      frame_count_multi = new_frame_count_multi;
      if ( frame_count_multi < 2 )
      {
        frame_count_multi = 2;
      }
      frame_count_max = frame_count_multi;      
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_temperature_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len)
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];

  /* Send the requested data */
  
  GetCameraTemperature(&cooler_power, &temperature, &setpoint);
      
  snprintf(tbuf, SZ_LINE, "%lf %lf %lf\n", temperature, setpoint, cooler_power);

 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
   
   
int receive_temperature_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len) 
{

  double new_setpoint;
  int j;

  /* Process paramlist */
 
  if( paramlist && *paramlist )
  {
    j=sscanf(paramlist,"%lf", &new_setpoint);
    
    if (j == 1)
    {    
      cool = TRUE;
      setpoint = new_setpoint;
      SetCameraTemperature(cool, setpoint);
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_file_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  snprintf(tbuf, SZ_LINE, "%s %s \n", base_name, file_name);
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
      
int receive_file_cb (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)     
{
  char new_base_name[SZ_LINE];
  int j;
  
  /* Process paramlist */
  
  /* Change the fits file name field */    
  if( paramlist && *paramlist )
  { 
    
    /* Test for one item in the name field to suppress blanks */    
    j=sscanf(paramlist,"%s", new_base_name);
    if (j == 1)
    { 
      strncpy(base_name, new_base_name, 64);
      base_name[63] = '\0';
    }
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_target_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{
  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  snprintf(tbuf, SZ_LINE, "%s\n", fits_target);
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
     
int receive_target_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)   
{

  /* Process paramlist to extract target for fits header */
  
  /* Change the fits target field */ 
  if( paramlist && *paramlist )
  { 
    strncpy(fits_target, paramlist, 64);
    fits_target[63] = '\0';
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}


int send_telescope_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{

  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  snprintf(tbuf, SZ_LINE, "%s\n", fits_telescope);
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
    
int receive_telescope_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)  
{

  /* Process paramlist */
  
  /* Change the fits telescope field */ 
  if( paramlist && *paramlist )
  { 
    strncpy(fits_telescope, paramlist, 64);
    fits_telescope[63] = '\0';
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}

int send_instrument_cb (void *client_data, void *call_data, char *paramlist,
  char **buf, size_t *len) 
{

  XPA xpa = (XPA)call_data;
  char tbuf[SZ_LINE];
  
  /* Send the requested data */
  
  snprintf(tbuf, SZ_LINE, "%s\n", fits_instrument);
 
  if( (xpa->send_mode & XPA_MODE_FILLBUF) )
  {
    send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0);
    *len = 0;
    *buf = NULL;
  }
  /* Return the buffer and let xpa transmit it */
  else
  {
    *len = strlen(tbuf);
    *buf = (char *)xmalloc(*len);
    memcpy(*buf, tbuf, *len);
  }
  
  fflush(stdout);
  fflush(stderr);
  return(0);
}
    
int receive_instrument_cb  (void *client_data, void *call_data, char *paramlist,
  char *buf, size_t len)  
{
  
  /* Process paramlist */
  
  /* Change the fits instrument field */ 
  if( paramlist && *paramlist )
  { 
    strncpy(fits_instrument, paramlist, 64);
    fits_instrument[63] = '\0';
  }
  
  /* Reset save buffer */
  if( save_buf != NULL )
  {
    xfree(save_buf);
    save_buf = NULL;
  }
  save_bytes = len;
  save_buf = (char *)xmalloc(len);
  memcpy(save_buf, buf, len);

  fflush(stdout);
  fflush(stderr);
  return(0);
}


