#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <fstream>

#include <time.h>
#include <math.h>
#include <errno.h>

#include "boinc_db.h"
#include "sched_config.h"
// #include "ap_assimilate_handler.h"
#include "sched_util.h"
#include "sched_msgs.h"
#include "parse.h"
#include "sched_util.h"
#include "util.h"
#include "validate_util.h"
#include "filesys.h"
#include "string.h"

// #include "s_util.h"

// jeffc - path sould be in makefile
#include "../validate/ap_result.h"
#include "../../../seti_boinc/db/db_table.h"
#include "../db/ap_schema.h"
#include "../../../seti_boinc/db/app_config.h"
#include "../../../seti_boinc/db/sqlint8.h"
class star {} ;
#include "setilib.h"
#include "ap_raw_data_db.h"

// the boinc assimilator makes these available
/* SCHED_CONFIG is a BOINC class that says how much memory BOINC
 * is allowed to use, how many floating point operations (?),
 * and so on.  It looks to me like $config never gets used !!
 */
extern SCHED_CONFIG config;
extern bool noinsert;		

const double  D2R =  0.017453292;
const int fpix_res = 10;            // Hz

// these items should go to the DB talbe analysis_cfg?
const long nside = 2048;       // number of cubic pixels
// The following line was already included from setilib/include/seti_coord.h
// const double stdepoch = 2000;  // our standard analysis epoch

const float bad_fp_val_marker = -91.0;
const int bad_int_val_marker = -30000;

struct timespec nanotime;

int populate_ap_result(
	result& ap_result, 
        RESULT& boinc_canonical_result, 
        WORKUNIT& b_wu,
        long long & ap_wu_id 
);
void radecl2pix(double ra, double decl, long &pix);
void pix2radecl(double &ra, double &decl, long pix);

int get_fraction_blanked(result& ap_result, std::ifstream& result_file);
template<typename T> int insert_signals( T&                  signal,
                                         char *              wu_name,
                                         sqlint8_t           ap_result_id,
                                         std::ifstream&      result_file,
                                         receiver_config&    receiver_cfg,
					 analysis_config     analysis_cfg,
                                         int                 appid,
                                         int                 max_signals_allowed);
template<typename T> int pre_process(T& signal, receiver_config& receiver_cfg);
template<typename T> int check_values(T& signal, sqlint8_t ap_result_id, char * wu_name, analysis_config analysis_cfg);
//int pre_process_spike(spike& sah_spike, receiver_config& receiver_cfg);
//int pre_process_gaussian(gaussian& sah_gaussian, receiver_config& receiver_cfg);
//int pre_process_pulse(pulse& sah_pulse, receiver_config& receiver_cfg);
//int pre_process_triplet(triplet& sah_triplet, receiver_config& receiver_cfg);
int eod2stdepoch(double jd, double &ra, double &decl, double stdepoch);
int get_science_configs(WORKUNIT& boinc_wu, long long ap_wu_id, receiver_config& receiver_cfg, analysis_config& analysis_cfg);
int parse_settings_id(WORKUNIT& boinc_wu);
int get_configs_old_method(WORKUNIT& boinc_wu, long long ap_wu_id, receiver_config& receiver_cfg, analysis_config& analysis_cfg);
// long long new_wu_id(long long old_wu_id);


