/* Simple CA Client with Callbacks */

#define TIMEOUT 1.0
#define SCA_OK 1
#define SCA_ERR 0
#define MAX_STRING 40

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <cadef.h>

/* Function prototypes */
int main(int argc, char **argv);
static void connectionChangedCB(struct connection_handler_args args);
static void valueChangedCB(struct event_handler_args args);
static char *timeStamp(void);
static int parseCommand(int argc, char **argv);
static void usage(void);

/* Global variables */
int pvSpecified=0;
char name[MAX_STRING];
time_t curTime, startTime;
double timeout=TIMEOUT;

/***************************** main ***********************************/
int main(int argc, char **argv)
{
    int stat;
    chid pCh;

  /* Parse the command line */
    if(parseCommand(argc,argv) != SCA_OK) exit(1);
    if(!pvSpecified) {
	printf("No PV specified\n");
	exit(1);
    }

  /* Initialize */
    stat=ca_context_create(ca_disable_preemptive_callback);
    if(stat != ECA_NORMAL) {
	printf("ca_context_create failed:\n%s\n",ca_message(stat));
	exit(1);
    }
    
  /* Search */
    stat=ca_create_channel(name,connectionChangedCB,NULL,CA_PRIORITY_DEFAULT,&pCh);
    if(stat != ECA_NORMAL) {
	printf("ca_create_channel failed:\n%s\n",ca_message(stat));
	exit(1);
    }
    printf("%s Search started for %s\n",timeStamp(),name);

  /* Wait */
    startTime=curTime;
    ca_pend_event(timeout);
    printf("%s ca_pend_event timed out after %g sec\n",timeStamp(),timeout);
    
  /* Clear the channel */
    stat=ca_clear_channel(pCh);
    if(stat != ECA_NORMAL) {
	printf("ca_clear_channel failed:\n%s\n",ca_message(stat));
    }
    
  /* Exit */
    ca_context_destroy();
    return(0);
}

/***************************** connectionChangedCB ********************/
static void connectionChangedCB(struct connection_handler_args args)
{
    chid pCh=args.chid;
    int stat;
    
  /* Branch depending on the state */
    switch (ca_state(pCh)) {
    case cs_conn:
	printf("%s Connection successful\n",timeStamp());
	stat=ca_array_get_callback(DBR_STRING,1,pCh,
	  valueChangedCB,NULL);
	if(stat != ECA_NORMAL) {
	    printf("ca_array_get_callback:\n%s\n",ca_message(stat));
	    exit(1);
	}
	break;
    case cs_never_conn:
	printf("%s Cannot connect\n",timeStamp());
	break;
    case cs_prev_conn:
	printf("%s Lost connection\n",timeStamp());
	break;
    case cs_closed:
	printf("%s Connection closed\n",timeStamp());
    break;
    }
}

/***************************** valueChangedCB *************************/
static void valueChangedCB(struct event_handler_args args)
{
  /* Print the value */
    if(args.status == ECA_NORMAL && args.dbr) {
	printf("%s Value is: %s\n",timeStamp(),(char *)args.dbr);
	printf("Elapsed time: %ld sec\n",curTime-startTime);
    } else {
	printf("%s Bad value\n",timeStamp());
    }
}

/***************************** timeStamp ******************************/
static char *timeStamp(void)
  /* Gets current time and puts it in a static array
   * The calling program should copy it to a safe place
   *   e.g. strcpy(savetime,timestamp()); */
{
	static char timeStampStr[16];
	time_t now;
	struct tm *tblock;
	
	time(&now);
	curTime=now;
	tblock=localtime(&now);
	strftime(timeStampStr,20,"%b %d %H:%M:%S",tblock);
	
	return timeStampStr;
}

/**************************** parseCommand ********************************/
static int parseCommand(int argc, char **argv)
{
    int i;

    for(i=1; i < argc; i++) {
	if (argv[i][0] == '-') {
	    switch(argv[i][1]) {
	    case 'h':
		usage();
		exit(0);
	    case 't':
		timeout=atof(argv[++i]);
		break;
	    default:
		fprintf(stderr,"\n\nInvalid option: %s\n",argv[i]);
		usage();
		return SCA_ERR;
	    }
	} else {
	    if(!pvSpecified) {
		strncpy(name,argv[i],MAX_STRING);
		name[MAX_STRING-1]='\0';
		pvSpecified=1;
	    } else {
		printf("\n\nInvalid option: %s\n",argv[i]);
		usage();
		return SCA_ERR;
	    }
	}
    }
    return SCA_OK;
}

/**************************** usage ***************************************/
static void usage(void)
{
    printf(
      "\nUsage: simpleca [Options] pvname\n"
      "  Connects to pvname and gets the value\n"
      "\n"
      "  Options:\n"
      "    -h help      This message\n"
      "    -t float     Timeout in seconds (Default: %g)\n",TIMEOUT);
}
