/*
  
================================================================================
 
  Abs:  IOC database restore routine that uses dynamic restore function calls 
        (no record processing). Also handles waveforms.

 
  Name: dynamic_restore.c
        (entry point is dynamic_restore(filename))

  Proto: restore_utils.h

  Auth: 13-Sep-2004, James Silva (jsilva)
  Rev:  DD-MMM-YYYY, Reviewer's Name (.NE. Author's Name)
 
--------------------------------------------------------------------------------
 
  Mod:  (newest to oldest)  
       04-Nov-2004, James Silva (jsilva):
       Added restore_version global variable and added it to save_file_ok args.
       11-Oct-2004, James Silva (jsilva)
       Added more environment variables to initHooks registry function.
       07-Oct-2004, James Silva (jsilva)
       Changed dynamicRestoreHookInit to pass environment variable to 
       dynamic_restore.
       06-Oct-2004, James Silva (jsilva)
       Added initHooks registry functions for use with EPICS R3.14
       01-Oct-2004, James Silva (jsilva)
       Made changes to code for build with soft IOC.
 
==============================================================================*/



#include        <stdio.h>         /* FILE,fprint,fopen,close */
#include	<string.h>        /* for strcat */
#include        <stdlib.h>        /* for getenv */
#include	<ctype.h>         /* for isalpha, isdigit, and isspace */
#include        <epicsPrint.h>    /* for epicsPrintf */
#include        <dbAccess.h>      /* dynamic database access definitions */
#include        <restore_utils.h> /* restore utility functions */
#include        <epicsVersion.h>  /* for EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION */

#if EPICS_VERSION >= 3 && EPICS_REVISION >= 14 && EPICS_MODIFICATION > 4
#include        <initHooks.h>     /* for initHookRegister, initHookState */
#include        <epicsExport.h>   /* for epicsExportRegistrar */
#endif 


char restore_version[10]; /* Version number of file to be restored */


/*=============================================================================
 
  Abs: Uses dynamic restore function calls (no record processing), given channel 
       list in given filename argument. Also handles waveforms.
  
  Name: dynamic_restore

  Args:   filename
          Use:  char-string    
          Type: char *    
          Acc:  read-only   
          Mech: reference 

 
  Rem:  Checks for an environment variable matching filename argument. If non-existant,
        attempts to open filename as a file. Once file is open, iterates through the channel
	name/value pairs in filename and calls dbPut() to restore the channel values to the
        IOC database(both scalars and waveforms). Calling dbPut() does not activate record
        processing.

  Side: Calls dbPut on channels in filename, and sets the values to those channels to 
        values it reads from the Channel List file(filename). Will send epicsPrintf 
	messages for channel values that it cannot restore.
 
  Ret:  0 if filename open sucessful, -1 if file open not successful.
  
==============================================================================*/