// assimilate_handler() is called by BOINC code and is passed the canonical 
// result for a workunit.  assimilate_handler() reads the referenced result
// file and inserts the result and its signals into the master science DB.
// BOINC also passes the workunit (as it appears in the BOINC DB) and a vector
// containing all results (including the canonical one) for that workunit.
//
// assimilate_handler() may also be called even if there is no canonical result, 
// when there is an error condition.  In this case, the workunit will have a
// nonzero error_mask.  According to the code below, this happens if and only if
// too many results were returned with no consensus.
int assimilate_handler(
    WORKUNIT& boinc_wu, vector<RESULT>& boinc_results, RESULT& boinc_canonical_result
) {
    // fprintf(stderr, "Here (stderr)!\n");
    // printf("Here (stdout)!\n");
    // fflush(stdout);
    int retval=0;
    // int spike_count=0, spike_inserted_count=0, gaussian_count=0, gaussian_inserted_count=0, pulse_count=0, pulse_inserted_count=0, triplet_count=0, triplet_inserted_count=0;
    static receiver_config receiver_cfg;
    static analysis_config analysis_cfg;
    // workunit s_wu;
    // workunit_grp s_wu_grp;
    // result sah_result;
    // spike sah_spike;
    // gaussian sah_gaussian;
    // pulse sah_pulse;
    // triplet sah_triplet;
    result ap_result;
    ap_signal signal;
    char filename[256];
    char * path;
    std::string path_str;
    long ap_result_id;
    // sqlint8_t sah_spike_id, sah_gaussian_id, sah_pulse_id, sah_triplet_id;
    sqlint8_t signal_id;
    static bool first_time = true;
    int sql_error_code;
    long long ap_wu_id;

    APP_CONFIG ap_config;

    nanotime.tv_sec = 0;
    nanotime.tv_nsec = 1000000;


    // app specific configuration
    /* If this is the first time calling this function, locate the science database */
    if (first_time) {
	first_time = false;
	receiver_cfg.id = 0;
	analysis_cfg.id   = 0;
	/* Parse the application config file ap_config.xml, taking
	 * the <scidb_name> tag in that file for the ap_config.scidbname
	 */
	retval = ap_config.parse_file("..");
    	if (retval) {
      		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	     	"First entrance to handler : can't parse config file. Exiting.\n"
      		);
  		return(retval);
	} else {
		/* This line changes to the correct database */
		retval = db_change(ap_config.scidb_name); 
		if (!retval) {
      			log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	     		"First entrance to handler : could not open science DB %s. Exiting.\n",
			ap_config.scidb_name
      			);
  			return(retval);
		} else {
      			log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
       	     		"First entrance to handler : using science DB %s\n",
			ap_config.scidb_name
      			);
		}
	}
    	// Sometimes we want to perform all assimilation functions
    	// *except* insertion into the science master DB.
    	if (noinsert) {
      		log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
      		"[%s] assimilator is in noinsert mode.\n",
      		boinc_wu.name
      		);
    	}
    // If not first time ... do nothing here.  (All commented out.)
    // delete this section once assimilator is working.
    } else {
/*
	retval = db_change(ap_config.scidb_name);
        if (!retval) {
              log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                  "First entrance to handler : could not open science DB %s. Exiting.\n",
                  ap_config.scidb_name
              );
              return(retval);
        } else {
              log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
                  "First entrance to handler : using science DB %s\n",
                  ap_config.scidb_name
              );
        }
*/
   }
   if (noinsert) return 0;   	// Note that this will result in the WU being marked as assimilated - 
				// we will not see it again.

   ap_wu_id = (long long)boinc_wu.opaque;
    // translate seti wuid for thos wus that changed ids during the DB merge
    /* The translation is not needed for astropulse
     * seti_wu_id = new_wu_id((long long)boinc_wu.opaque);
   log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
         	"[%s] old seti WU id is : %lld  new seti WU id is : %lld\n", 
            boinc_wu.name, (long long)boinc_wu.opaque, seti_wu_id
   );
    */

    /* The canonical result id was decided by the validator */
    if (boinc_wu.canonical_resultid) {
      log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
            "[%s] Canonical result is %d.  AstroPulse workunit ID is %lld.\n", 
	    boinc_wu.name,  boinc_wu.canonical_resultid, ap_wu_id
      );
    } else {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
            "[%s] No canonical result\n", boinc_wu.name
      );
    }

    if (!boinc_wu.canonical_resultid) {
	return 0;		// Note that this will result in the WU being marked as assimilated - 
                                // we will not see it again.  No canonical result means that
				// too many results were returned with no consensus. 

    }

    // Obtain the full path to the boinc result file, and check to make sure the file exists.
    retval = get_output_file_path(boinc_canonical_result, path_str);
    if (retval) {
	if (retval == ERR_XML_PARSE) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
			"[%s] Cannot extract filename from canonical result %ld.\n",
            		boinc_wu.name,  boinc_wu.canonical_resultid);
        	return(retval);
	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] unknown error from get_output_file_path() for result %ld.\n",
                        boinc_wu.name,  boinc_wu.canonical_resultid);
                return(retval);
   	}
     } else {
     	path = (char *)path_str.c_str();
     	if (!boinc_file_exists(path)) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        		"[%s] Output file %s does not exist for result %ld.\n",
               	  	boinc_wu.name, path,  boinc_wu.canonical_resultid);
        	return(-1);
     	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Result %ld : using upload file %s\n",
               	  	boinc_wu.name, boinc_wu.canonical_resultid, path);
	}
    }

    // Open it.
    std::ifstream result_file(path, std::ios_base::in);
    if (!result_file.is_open()) {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
           "[%s] open error for result file %s : errno %d\n", 
           boinc_wu.name, path, errno
      );
      return -1;
    }

    // Get the receiver config and analysis config out of the astropulse science database.
    // retval = get_science_configs(boinc_wu, ap_wu_id, receiver_cfg, analysis_cfg);
    retval = get_configs_old_method(boinc_wu, ap_wu_id, receiver_cfg, analysis_cfg);
    if (retval) {
	if (retval == 100) {
		return (0);
	} else {
		return (-1);
 	}
    }
    log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Result %ld : using receiver_cfg %d and analysis_cfg %d\n",
               	  	boinc_wu.name, boinc_wu.canonical_resultid, receiver_cfg.id, analysis_cfg.id);

    // Insert a sah result
    /* This function turns a boinc result into an ap result, more or less by copying
     * the fields one by one from the boinc result to the ap result.  $boinc_wu and
     * $ap_wu_id are hardly used at all.
     */
    retval = populate_ap_result(ap_result, boinc_canonical_result, boinc_wu, ap_wu_id);

    // gets fraction_blanked into ap_result, just before we insert ap_result into the ifx database
    // will return -1 if it can't find the tag, but we ignore the return value.
    get_fraction_blanked(ap_result, result_file);

    /* I assume this function is just trivially inserting the ap_result into the database, 
     * which is possible because the result class is defined from a mysql schema.
     */
    ap_result_id = ap_result.insert();
    if (ap_result_id) {
    	log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
         		"[%s] Inserted result.  Boinc result id is %d.  AP result id is %lld.\n", 
	 		boinc_wu.name, boinc_canonical_result.id, 
			(long long)ap_result_id
   	);
    } else {
	if (sql_last_error_code() == -239) {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] Could not insert duplicate result.  SQLCODE is %ld.  SQLMSG is %s.\n",
                        boinc_wu.name, sql_last_error_code(), sql_error_message()
        	);
		return 0; 	// non-fatal - we will never see this result again
	} else {
		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        "[%s] Could not insert result.  SQLCODE is %ld.  SQLMSG is %s.\n",
                        boinc_wu.name, sql_last_error_code(), sql_error_message()
        	);
        	return -1;	// fatal - non-dup error
	}
    }

    /* Read the signals from the result file and insert them into the database */

    insert_signals( signal,
                    boinc_wu.name, 
                    ap_result_id,
                    result_file, 
                    receiver_cfg, 
		    analysis_cfg,
                    boinc_canonical_result.appid, 
                    60);

    return 0;   // the successful assimilation of one WU
}

/* get_fraction_blanked
 *
 * Arguments:
 * result& ap_result: astropulse result to modify
 * std::ifstream& result_file: Read fraction_blanked from this file
 *
 * Return value:
 * 0 on success (found fraction_blanked tag), -1 on failure
 *
 * This function examines a result file, finds the fraction_blanked,
 * and sets ap_result's fraction_blanked to that value.
 * on failure, set fraction_blanked to -1
 */
int get_fraction_blanked(result& ap_result, std::ifstream& result_file) {
  result_file.clear();
  result_file.seekg(0);

  std::string fraction_blanked = "";
  std::string line;
  int pos_start;
  int pos_end;

  // Keep getting lines from the result file until you find one containing
  // the <fraction_blanked>...</fraction_blanked> tag
  while(std::getline(result_file, line)) {
    // We can find the tag "fraction_blanked" ...
    if((pos_start = line.find("<fraction_blanked>")) != std::string::npos) {
      pos_start += strlen("<fraction_blanked>");
      if((pos_end = line.find("</fraction_blanked>")) == std::string::npos) {
	 ap_result.fraction_blanked = -1;
	 return -1;
      }
      fraction_blanked = line.substr(pos_start, pos_end - pos_start);
      ap_result.fraction_blanked = strtod(fraction_blanked.c_str(), NULL);
      return 0;
    }
    // or "percent_blanked" ...
    if((pos_start = line.find("<percent_blanked>")) != std::string::npos) {
      pos_start += strlen("<percent_blanked>");
      if((pos_end = line.find("</percent_blanked>")) == std::string::npos) {
         ap_result.fraction_blanked = -1;
	 return -1;
      }
      fraction_blanked = line.substr(pos_start, pos_end - pos_start);
      ap_result.fraction_blanked = strtod(fraction_blanked.c_str(), NULL);
      return 0;
    }
  }
  // if we don't find either type of tag, set fraction_blanked to -1
  ap_result.fraction_blanked = -1;
  return -1;
}