int dynamic_restore(char * filename)
{
  char            read_filename[120];
  FILE            *inp_fd;
  char            channel[80];
  char            input_line[120];
  char            value[80];
  
  int status;                  /* from ca_* calls */
  long chType;                 /* channel access channel type as defined in <cadef.h> */
  unsigned count;              /* waveform count from s/r V2.x restore file */
  void * valueNative = NULL;   /* Native datatype for waveform record */
  struct dbAddr channel_addr;  /* stores channel information for use in dbPut */
  long result;
  char *str_c = NULL;
  
    /* Get the environmental variable */
  str_c =  getenv( filename );
  
  if ( str_c ) {
    /* Construct filename of channels to be restored */
    memset( read_filename,' ',sizeof(read_filename) );
    sprintf(read_filename,"%s.sav",str_c);
  }
  else { /* check if filename argument exists as file instead */
    strcpy (read_filename, filename);
  }


  /* save file ok */  
  /* see if filename is suitable for restore */

  if (save_file_ok(read_filename, restore_version) != 0) {
    epicsPrintf ("dynamic_restore: %s is not suitable to restore.\n", read_filename);
    strcat (read_filename, "B");
    if (save_file_ok(read_filename, restore_version) == 0) {
      epicsPrintf ("dynamic_restore: Will restore %s instead.\n", read_filename);
      }
    else {
      epicsPrintf ("dynamic_restore: %s is not suitable to restore.\n", read_filename);
      strcpy (read_filename, filename);
      strcat (read_filename, ".next");
      if (save_file_ok(read_filename, restore_version) == 0) {
        epicsPrintf ("dynamic_restore: Will restore %s instead.\n", read_filename);
        }
      else {
        epicsPrintf ("dynamic_restore: %s is not suitable to restore.\n", read_filename);
        strcpy (read_filename, filename);
        strcat (read_filename, ".bu");
        if (save_file_ok(read_filename, restore_version) == 0) {
          epicsPrintf ("dynamic_restore: Will restore %s instead.\n", read_filename);
          }
        else {
          epicsPrintf ("dynamic_restore: %s is not suitable to restore.\n", read_filename);
          epicsPrintf ("dynamic_restore: aborting request to restore %s.\n", filename);
          return (-1);
          };
        };
      };
    };

  /* open file */
  if ((inp_fd = fopen(read_filename, "r")) == NULL){
    epicsPrintf("dynamic_restore: unable to open file %s.\n",read_filename);
    return (-1);
  };

  while (fscanf(inp_fd,"%s",channel) != EOF){

  /* toss comment lines */

      /* get the entire input line from the file */
      restore_line(read_filename, inp_fd, sizeof(input_line), input_line);

      if (isalpha(channel[0]) || isdigit(channel[0])) {

	/* parse the input line for type, count, and value */
	status = read_restore_channel(read_filename, inp_fd, channel, input_line, &chType, &count, value, &valueNative);

	if (1 == status)	{ /* successful read_restore_channel */
	  /* get the dbAddr struct from the channel name*/
	  status = dbNameToAddr(channel, &channel_addr);

	  if (0 == status) { /* db entry for channel name found */

	      /* at this point we should have the channel name, type, and value (valueNative if waveform) */
	      if ((strstr(value, "waveform")) && (count > 1)) /* if waveform */
		{

		  result = dbPut(&channel_addr, channel_addr.field_type, valueNative, count);
		  if (result != 0)
		    epicsPrintf ("dynamic_restore: Failure of dbPut on channel %s (waveform)\n", channel);    

		} 

	      else { /* scalar */

		    result = dbPut(&channel_addr, DBR_STRING, value, 1); /* only one element to write */
		    if (result != 0)
		      epicsPrintf ("dynamic_restore: Failure of dbPut on channel %s\n", channel);      

	      }
	  }
	  else { /* no db entry for channel name found */
	    epicsPrintf ("dynamic_restore: unable to find db addr for channel %s\n", channel);
	  };	  
      }
      else { /* bad status from read_restore_channel */
	epicsPrintf ("dynamic_restore: unable to read value for channel %s from %s", channel, read_filename);
	    
      }; /* status of read_restore_channel NOT OK */
    }; /* if line not comment line */
  } /* while loop for reading restore file */

  return (0);

} /* end of dynamic_restore */





#if(EPICS_VERSION >= 3 && EPICS_REVISION >= 14 && EPICS_MODIFICATION > 4)


void dynamicRestoreFunction(initHookState state);

void dynamicRestoreHookInit(void)
{
  initHookRegister(dynamicRestoreFunction);
}


epicsExportRegistrar(dynamicRestoreHookInit); 


void dynamicRestoreFunction(initHookState state)
{
  switch(state) {

  case  initHookAfterScanInit:

    dynamic_restore("DYNAMIC_RESTORE_FILE");   
/*    dynamic_restore("DYNAMIC_RESTORE_FILE_2");   
    dynamic_restore("DYNAMIC_RESTORE_FILE_3");   
    dynamic_restore("DYNAMIC_RESTORE_FILE_4");  */
    break;

  default:
    break;
  }
}


#endif