/* insert_signals
 * 
 * Arguments:
 * T& signal: Used in the function to temporarily store each signal from the result file.
 * 	      It's listed in the inputs in order to specify the type of signal we're processing.
 * char* wu_name: BOINC workunit name, used only in printing log messages.
 * sqlint8_t ap_result_id: Used to set the signal.resultid before insertion, which connects 
 *            signals to a result.
 * std::ifstream& result_file: Read signals from this file
 * receiver_config& receiver_cfg: Used to pre-process the signal before insertion.
 * analysis_config analysis_cfg: Provides dm_low and dm_high to check for invalid dm
 * int appid: "Temporary for enhanced rollout" (?)
 * int max_signals_allowed: Don't insert more than this many signals, no matter how big
 *	      the result file is.  If 0, insert unlimited number of signals.
 *
 * This function examines a result file and inserts its signals into the astropulse science
 * database.
 */
template<typename T>
int insert_signals( T&                  signal,   
                    char *              wu_name,
                    sqlint8_t           ap_result_id,
                    std::ifstream&      result_file, 
                    receiver_config&    receiver_cfg,   
		    analysis_config	analysis_cfg,
                    int                 appid,
                    int                 max_signals_allowed) {
    int slec;
    const char* sem;
    int signal_count=0, signal_inserted_count=0, retval=0;
    sqlint8_t signal_id=0;
    char full_wu_name[256];

    result_file.clear();
    /* Go to the beginning of the result file */
    result_file.seekg(0); 


    // READ result_file INTO A STRING, result_file_string
    // QUESTIONS: 
    // 		1) What happens if the file is too big?  [SEEMS UNLIKELY, SINCE WE READ IT LINE BY LINE]
    // 		2) Should I be using read_file_malloc instead?
    std::string result_file_buf = "";
    std::string line;
    while(std::getline(result_file, line))
      result_file_buf += line;
    
    // SEEK THE SUBSTRING "best_pulses", AND TRUNCATE result_file_string AT THAT POINT
    size_t best_pulses_pos;
    best_pulses_pos = result_file_buf.find( "<best_pulses>" );
    if(best_pulses_pos > 10000000) {
                log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                                "[%s] Could not insert ap_signal for ap result %ld.  Short result file or couldn't find best pulses.",
                                wu_name, ap_result_id
                );
		return -1;
    }
    result_file_buf.erase(best_pulses_pos);

    // ENABLE READING FROM result_file_string USING CLASS istringstream
    std::istringstream result_file_truncated(result_file_buf, std::istringstream::in);

    // CHANGE result_file TO result_file_truncated WHEREVER IT APPEARS
    while (!result_file_truncated.eof()) {
	/* Read a signal from the result file */
        result_file_truncated >> signal;
        if (!result_file_truncated.eof()) {
            signal_count++;
	    /* Only insert the signal into the database if we haven't yet read the max. 
	     * number of signals allowed.  (Or if max_signals_allowed == 0, meaning we allow
	     * an unlimited number of signals.)
	     */
            if (max_signals_allowed == 0 || signal_count <= max_signals_allowed) {
		/* Check the ra, decl, time, and dm of the signal to make sure they are
		 * in a sensible range.
		 */
                if (!(signal.rfi_found = check_values(signal, ap_result_id, wu_name, analysis_cfg))) {
                    // preprocess only if we have good values
                    retval = pre_process(signal, receiver_cfg);  
                }
                signal.resultid = ap_result_id;

                retval = dir_hier_path(wu_name,
                                       config.download_dir,
                                       config.uldl_dir_fanout,
                                       full_wu_name,
                                       true
                        );
                if (retval) {
                       log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"[%s] dir_hier_path() failed: %d\n", wu_name, retval);
                       exit(1);
                }
		if(signal.period == 0) {
                  signal.raw_data_len = (Astropulse::NUM_SAMPLES_TO_COPY / 4);
		  signal.raw_data = sqlblob<unsigned char>(Astropulse::make_raw_data_string(full_wu_name, signal.peak_bin));
                  if(signal.raw_data.size() == 0) {
                       log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,"[%s] make_raw_data_string() failed.  (Couldn't open file?)\n", full_wu_name);
                       exit(1);
                  }
		}
		else
		  signal.raw_data_len = 0;
                if (appid == 2) signal.reserved = 1;        // temporary for enhanced rollout
		/* Insert the signal in the database */
                signal_id = signal.insert();
		/* Check for errors in the database insert */
                if (signal_id) {
                    log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
                        "[%s] Inserted ap_signal %"INT8_FMT" for ap result %"INT8_FMT"\n",
                        wu_name, INT8_PRINT_CAST(signal_id), INT8_PRINT_CAST(ap_result_id)
                    );
                    signal_inserted_count++;
                } else {
                    slec = sql_last_error_code();
		    sem = sql_error_message();
		    printf("%d %s\n", slec, sem);
		    printf("%ld %s\n", ap_result_id, sem);
		    printf("%ld %d\n", ap_result_id, slec);
		    printf("%d %d %s\n", ap_result_id, slec, sem);
                    log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                                "[%s] Could not insert ap_signal for ap result %ld. SQLCODE is %d.  SQLMSG is %s.\n",
                                wu_name, ap_result_id, sql_last_error_code(), sql_error_message()
                    );
                    return -1;
                }  
            }   
        }   
    } 

    log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
        "[%s] Inserted %d out of %d signals for ap result %"INT8_FMT" \n",
        wu_name, signal_inserted_count, signal_count, INT8_PRINT_CAST(ap_result_id)
    );

}

/* populate_ap_result
 * result& a_result: astropulse result to create
 * RESULT& b_result: boinc result to create it from
 * WORKUNIT& b_wu: boinc workunit, used only for its error_mask
 * long long & ap_wu_id: used to create a_result.wuid
 *
 * Starting from a boinc result object, creates an astropulse result object
 */
int populate_ap_result(
	result& a_result, RESULT& b_result, WORKUNIT& b_wu, long long & ap_wu_id
) {

    // from boinc result
    a_result.boinc_result	= b_result.id;
    a_result.received		= seti_time((time_t) b_result.received_time).jd().uval();
    a_result.wuid		    = ap_wu_id;
    a_result.hostid		    = b_result.hostid;
    a_result.versionid		= b_result.app_version_num;
    a_result.return_code	= b_result.exit_status;
    if ((int)b_result.opaque & RESULT_FLAG_OVERFLOW) a_result.overflow = 1;

    // from boinc WU
    a_result.reserved		= b_wu.error_mask;

    return(0);
	
}

template<typename T>
int pre_process(T& signal, receiver_config& receiver_cfg) {
#if 0
    // barycenter reference frame - note that we pass epoch of the 
    // day coordinates as s4dop_FreqAtBaryCenter() does precession.
    // We should change this but such a change will involve several
    // projects.
    signal.barycentric_freq =  s4dop_FreqAtBaryCenter(signal.detection_freq,
                                                         signal.time,
                                                         signal.ra,
                                                         signal.decl,
                                                         stdepoch,
                                                         receiver_cfg.s4_id
                                                        );
#endif

    // precess in place.  eod stands for "epoch of the day"
       eod2stdepoch(signal.time, signal.ra, signal.decl, stdepoch);

#if 0
    // cubic pixel number with precessed coords
    // radecl2pix(signal.ra, signal.decl, q_pix); 
    // move the sky portion to the high order 4 bytes 
    // and add in freq portion (will land in the low order 4 bytes). 
    // signal.q_pix = q_pix;
    // signal.q_pix = (signal.q_pix << 32) + ((unsigned long)round(signal.barycentric_freq / fpix_res));  
    //
#endif 

    return 0;
}

/* check_values
 * 
 * Arguments:
 * T& signal: A signal object that we will check to see if its values are good.
 * sqlint8_t ap_result_id: Used only in the text of error log messages.
 * char* wu_name: Used only in the text of error log messages.
 *
 * This function checks the signal values (ra, decl, time, and freq) to make
 * sure that they are sensible.  If they are not, it sets the bad value to
 * bad_fp_val_marker, and returns 1.  Otherwise, it returns 0.
 */ 
template<typename T>
int check_values(T& signal, sqlint8_t ap_result_id, char * wu_name, analysis_config analysis_cfg) {

    int retval=0;

    // check for ranges and NaN's

    if(signal.ra < 0 || signal.ra >= 24 || signal.ra != signal.ra) {
   	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	"[%s] Signal for result %ld has out of range RA : %lf\n", 
	wu_name, ap_result_id, signal.ra
    	);
	signal.ra = bad_fp_val_marker;
    	retval = 1;
    }
    if(signal.decl < -90 || signal.decl > 90 || signal.decl != signal.decl) {
   	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	"[%s] Signal for result %ld has out of range declination : %lf\n", 
	wu_name, ap_result_id, signal.decl
    	);
	signal.decl = bad_fp_val_marker;
    	retval = 1;
    }
    // time range is from 1990 to 2050
    if(signal.time < 2447892.5 || signal.time > 2469807.5 || signal.time != signal.time) {
   	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	"[%s] Signal for result %ld has out of range time : %lf\n", 
	wu_name, ap_result_id, signal.time
    	);
	signal.time = bad_fp_val_marker;
    	retval = 1;
    }
    // dm range is from
    if(abs(signal.dm) < analysis_cfg.dm_low || 
       // this needs the dm_chunk_large added
       // because dm_high is just the maximum value for the _start_ of a chunk
       abs(signal.dm) > analysis_cfg.dm_high + analysis_cfg.dm_chunk_large || 
       signal.dm != signal.dm) {
        log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
	"[%s] Signal for result %ld has out of range dm : %d\n",
	wu_name, ap_result_id, signal.dm
	);
	signal.dm = bad_int_val_marker;
	retval = 1;
    }
    /*
    if(signal.freq < 0 || signal.freq != signal.freq) {
   	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
       	"[%s] Signal for result %ld has out of range frequency : %lf\n", 
	wu_name, ap_result_id, signal.freq
    	);
	signal.freq = bad_fp_val_marker;
    	retval = 1;
    }
    */

    return(retval);
}


/* get_science_configs
 *
 * Arguments:
 * WORKUNIT& boinc_wu: a BOINC workunit.  Should be given to us by BOINC.
 * long long ap_wu_id: This input is unused, had something to do with the "old method".
 * receiver_config& receiver_cfg: return value
 * analysis_config& analysis_cfg: return value
 *
 * This function locates the receiver and analysis configuration settings (from the astropulse
 * science database) that are appropriate to the input workunit.
 */
int get_science_configs(WORKUNIT& boinc_wu, long long ap_wu_id, receiver_config& receiver_cfg, analysis_config& analysis_cfg) {

    int settings_id, retval=0;
    static int first_time=1, current_settings_id=0, current_analysis_config_id=0, current_receiver_config_id=0;
    settings settings;

#if 1
    settings_id = parse_settings_id(boinc_wu);
    if (settings_id) {
        log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
             "[%s] Name string contains  settings id %ld\n",
             boinc_wu.name, settings_id
        );
    } else {
        log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG,
             "[%s] Name string contains no settings id\n",
             boinc_wu.name
        );
    }
#endif

#if 0
    // temp hack
    if (first_time) {
	    retval = get_configs_old_method(boinc_wu, ap_wu_id, receiver_cfg, analysis_cfg);
	    if (!retval) first_time = 0;
    }
    return (retval);
    // end temp hack
#endif

  /* Remove this from astropulse, b/c we're starting with new WUs.  ? */
    // can be removed after all WUs have settings (no settings would then be a fatal error)
    if (!settings_id) {
	    get_configs_old_method(boinc_wu, ap_wu_id, receiver_cfg, analysis_cfg);
        return(0);
    }
    // end can be removed after all WUs have settings

    if (settings_id == current_settings_id) {
	    return (0);
    } else {
        current_settings_id = settings_id;

	    // get settings row
	    /* This line is fetching information from the settings table in the astropulse science
	     * database.  Note that in the splitter, we use fetch(std::string "where active=1"), meaning 
	     * to fetch the currently active settings.  Here, however, we fetch(int settings_id), where
	     * $settings_id was extracted from the name of the workunit given to us by BOINC.
	     * This is because the workunit needs to be associated with the settings that were active
	     * when it was created, _not_ the currently active settings.
	     */
	    if (settings.fetch(settings_id)) {
            log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
                    "[%s] Obtained science master DB row for settings table id %ld\n",
                    boinc_wu.name, settings.id
            );
        } else {
            log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                    "[%s] Could not obtain science master DB row for settings table id %ld. SQLCODE is %d\n",
                    boinc_wu.name, settings_id, sql_last_error_code()
            );
		    return(-1);
	    }

	    /* Now, using the settings information that we just fetched, it is a trivial matter to
	     * fetch the appropriate rows from the astropulse science database's
	     * analysis_config and receiver_config tables.
	     */

	    // get analysis config row
	    if (settings.analysis_cfg.id != current_analysis_config_id) {
		    if (analysis_cfg.fetch(settings.analysis_cfg.id)) {
                current_analysis_config_id = analysis_cfg.id;
			    log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
                       		"[%s] Obtained science master DB row for analysis_config table id %ld\n",
                     		boinc_wu.name, analysis_cfg.id
                );
            } else {
               	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                        	"[%s] Could not obtain science master DB row for analysis_config table id %ld. SQLCODE is %d\n",
                        	boinc_wu.name, settings.analysis_cfg.id, sql_last_error_code()
               	);
			    return(-1);
		    }
        }

	    // get receiver config row 
	    if (settings.receiver_cfg.id != current_receiver_config_id) {
		    if (receiver_cfg.fetch(settings.receiver_cfg.id)) {
                current_receiver_config_id = receiver_cfg.id;
			    log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
                       		"[%s] Obtained science master DB row for receiver_config table id %ld\n",
                       		boinc_wu.name, receiver_cfg.id
                );
           	} else {
               	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                           	"[%s] Could not obtain science master DB row for receiver_config table id %ld. SQLCODE is %d\n",
                        	boinc_wu.name, settings.receiver_cfg.id, sql_last_error_code()
               	);
			    return(-1);
		    }
        }
        
        return(0);

    }
}


/* parse_settings_id
 *
 * Arguments:
 * WORKUNIT& boinc_wu: a workunit given to us by BOINC
 *
 * This function parses boinc_wu.name to determine the
 * settings_id for the workunit.  This settings_id corresponds to a
 * row of the settings table in the astropulse science database, which
 * tells us what the settings were when this workunit was created, thereby
 * allowing to properly assimilate it.
 */
int parse_settings_id(WORKUNIT& boinc_wu) {

    int i=0, j=0, num_tokens=1, pos=0, len=0;
    const int max_tokens=6, max_token_len=128, settings_token=4;
    char tokens[max_tokens][max_token_len];

    len = strlen(boinc_wu.name);

    // parse the dot delimited WU name into its
    // constituent tokens
    for (pos=0, i=0, j=0; 
         pos < len && i < max_tokens; 
         pos++) {
        if (boinc_wu.name[pos] == '.') {
                // terminate this token
                tokens[i][j] = '\0';
                j = 0;
                i++;
                num_tokens++;
                continue;
        }
        if (j >= max_token_len) {
      		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        		"[%s] Token too long during name parsing.\n",
        		boinc_wu.name
      		);
            exit(1);
        } else {
            // continue this token
            tokens[i][j] = boinc_wu.name[pos];
            j++;
        }
    }

    if (i >= max_tokens) {
	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
                "[%s] Too many tokens during name parsing.\n",
                boinc_wu.name
        );
        exit(1);
    } else {
        tokens[i][j] = '\0';		// terminate the final token
        num_tokens++;
    }

    if (i == max_tokens-1) {		// does name cointain settings?
        return(atoi(tokens[settings_token]));    // yes - return it
    } else {
	    return(0);
    }
}

/* get_configs_old_method
 * 
 * Arguments:
 * WORKUNIT& boinc_wu:
 * long long ap_wu_id:
 * receiver_config& receiver_cfg
 * analysis_config& analysis_cfg
 *
 * If receiver_cfg.id or analysis_cfg.id is not the same as that indicated
 * for the ap workunit with ap_wu_id, then fetch new receiver config and/or
 * analysis config.
 */
int get_configs_old_method(WORKUNIT& boinc_wu, long long ap_wu_id, receiver_config& receiver_cfg, analysis_config& analysis_cfg) {

    workunit 	  a_wu;
    // workunit_grp  a_wu_grp;
    int 	  sql_error_code;

    // workunit
    if (a_wu.fetch(ap_wu_id)) {
      log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
        "[%s] Obtained science master DB row for wu %lld [%s]\n",
        boinc_wu.name, ap_wu_id, a_wu.name
      );
    } else {
      sql_error_code = sql_last_error_code();
      if (sql_error_code == 100) {
      	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        	"[%s] Science master DB row for wuid %lld does not exit.  Marking as assimilated.\n",
        	boinc_wu.name, ap_wu_id
      	);
	return sql_error_code;
      } else {	
      	log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        	"[%s] Could not obtain science master DB row for wuid %lld. SQLCODE is %d\n",
        	boinc_wu.name, ap_wu_id, sql_error_code
      	);
        return -1;
      }
    }

#if 0
    // workunit_grp
    if (a_wu_grp.fetch(a_wu.group_info.id)) {
      log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
        "[%s] Obtained science master DB row for wugid %ld\n",
        boinc_wu.name, a_wu_grp.id
      );
    } else {
      log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        "[%s] Could not obtain science master DB row for wugid %ld. SQLCODE is %d\n",
        boinc_wu.name, a_wu.group_info.id, sql_last_error_code() 
      );
      return -1;
    }
#endif

    // receiver_config - get it if it has changed
    if (receiver_cfg.id != a_wu.receiver_cfg.id) {
    	if (receiver_cfg.fetch(a_wu.receiver_cfg.id)) { 
      		log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
        		"[%s] Obtained science master DB row for receiver table id %ld [%s]\n",
        		boinc_wu.name, receiver_cfg.id, receiver_cfg.name
      		);
    	} else {
      		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        		"[%s] Could not obtain science master DB row for receiver table id %ld. SQLCODE is %d\n",
        		boinc_wu.name, a_wu.receiver_cfg.id, sql_last_error_code() 
      		);
      	return -1;  
    	}
    }

    // analysis_config - get it if it has changed
    if (analysis_cfg.id != a_wu.analysis_cfg.id) {
     	if (analysis_cfg.fetch(a_wu.analysis_cfg.id)) { 
      		log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL,
       		 	"[%s] Obtained science master DB row for analysis table id %ld.\n",
       		 	boinc_wu.name, analysis_cfg.id
      		);
    	} else {
      		log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL,
        		"[%s] Could not obtain science master DB row for analysis table id %ld. SQLCODE is %d\n",
        		boinc_wu.name, a_wu.analysis_cfg.id, sql_last_error_code()
      		);
      	return -1;
    	}
    }

    return 0;
}

/* Function is not called
long long new_wu_id(long long old_wu_id) {

    workunit seti_wu;
    char buf[256];

    sprintf(buf,"where sb_id=%lld", old_wu_id);

    if (!seti_wu.fetch(std::string(buf))) {
        return (old_wu_id);
    } else {
        return (seti_wu.id);
    }
    
}
*/
