
/*=====================================================================
// Copyright (C) 2000-2007 Instrumental Software Technologies, Inc.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code, or portions of this source code,
//    must retain the above copyright notice, this list of conditions
//    and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
// 3. All advertising materials mentioning features or use of this
//    software must display the following acknowledgment:
//    "This product includes software developed by Instrumental
//    Software Technologies, Inc. (http://www.isti.com)"
// 4. If the software is provided with, or as part of a commercial
//    product, or is used in other commercial software products the
//    customer must be informed that "This product includes software
//    developed by Instrumental Software Technologies, Inc.
//    (http://www.isti.com)"
// 5. The names "Instrumental Software Technologies, Inc." and "ISTI"
//    must not be used to endorse or promote products derived from
//    this software without prior written permission. For written
//    permission, please contact "info@isti.com".
// 6. Products derived from this software may not be called "ISTI"
//    nor may "ISTI" appear in their names without prior written
//    permission of Instrumental Software Technologies, Inc.
// 7. Redistributions of any form whatsoever must retain the following
//    acknowledgment:
//    "This product includes software developed by Instrumental
//    Software Technologies, Inc. (http://www.isti.com/)."
// THIS SOFTWARE IS PROVIDED BY INSTRUMENTAL SOFTWARE
// TECHNOLOGIES, INC. "AS IS" AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL INSTRUMENTAL SOFTWARE TECHNOLOGIES,
// INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//=====================================================================
//  A current version of the software can be found at
//                http://www.isti.com
//  Bug reports and comments should be directed to
//  Instrumental Software Technologies, Inc. at info@isti.com
//=====================================================================
// This work was funded by the IRIS Data Management Center
// http://www.iris.washington.edu
//===================================================================== 
*/

/*
**   THIS FILE IS UNDER RCS - DO NOT MODIFY UNLESS YOU HAVE
**   CHECKED IT OUT USING THE COMMAND CHECKOUT.
**
**    $Id: ew2mseed_files.c,v 1.2 2010/04/28 13:48:49 ilya Exp $
** 
**	Revision history:
**	$Log: ew2mseed_files.c,v $
**	Revision 1.2  2010/04/28 13:48:49  ilya
**	Update to sync with IRIS DMC version
**	
**	Revision 1.14  2010/04/23 18:45:45  ilya
**	Fixed a bug in error message related to failed ms_pack()
**	
**	Revision 1.13  2010/04/23 18:39:08  ilya
**	Fixed logging for ms_pack failed return value
**	
**	Revision 1.12  2009/12/21 17:31:28  ilya
**	Fixed a problem which widened gaps in the beginning of day files
**	
**	Revision 1.11  2008/12/29 17:49:02  ilya
**	Fixes to suypport long period channels
**	
**	Revision 1.10  2007/09/17 16:29:48  ilya
**	Fixing problems with 78.1 EW integration
**	
**	Revision 1.9  2007/04/12 19:21:04  hal
**	Added ew7.0+ support (-dSUPPORT_SCNL) and linux port (-D_LINUX)
**	
**	-Hal
**	
**	Revision 1.8  2006/09/20 21:12:13  ilya
**	Apperantly fixed the lepseconds bug
**	
**	Revision 1.7  2006/07/17 17:26:02  ilya
**	7/17/06
**	
**	Revision 1.6  2005/03/23 03:24:25  ilya
**	Fixed alighnment of data in BUD volumes
**	
**	Revision 1.5  2003/03/12 19:09:34  ilya
**	Patched condition generated by buggy waverserver
**	
**	Revision 1.4  2002/05/13 16:54:03  ilya
**	More changes for not writing into the wrong file
**	
**	Revision 1.3  2002/05/03 02:35:09  ilya
**	BUG fix: MSEED records -> wrong files
**	
**	Revision 1.2  2002/02/21 21:17:19  ilya
**	Bug fixed: use SCN->num instead of SCN->avail in init functions
**	
**	Revision 1.1.1.1  2002/01/24 18:32:05  ilya
**	Exporting only ew2mseed!
**	
**	Revision 1.1.1.1  2001/11/20 21:47:00  ilya
**	First CVS commit
**	
 * Revision 1.9  2001/02/06  05:54:24  comserv
 *  new variable writeOut is used to monitor the status of fwrite() in createMseed()
 *
 * Revision 1.8  2001/01/26  05:53:12  comserv
 * Added byteswapping of wf->pinno in createMseed
 *
 * Revision 1.7  2001/01/24  05:07:08  comserv
 * Condition in createFirstMseedFile is fixed
 *
 * Revision 1.6  2001/01/18  23:50:30  comserv
 * Fixed memory leak in ew2mseedUpdateFileName()
 * Removed TEAR and OVERLAP messages from stderr
 *
 * Revision 1.5  2001/01/18  21:58:35  comserv
 * Modifications to removed overlapped snippets
 * Algorithm for  computing actEndtime is changed to account for GAPS case
 *
 * Revision 1.4  2001/01/17  21:52:14  comserv
 * Prerelease version ; copyright notice
 *
 * Revision 1.3  2001/01/08  22:54:48  comserv
 * Modifications to speed up
 *
 * Revision 1.2  2001/01/04  19:29:57  comserv
 * Changes reflecting %03d format for jday and fixes required to write correct data into the right files
 *
 * Revision 1.1  2000/11/17  06:56:07  comserv
 * Initial revision
 *
*/

/* Standard includes */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <math.h>

/* Includes from Earthworm distribution */
#include "earthworm.h"
#include "ws_clientII.h"
#include "trace_buf.h"

#include "qlib2.h"

/* Local includes */
#include "ew2mseed.h"

/* Define MiniSEED fixed header size in bytes */
/* It could be definced also in qlib2 ms_utils.c */
#ifndef FIXED_DATA_HDR_SIZE 
#define FIXED_DATA_HDR_SIZE 48
#endif


/*************************************************************************************/

  int filesHandler(RINGS *rn)
  {	
  	int retVal;

	

  /*  Test and create (if needed) NET/STA subdirs 
   ********************************/
  	retVal = checkNetDirs(rn);
  	if (retVal == EW_FAILURE)
  	{
  		fprintf(stderr, "ew2mseed_files fatal error: checkNetDirs failed\n");	
  		logit("pt", "ew2mseed_files fatal error: checkNetDirs failed\n");
		exit (-3);	
  	}
  
/*  Test and create (if needed) MSEED files
 * Get the last MSEED time from the existing files 
   ********************************/
	retVal = setMseedFiles (rn);
  	if (retVal == EW_FAILURE)

  	{
  		fprintf(stderr, "ew2mseed_files fatal error: setMseedFiles failed\n");	
  		logit("pt", "ew2mseed_files fatal error: setMseedFiles failed\n");
		exit (-4);
  	}


  	return EW_SUCCESS;	
  }

/***************************************************************************************/

  int setMseedFiles (RINGS *rn)
  {	
	int i;
	struct SCN_ring *scn_head;
	char *begFileName;

	scn_head = rn->scnRing;
	begFileName = (char *) calloc(1, 40);

	/* IGD 02/21/02 Bug fixed : was rn->sc_avail, however the unavalable
	 * stations were not removed from SCN loop yet, that caused the last
	 * few stations to never associate theirselves with the corresponding files. 
         * Thanks, Tim and Sandy
         */ 
	for (i = 0 ; i < rn->SCN_num ; i++)
  	{
		sprintf(begFileName, "%s.%s.%s.%s.",
			rn->scnRing->traceRec.sta, rn->scnRing->traceRec.net,
			(rn->scnRing->locId[0] == '-') ? "" : rn->scnRing->locId, rn->scnRing->traceRec.chan);
		rn->scnRing->curMseedFile = getMostRecentMseedFile(rn->scnRing->dir, 
									begFileName);
		if (rn->scnRing->curMseedFile != NULL)
		{
			

 			/* Find the most recent MSEED in this file
			** Get the time right after the last sample
			********************************************/	
			rn->scnRing->timeOnFiles = getMostRecentMseedTime
						(rn->scnRing->dir, rn->scnRing->curMseedFile,
						rn->scnRing->traceRec.sta, rn->scnRing->traceRec.net,
						rn->scnRing->traceRec.chan, &(rn->scnRing->mseedSeqNo));
			if (rn->scnRing->timeOnFiles < 0)
				exit(-7); /* cannot handle the file */
			if  (rn->scnRing->timeOnFiles == 0) 
			{
				rn->scnRing->timeOnFiles = rn->scnRing->tankStarttime + 120;

				/*  Set the date boundary for the file name changing
				 ****************************************/
				rn->scnRing->newFileJulTime =  findNewFileJulTime (rn->scnRing->timeOnFiles);
				/* rn->scnRing->newFileJulTime = 0; was a bug IGD 01/03/01 */
			}
			else
			{
				char *testFileName;
				testFileName = createBUDFileName(rn->scnRing->dir, 
							begFileName, rn->scnRing->timeOnFiles);
				/* IGD 05/10/02 
				 * The only sitution when the strcmp() below does not work is
				 *  presumably when we read the time on file from a fully completed
				 * file and setting the next MSEED should be set to the next day 
				 */
				if (strcmp(rn->scnRing->curMseedFile, testFileName) != 0)
				{	
					
					free(rn->scnRing->curMseedFile);
	 				rn->scnRing->curMseedFile = createFirstMseedFile (rn->scnRing->dir, 
							begFileName, rn->scnRing->timeOnFiles);
					rn->scnRing->mseedSeqNo = 1;
					logit ("pt", "New file name is available for writing due to change of date: %s\n",
						rn->scnRing->curMseedFile);

				}

				free(testFileName); 
				
				rn->scnRing->prevEndtime = rn->scnRing->timeOnFiles;
				/* Set the date boundary for the file name changing
				 **********************************************/	
				rn->scnRing->newFileJulTime =  findNewFileJulTime (rn->scnRing->timeOnFiles);

				logit ("pt", "setMseedFiles found the most recent file %s/%s for %s %s %s\n",
					rn->scnRing->dir, rn->scnRing->curMseedFile, 
					rn->scnRing->traceRec.sta, rn->scnRing->traceRec.net,
					rn->scnRing->traceRec.chan);

					logit ("pt", "%s %s %s timeFromFile set to %s\n",
					rn->scnRing->traceRec.sta, rn->scnRing->traceRec.net,
					rn->scnRing->traceRec.chan, strDate(rn->scnRing->timeOnFiles, d20));
			}
		}				
		else
		{ 

			/* No valid files are found: create a new file
			********************************************/
			/* If a user-requested cutoff time is
			 * later than the beginning of the tank, we set the FILE NAME to 
		 	 * the user-defined date (from configuration)
			 * Otherwise use tankStartime
		 	 ******************************/
			if (rn->julStartTime > rn->scnRing->tankStarttime)   /* fix IGD 01/04/01 */
	 			rn->scnRing->curMseedFile = createFirstMseedFile (rn->scnRing->dir, 
							begFileName, rn->julStartTime);
			else
				rn->scnRing->curMseedFile = createFirstMseedFile (rn->scnRing->dir, 
							begFileName, rn->scnRing->tankStarttime);
			if (rn->scnRing->curMseedFile == (char *) NULL)
				exit (-6); /* Fatal: could not create a file */

			logit("pt", "setMseedFiles created new file %s/%s\n", 
						rn->scnRing->dir, rn->scnRing->curMseedFile);

			/*we will start 2 minutes after the beginning of
			* the tank : otherwise the data could be removed from the tank
                        *****************************************************/  
			rn->scnRing->timeOnFiles = rn->scnRing->tankStarttime + 120;
		}

		/* A last check: if a user-requested cutoff time is
		 * later than the timeOnFiles, we set the timeOnFile to 
		 * the user-defined time (from configuration)
		 ******************************/
		if (rn->julStartTime > rn->scnRing->timeOnFiles)
			rn->scnRing->timeOnFiles = rn->julStartTime; 	

		/* Here we have enough info to set the requestesed start time 
		 * for the first data snippet                                          	
		 ***********************************************************/

		rn->scnRing->traceRec.reqStarttime = getReqStartTime (rn->scnRing->tankStarttime,
								rn->scnRing->timeOnFiles);
		
		/* We now check for the following:
		 * Data file has exist, but the data in the tank are for the day after the file date
		 * We need to start a new file in this case
		 ********************************************/	
		if (rn->scnRing->newFileJulTime > 0 &&  
			rn->scnRing->traceRec.reqStarttime >= rn->scnRing->newFileJulTime) /*IGD was just = before */
		{
			free(rn->scnRing->curMseedFile);
			rn->scnRing->curMseedFile = ew2mseedUpdateFileName 
				(&(rn->scnRing->traceRec), rn->scnRing->dir, rn->scnRing->locId);
			rn->scnRing->mseedSeqNo = 1;
			logit ("pt", "New file name is available for writing: %s\n",
				rn->scnRing->curMseedFile);

		}
		
		

		/* From the reqStarttime we find the gregorian second
		 * to start the new file (date boundary)
		 * We only change the file once a day
		 **********************************************/
		rn->scnRing->newFileJulTime = findNewFileJulTime (rn->scnRing->traceRec.reqStarttime);
		
							 
		rn->scnRing = rn->scnRing->next;
	}
	
	rn->scnRing = scn_head;
	free (begFileName); 
 	return EW_SUCCESS;
  }

/****************************************************************************************/

  int checkNetDirs (RINGS *rn)
  {	
	char *netName, *staName, *mainDir ;
  	int i;
	struct SCN_ring *scn_head;

	scn_head = rn->scnRing;
	mainDir = rn->MseedDir;

	/* IGD 02/21/02 Bug fixed : was rn->sc_avail, however the unavalable
	 * stations were not removed from SCN loop yet, that caused the last
	 * few stations to never create corresponding files. Thanks, Tim and
         * Sandy
         */ 
  	for (i = 0 ; i < rn->SCN_num ; i++) 
  	{	
		netName = (char *) calloc(1, strlen(mainDir) +
					 strlen(rn->scnRing->traceRec.net) + 2);
		sprintf(netName, "%s/%s", mainDir, rn->scnRing->traceRec.net);
		if (make_dir(netName, 0777) < 0 && errno != EEXIST)
		{
			logit ("pt", "ew2mseed_files: cannot create directory %s\n",
				netName);
			fprintf (stderr, "ew2mseed_files fatal error: cannot create directory %s\n",
				netName);
			free (netName);
			perror ("Error message \n");
			return EW_FAILURE;
		}
		if (errno != EEXIST)
			logit("pt", "ew2mseed_files: created directory %s\n", netName);	
		staName  = (char *) calloc(1, strlen(netName) +
					 strlen(rn->scnRing->traceRec.sta) + 2);
		sprintf(staName, "%s/%s", netName, rn->scnRing->traceRec.sta);
		if (make_dir(staName, 0777) < 0 && errno != EEXIST)
		{
			logit ("pt", "ew2mseed_files: cannot create directory %s\n",
				staName);
			fprintf (stderr, "ew2mseed_files fatal error: cannot create directory %s\n",
				staName);
			perror ("Error message is ");
			return EW_FAILURE;
		}
		if (errno != EEXIST)
			logit("pt", "ew2mseed_files: created directory %s\n", staName);	
		rn->scnRing->dir = (char *)calloc(1, strlen(staName) + 1);
		strcpy(rn->scnRing->dir, staName);
		free(netName); free(staName);
		rn->scnRing = rn->scnRing->next;		
	}

	rn->scnRing = scn_head;
  	return EW_SUCCESS;
  }  
/*************************************************************************************/
  int make_dir (char *dirname, int mode)
  {
  	int status;
 	 while((status = mkdir (dirname, mode)) == -1 && errno == EINTR)
		/*do nothing , just loop */  ;	/* EINTR - interrupted fucntion call */

  	return(status);
  }

/*************************************************************************************/
  char *getMostRecentMseedFile (char *dirName, char *nameHead)
  {
	char *fileName = (char *) NULL;
	int retVal;
  	DIR *curDir;
  	struct dirent *curDirContent;
	struct yjd YJD;
	struct yjd master_YJD;	
	struct stat buf;

  	curDir = opendir(dirName);
	if (curDir == NULL)
	{
		logit("pt", "getMostRecentMseedFile fatal error: %s %s\n", 
			strerror(errno), dirName);
		exit(-5);
	}
	master_YJD.year = 0;
	master_YJD.jday = 0;
  	while ((curDirContent = readdir(curDir)) != NULL)
  	{
	        char curPathName[1000];	
		YJD.year=0;
		YJD.jday=0;
		sprintf(curPathName, "%s/%s", dirName, curDirContent->d_name);
		if (isValidMseedName (curDirContent->d_name, &YJD, nameHead) == EW_SUCCESS)

		{
			/* 04/26/02 We want to prevent using the empty files as the valid file names 
			 * The code below inspired by the findings of incorrect MSEED dates in a few 
			 * BUD files pointed by Mary 
			 * 05/09/02 Fixed a bug in using dirent
			 */
			retVal = stat (curPathName, &buf);
			logit("pt", "Processing pathname %s\n", curPathName);
			if (retVal < 0)
			{	
				logit ("pt", "getMostRecentMseedFile: cannot stat %s\n",
					curPathName);
				exit(-6); 
			}
			if (buf.st_size < EW2MSEED_SMALLEST_MSEED)
			{
				logit ("pt", "getMostRecentMseedFile: %s does not contain MSEEDs\n",
					curPathName);
				continue;
			}

			/* 04/26/02 END of new code -> updated 05/09/02 */
			
			if (YJD.year > master_YJD.year)
			{
				master_YJD.year = YJD.year;
				master_YJD.jday = YJD.jday;				
			}				
			else if (YJD.year == master_YJD.year && 
				YJD.jday > master_YJD.jday)
			{
				master_YJD.year = YJD.year;
				master_YJD.jday = YJD.jday;				
			}
			else ;			
		}
	}

	/* close dir
	 ***********************/
 	closedir (curDir);
	
	/*  Construct an output filename
	 ***********************/
	if (master_YJD.year > 0)	
	{
		fileName = (char *) calloc(1, strlen(nameHead) + 10);
		sprintf(fileName, "%s%d.%03d", nameHead, master_YJD.year, master_YJD.jday);
	}
	return(fileName);
  }

/*******************************************************************************/

  int isValidMseedName (char *fileName, struct yjd *YJD, char *nameHead) 
  {
	char *nameTail;
	short year, jday;
	/* Just in case
	 *****************/
	YJD->year = 0;
	YJD->jday = 0;

	if (strncasecmp(fileName, nameHead, strlen(nameHead)) != 0)
		return EW_FAILURE;
	else
	{	
		nameTail = fileName + strlen(nameHead);
		year = (short)(atoi(strtok(nameTail, ".")));
		jday = (short)(atoi(strtok(NULL, ".")));
	
		/* Rudimentary check of the results
		 *******************************/
		if (year < 1000 || year > 3000)
			return EW_FAILURE;
		if (jday < 1 || jday > 365)
			return EW_FAILURE;

		/* If we are here we are OK
		 *************************/
		YJD->year = year;
		YJD->jday = jday;			
	}

	return EW_SUCCESS;
  }
/* Returns the epoch time of the next sample right after the last sample      */
/* in the MSEED file. If the file not found or any other fatal error occurs,  */
/* returns -1; If the file exist, but empty : returns 0			      */
/******************************************************************************/
  double getMostRecentMseedTime (char *dir, char *filename, char *sta, char *net, char *chan, long *seqNo)	
  {	
	char *pathname;
	int retVal;
	int i;
	FILE * fid;
	double startTime;
	double nextRecordTime;
	struct stat *buf;
	SDR_HDR *mseed_hdr;
	void *inBuf;
	int smallestMseedSize = EW2MSEED_SMALLEST_MSEED; /*in bytes: first 64 bytes are always a header */
	int bytesRead;
	short foundFlag = FALSE;
	
	*seqNo = 1;
	buf = (struct stat *) calloc(1, sizeof(struct stat));
	pathname = (char *)calloc(1, strlen(dir) + strlen(filename) + 2);
	
	sprintf(pathname, "%s/%s", dir, filename);
	retVal = stat (pathname, buf);
	if (retVal < 0)
	{
		fprintf(stderr, "getMostRecentMseedTime fatal error: cannot stat %s\n", pathname);
		perror("Error: ");
		logit ("pt",  "getMsotRecentMseedTime fatal error: cannot stat %s\n", pathname);
		free(pathname); free(buf);
		return -1;
	}
	if (buf->st_size < FIXED_DATA_HDR_SIZE) /* file is empty or no valid MSEED records */
	{
		free(pathname); free(buf);
		return (0);

	}
	/* Open the file for reading
	 **************************/
	fid = fopen (pathname, "rb");
	if (fid == NULL)
	{
		fprintf(stderr, "getMostRecentMseedTime fatal error: cannot open %s\n", pathname);
		perror("Error: ");
		logit ("pt",  "getMsotRecentMseedTime fatal error: cannot open %s\n", pathname);
		free(pathname); free(buf);
		return -1;
	}
	inBuf = (void *) calloc(1, FIXED_DATA_HDR_SIZE);
	for (i = 0; i< 9; i++)
	{
		
		if (fseek(fid, -(smallestMseedSize), SEEK_END) == -1)
		{
			fprintf(stderr, "getMostRecentMseedTime fatal error: fseek %s failed\n", pathname);
			perror("Error: ");
			logit ("pt",  "getMsotRecentMseedTime fatal error: fseek %s failed\n", pathname);
			free(pathname); free(buf); free(inBuf); fclose(fid);
			return -1;
		}
		if ((bytesRead = fread(inBuf, 1, FIXED_DATA_HDR_SIZE, fid)) != FIXED_DATA_HDR_SIZE)
		{
			fprintf(stderr, "getMostRecentMseedTime fatal error: fread %s failed\n", pathname);
			if (bytesRead == 0)
				perror("Error: ");
			logit ("pt",  "getMostRecentMseedTime fatal error: fread %s failed\n", pathname);
			free(pathname); free(buf); free(inBuf); fclose(fid);
			return -1;	
		}
 
		mseed_hdr = (SDR_HDR *) inBuf;
		if ((strncmp(mseed_hdr->station_id, sta, 2) == 0)
			&& (strncmp(mseed_hdr->network_id, net, 2) == 0)
			&& (strncmp(mseed_hdr->channel_id, chan, 2) == 0))
			{
				foundFlag = TRUE;
				break;
			}	
		smallestMseedSize = smallestMseedSize * 2;
	}
	/* File has no valid records
	********************************/	
	if (foundFlag == FALSE)
	{
		fclose(fid);
		free (pathname); free(buf); free(inBuf);
		return 0;
	}
	
	
 		
#ifdef _SPARC
	startTime = int_to_tepoch(decode_time_sdr(mseed_hdr->time, SEED_BIG_ENDIAN));
#endif
#ifdef _INTEL
	startTime = int_to_tepoch(decode_time_sdr(mseed_hdr->time, SEED_LITTLE_ENDIAN));
#endif
	nextRecordTime = startTime + ((double) mseed_hdr->num_samples)/mseed_hdr->sample_rate_factor;		
	*seqNo = atol (mseed_hdr->seq_no) + 1;
	
	logit ("pt", "start time of old block %s\n", strDate(startTime, d20));
	logit ("pt", "start time of next block %s\n", strDate(nextRecordTime, d20));

	
	fclose(fid);
	free (pathname); free(buf); free(inBuf);

	return (nextRecordTime);
  }

/***************************************************************************/
  char *createFirstMseedFile (char * dir, char*begName, double jtime)
  {	
	char *fileName = (char *) NULL;
	char *pathName;
	struct Greg *gregDate;
	short jday;
	FILE *newFD;

	double  sec1970 = SEC1970;

	gregDate = (struct Greg *) calloc(1, sizeof (struct Greg));

	datime (jtime + sec1970, gregDate);
	if (gregDate->year < 1000 || gregDate->year > 3000)
	{
		logit("pt", "fatal error in createMseedFile: year %d is out of range\n",
			gregDate->year);
		fprintf(stderr, "fatal error in createMseedFile: year %d is out of range\n",
			gregDate->year);
		free (gregDate);
		return ((char *) NULL);
	}  	

	jday = getJdayFromGreg(gregDate);

	if (jday < 1 || jday > 366 ) /* IGD 01/24/01 Fix 366 is valid once in 4 years*/
	{
		logit("pt", "fatal error in createMseedFile: julian day %d is invalid\n",
			jday);
		fprintf(stderr, "fatal error in createMseedFile: julian day %d is invalid\n",
			jday);
		free (gregDate);
		return ((char *) NULL);
	}
	fileName = (char *) calloc (1, strlen(begName)+15);
	sprintf(fileName, "%s%d.%03d", begName, gregDate->year, jday);
	pathName = (char *) calloc(1, strlen (fileName) + strlen(dir) + 2);
	sprintf(pathName, "%s/%s", dir, fileName);	

	if ((newFD = fopen(pathName, "ab+")) == NULL)
	{
		logit("pt", "fatal error in createMseedFile: cannot open file %s\n",
			pathName);

		fprintf(stderr, "fatal error in createMseedFile: cannot open file %s\n",
			pathName);
		perror("Reason for error: ");
		free (gregDate);
		return ((char *) NULL);
	}
	fclose (newFD);	
	free(gregDate); free(pathName);
	return(fileName);
  }

/***************************************************************************/
  char *createBUDFileName (char * dir, char*begName, double jtime)
  {	
	char *fileName = (char *) NULL;
	char *pathName;
	struct Greg *gregDate;
	short jday;

	double  sec1970 = SEC1970;

	gregDate = (struct Greg *) calloc(1, sizeof (struct Greg));

	datime (jtime + sec1970, gregDate);
	if (gregDate->year < 1000 || gregDate->year > 3000)
	{
		logit("pt", "fatal error in createMseedFile: year %d is out of range\n",
			gregDate->year);
		fprintf(stderr, "fatal error in createMseedFile: year %d is out of range\n",
			gregDate->year);
		free (gregDate);
		return ((char *) NULL);
	}  	

	jday = getJdayFromGreg(gregDate);

	if (jday < 1 || jday > 366 ) /* IGD 01/24/01 Fix 366 is valid once in 4 years*/
	{
		logit("pt", "fatal error in createMseedFile: julian day %d is invalid\n",
			jday);
		fprintf(stderr, "fatal error in createMseedFile: julian day %d is invalid\n",
			jday);
		free (gregDate);
		return ((char *) NULL);
	}
	fileName = (char *) calloc (1, strlen(begName)+15);
	sprintf(fileName, "%s%d.%03d", begName, gregDate->year, jday);
	pathName = (char *) calloc(1, strlen (fileName) + strlen(dir) + 2);
	sprintf(pathName, "%s/%s", dir, fileName);		
	free(gregDate); free(pathName);
	return(fileName);
  }

/***************************************************************/

  char *ew2mseedUpdateFileName(TRACE_REQ *p_trace, char *dir, char *locId)
  {
	char *fileName;
	char *begFileName;
	begFileName = (char *) calloc(1, 40);
	sprintf(begFileName, "%s.%s.%s.%s.",
		p_trace->sta, p_trace->net,
		'-' == locId[0] ? "" : locId, p_trace->chan);
 				fileName = createFirstMseedFile (dir, 
							begFileName, p_trace->reqStarttime);
	free(begFileName);

	return(fileName);
  }

/****************************************************************************/
 short getJdayFromGreg (struct Greg *gregDate)
 {
	short jday;
	struct Greg *gregDateBegYear;
	
	gregDateBegYear = (struct Greg *) calloc(1, sizeof (struct Greg));
		
	gregDateBegYear->year = gregDate->year;
	gregDateBegYear->month = 1;
	gregDateBegYear->day = 1;
	gregDateBegYear->hour = gregDate->hour ;
	gregDateBegYear->minute = gregDate->minute ;
	gregDateBegYear->second = gregDate->second ;

	/* +1 is because if jday == Jan 1 and gregDateBegYear == Jan 1
	 * jday is 0, but it should be 1!!!
	 ********************************/
	jday = (short)(julian(gregDate) - julian (gregDateBegYear) + 1) ;
	free(gregDateBegYear);
	return(jday);
 } 

/******************************************************************************/

  double getReqStartTime (double tankStartTime, double timeFromLastFile)
  {
	if (tankStartTime >= timeFromLastFile)
		return(tankStartTime);
	else
		return(timeFromLastFile);	
   }

/*****************************************************************************/

  int createMseed (struct SCN_ring *scnRing,

				 short verbosity)
  	{
	TRACE_REQ * trace = &(scnRing->traceRec);
	double samprate = scnRing->samprate;
	short mseed_data_type = scnRing->CompAlg;
	int blocksize = scnRing->logRec;
	char *locId = scnRing->locId;
	long *mseedSeqNo = &(scnRing->mseedSeqNo);
	long *gapCount = &(scnRing->gapsCount); 	
	double *oldStarttime = &(scnRing->prevStarttime);
	double *oldEndtime = &(scnRing->prevEndtime); 
	double timeAdvance = scnRing->timeInterval; 
					
	char pathname[500];
	FILE *fid;							
	char *msg_p;  			/* Pointer into tracebuf data */
	char *ms = NULL;		/* MiniSeed output structure */
	char **pp_ms; 			/* Pointer to *ms */ 
	char p_errmsg[1000];	 	/* Error message string */
	char tear_errmsg[1000] = "";		/* Tear error message */


	double prevEndTime;		/* Tracks the endtime obtained from the previous tracebuf wf */
	double afterGapTime;		/* Time after the gap */
	double gapInSec=0;		/* Gap in seconds */
	double newReqStartTime;		/* Variable to compute the new trace->reqStarttime */
	double smallValue;
	double actStarttime;		/* Actual starting time of data we  write in the file */
	double actEndtime; 		/* Actual end time of data we write into the file */

	int retVal;
	int bytesPerSample;		/* How many bytes are in a single sample of WaveServer data */
	int snipCount = 0;		/* Current number of snippets in the input trace->pBuf: not used */
	int shiftInCounts;		/* How many counts between trace->actStarttime and trace->reqStarttime */
	int numSamples = 0;		/* Number of data samples we have read from trace->pBuf so far */
	int n_blocks = 0; 		/* Number of MSEED blocks created */
	int *buffer; 			/* Input data buffer for ms_pack_data() started at trace->actStarttime */
	int *buffer1;			/* The same buffer but started at trace-> reqStarttime */
	int *bufferTmp;			/* Buffer for reallocation*/ 
	long position = 0; 		/* Our current position in the input temp buffer */
	int gapFlag = FALSE;		/* Indicates  we found the gap in the requested trace buffer */
	int overlapFlag = FALSE;	/* Indicates if the overlap is found in the requested trace buffer */ 	
	int writeOut;			/* Return of write */
	int bufferSizeInSamples;	/* A size of allocated temp data buffer new 11/26/01 */

	DATA_HDR *mseed_hdr = NULL;	/*Fixed MiniSEED header from qlib2*/
	TRACE_HEADER *wf;		/* Trace header from trace->pBuf : defined in ew trace_buf.h */ 	
	SDR_HDR* curBlock;		/* MiniSEED standard header for the block we write */
	SDR_HDR* lastBlock;		/* MiniSEED standard header */	
	BS *init_bs = NULL;		/* Temp structure required by ms_pack_data() */ 

	/* Define smallValue as 
	 ***************************/
	smallValue = 1./(samprate * 10.);

	/* Check if we have a proper trace buffer
	 * Something should be seriously wrong if it is not so and we must return 
	 ******************************************************/
	if (!trace)
		return TRACE_REQ_EMPTY;

	/* For the proper work of this function requested time interval
	 *  should be within the actual time interval. If it is not true,
	 * return
 	 ********************************************************/ 
	
	/* Get trace headers  and  snippets
         * If it does not exist, return with error message
	 *****************************************************/
	msg_p = (char *)trace->pBuf;
	if (msg_p == NULL)
		return WS_DATA_BUF_EMPTY;

	/* Here is a header for the first snippet	
	 ********************************************/
	wf = (TRACE_HEADER *) calloc(1, sizeof(TRACE_HEADER));
	memcpy(wf, msg_p, sizeof(TRACE_HEADER));
		

	/* Create and fill fixed MSEED hdr 
	 *************************************/
	mseed_hdr = new_data_hdr();

	/* Fill the mseed_hdr structure 
	 *************************************/   
	memcpy (mseed_hdr->station_id, wf->sta, 6);
	memcpy (mseed_hdr->channel_id, wf->chan, 4); 
	memcpy (mseed_hdr->network_id, wf->net, 3);
	if ('-' == locId[0])
		mseed_hdr->location_id[0] = '\0';
	else
		memcpy (mseed_hdr->location_id, locId, 3);
	
	mseed_hdr->data_type = mseed_data_type;
	mseed_hdr->blksize = blocksize;
	if (samprate >= 1)
		mseed_hdr->sample_rate = samprate;
	else
	{
		mseed_hdr->sample_rate = 1;
		mseed_hdr->sample_rate_mult = -(1./samprate + 0.5);
	}
	mseed_hdr->seq_no = *mseedSeqNo;
	
	/* calloc the tmp integer buffer
	 ***************************************/
	bufferSizeInSamples = samprate * (trace->actEndtime - trace->actStarttime);
	buffer = (int *) calloc( 20 + (int) bufferSizeInSamples, sizeof(int));

	if (buffer == NULL)	/* die if cannot get enough memory */
	{
		logit("pt", "Fatal error: %s %s %s: cannot calloc %d bytes\n",
			trace->sta, trace->net, trace->chan, bufferSizeInSamples * sizeof(int));
		exit(-22);
	}

/* Debug
	fprintf(stderr, "Buffer size = %d\n", 
	 20 + (int) (samprate * (trace->actEndtime - trace->actStarttime)) * sizeof(int));
*/
	/* How many bytes per sample?
         ***********************************************/
	bytesPerSample = atoi (&wf->datatype[1]);

	/* Loop over snippets and fill the integer buffer 	
	 **************************************************/
	while (1)
	{
		memcpy(wf, msg_p, sizeof(TRACE_HEADER));
		ew2mseed_SwapInt (&(wf->pinno), wf->datatype[0]); /* IGD 01/26/01 Add this line */
		ew2mseed_SwapInt (&(wf->nsamp), wf->datatype[0]);
		ew2mseed_SwapDouble (&(wf->samprate), wf->datatype[0]);
		ew2mseed_SwapDouble (&(wf->starttime), wf->datatype[0]);
		ew2mseed_SwapDouble (&(wf->endtime), wf->datatype[0]);
		msg_p = msg_p + sizeof(TRACE_HEADER); /* Pointer to data */	
		/*
		fprintf(stderr, "nsamp = %d; starttime = %f endtime = %f samp = %f\n",
			wf->nsamp, wf->starttime, wf->endtime, wf->samprate);
		*/
		
		/* IGD 03/11/03 Sanity check */
		if (wf->endtime  > (trace->actEndtime + 10))
		{
			logit("pt", "WARNING: bogus wfdisk record tossed: <%s %s %s> wf end time <%s> is after trace endtime <%s>\n",
				wf->sta, 
				wf->net,
				wf->chan,
				strDate(wf->endtime, d20), 
				strDate(trace->actEndtime, d20a));
 
			msg_p += (int) (bytesPerSample * wf->nsamp);
			if ((long) msg_p >= (long) trace->actLen + (long) trace->pBuf) 
				break;
			else 
				continue;
		} 
		/* If this is not the beginning of data
		 *	we check the trace for the gaps and -since Jan. 18, 2000 - overlaps
		 ******************************************/			
		if (snipCount != 0)
		{
			if ((gapInSec = checkForGaps (prevEndTime, wf->starttime,  samprate)) > 0)	
			{	
				/* Congratulations, we discovered a tear! 
				 *****************************************/	
				afterGapTime = wf->starttime;

				/* If shiftInCounts is < 0, trace->reqStarttime < wf->starttime,  
				 *******************************************/
			 	if ((shiftInCounts = getBufferShift ( trace->reqStarttime, wf->starttime, samprate)) < 0)
				{
					/* Previous End time is also after requested time
					 ********************************************/	
					if (getBufferShift ( trace->reqStarttime, prevEndTime, samprate) < 0)
					{
						/* Snippet starttime is before trace->reqEndTime
						 *********************************************/	
						if  (getBufferShift ( wf->starttime, trace->reqEndtime , samprate) < 0)
						{
							/* If we are here, we probably have some data to write in MSEED record
							 * Increment the gapcount, set the gapflag and break the loop 
							 ***********************************************************************/
						 	gapFlag = TRUE;	
							break;									
						}		
						else
						{
							/* This is a case the snippet startime is after our requested end time
							 * Just set flag to correctly set the future reqTime and break
							 *********************************************************/
							gapFlag = TRUE;
							break; 						
						}
					}
					else
					{
						/* This is a case when the tear occurs across the reqStartTime point
						 * PrevEndTime is before reqStartTime, while the snippet starttime is after
						 * We should reset the position pointer, set trace->actStarttime, 
						 * trace->reqStarttime to wf->starttime and log the gap
						 * We should should not set the gap flag
						********************************************************/
 							position = 0L;
							trace->reqStarttime = afterGapTime;
							trace->actStarttime = afterGapTime;
 							/* Do not break ! */							
					}
				}	
				else	
				{
					/* PrevEndTime and wf->startime are both before 
					 * the trace->reqStarttime: this is the gap we don't care about
					 * Reset the position and trace->actStarttime and proceed
					   ***********************************************/
					position = 0L;
					trace->actStarttime = afterGapTime;	
				}
			}
			/* Overlap case: toss this snippet
			 ********************************/
			if (gapInSec < 0)
			{
				overlapFlag = TRUE; /* to skip this snippet competely */
				sprintf (tear_errmsg, "OVERLAP in %s %s %s (%f s) prevEndTime %s; curSnippetStartTime is %s\n",
		  				wf->sta, wf->net, wf->chan, gapInSec, 
						strDate(prevEndTime, d20), strDate(wf->starttime, d20a));
			/*	fprintf (stderr, "%s", tear_errmsg ); */
				logit ("pt", "%s", tear_errmsg);
				sprintf (tear_errmsg, "OVERLAP in %s %s %s: Snippet <%s : %s > is discarded\n",
						wf->sta, wf->net, wf->chan, 
						strDate(wf->starttime, d20), strDate(wf->endtime , d20a));
				logit ("pt", "%s", tear_errmsg);
			/*	fprintf (stderr, "%s", tear_errmsg ); */
			}
		}  /*end snipCount !=0 */

		/* Add data to the buffer: no gaps or don't-care-gaps so far 
		**********************************/
		if (overlapFlag == FALSE)
		{
			/* IGD 11/26/01 Check for overflow of the buffer increase the buffer size conditionally */
			if ((position + wf->nsamp) > bufferSizeInSamples + 20)
			{
				logit ("pt", "Warning: reallocating buffer from %d bytes to %d bytes: potential data problems\n",
					(bufferSizeInSamples + 20) * sizeof(int), trace->actLen);
				if (trace->actLen > bufferSizeInSamples + 20)
				{
					bufferTmp = (int *) calloc (sizeof(int), bufferSizeInSamples * 2);
					bufferSizeInSamples *= 2;
				}
				else
				{	 
					bufferTmp = (int *) calloc(1, trace->actLen); 
					bufferSizeInSamples = (int) (trace->actLen/sizeof(int));
				}
				if (bufferTmp == NULL)	/* die if cannot get enough memory */
				{
					logit("pt", "Fatal error: %s %s %s: cannot calloc %d bytes\n",
					trace->sta, trace->net, trace->chan, trace->actLen);
					exit(-22);
				}

				memcpy (bufferTmp, buffer, position * sizeof(int));
				free(buffer);
				buffer = bufferTmp;

				
			}
			position = ew2mseedAddData (buffer, msg_p, position, wf->nsamp, wf->datatype[0], bytesPerSample );
/* debug
			fprintf(stderr, "position %ld %d\n", position, wf->nsamp * sizeof(int));
*/
			if (position == -1) 
				return WRONG_DATA_TYPE;
			

			prevEndTime = wf->endtime;
			snipCount++;		
		}
		else /* if overlapFlag == TRUE reset it */
			overlapFlag = FALSE;
		
		msg_p += (int) (bytesPerSample * wf->nsamp);
		if ((long) msg_p >= (long) trace->actLen + (long) trace->pBuf) 
			break;
	} /*end of while */

	shiftInCounts = getBufferShift (trace->reqStarttime, trace->actStarttime, samprate);
	if (shiftInCounts == -1) 
	{
		fprintf(stderr, "Algorithm error: shifting Count == -1\n");
		exit (-11);
	}

	buffer1 = buffer + shiftInCounts;
	numSamples = position - shiftInCounts;
	
	

	/* If our data buffer contains data
	 *	(1) Set time in mseed_hdr
	 *	(2) Pack data in mseed fromat
	 *	(3) Update the current file 
	 *************************************/
	
	
	if (numSamples > 0)	
	{
		mseed_hdr->begtime = tepoch_to_int (trace->actStarttime + (double) shiftInCounts / samprate);
		mseed_hdr->hdrtime = tepoch_to_int (trace->actStarttime + (double) shiftInCounts / samprate);
		
		/*shorten number of samples to fit in a single day volume */
		if ((scnRing->newFileJulTime - (trace->actStarttime + (double) shiftInCounts / samprate)) * samprate < numSamples)
		{
			int newNumSamples;
			newNumSamples = (scnRing->newFileJulTime - (trace->actStarttime + (double) shiftInCounts / samprate)) * samprate + 1;
			if (newNumSamples < numSamples)
				numSamples = newNumSamples;
			if (newNumSamples < 0)
				numSamples = 0;
			/* IGD 12/21/09 Disable gapFlag if we switch the date */
			gapFlag = FALSE;
		}

/*		ms = (char *) calloc(10, mseed_hdr->blksize); */
		ms = (char *)NULL;
		pp_ms = &ms;
/*		pp_ms = NULL; */
		retVal = ms_pack_data (mseed_hdr, init_bs, numSamples , buffer1, &n_blocks,   
				pp_ms, (int) (10 * mseed_hdr->blksize),  p_errmsg);
 	
		if (MS_ERROR != retVal) /*IGD 17-Jul-2006 Check for ms_pack_data retVal */
		{
	 		sprintf(pathname, "%s/%s", scnRing->dir, scnRing->curMseedFile);
			fid = fopen (pathname, "rb+");
			if (fid == NULL)
			{
				logit("pt", "processSingleSCN: cannot open file %s\n", pathname);
				return CANNOT_OPEN_FILE;
			}
		
			if (verbosity > 2) logit ("pt", "processSingleSCN: opened file %s for update\n", pathname);

			/* Update the MSEED file 
			****************************/
			fseek (fid, 0L, SEEK_END);
			/* Since the last record is usually not filled completely
		 	* we are not writing it if we created more than one MSEED record
		 	************************************************************/	
 
			if (n_blocks > 1)	
 				writeOut = 
				    fwrite ((const void *)ms, (size_t)mseed_hdr->blksize, (size_t) n_blocks - 1, fid);
			else
				writeOut = fwrite ((const void *)ms, (size_t)mseed_hdr->blksize, (size_t) n_blocks, fid);

			fclose(fid);
			curBlock = (SDR_HDR *) ms;	
		}
		else /* error in ms_pack_data */
		{
			logit("pt", "Failed to ms_pack_data() for %s %s %s %s\n",  
				wf->sta, wf->net, wf->chan, strDate(trace->actStarttime + (double) shiftInCounts / samprate, d20));
			n_blocks = 0;
		}
	}

			
	if (n_blocks > 1)
	{	
		*mseedSeqNo = *mseedSeqNo + n_blocks -1 ;
		lastBlock = (SDR_HDR* )(ms + blocksize * (n_blocks - 1));
#ifdef _SPARC
		newReqStartTime = int_to_tepoch(decode_time_sdr(lastBlock->time, SEED_BIG_ENDIAN));
#endif
#ifdef _INTEL
		newReqStartTime = int_to_tepoch(decode_time_sdr(lastBlock->time, SEED_LITTLE_ENDIAN));
#endif
		actEndtime = 	newReqStartTime - 1./samprate;
	}
	else if (n_blocks == 1)
	{
		*mseedSeqNo = *mseedSeqNo + 1;

		/* If gapFlag is true
		 * we set the newReqStartTime to time right after the Gap + 1 sample
		 * whereas actEndtime is computed as a last sample on MSEED record
		 ****************************************************************/
#ifdef _SPARC
		actEndtime = int_to_tepoch(decode_time_sdr(curBlock->time, SEED_BIG_ENDIAN)) + 
				(double)(curBlock->num_samples -1)/samprate;	
#endif
#ifdef _INTEL
		actEndtime = int_to_tepoch(decode_time_sdr(curBlock->time, SEED_LITTLE_ENDIAN)) + 
				(double)(curBlock->num_samples -1)/samprate;	
#endif
		if (gapFlag == TRUE)	
			newReqStartTime  = afterGapTime + 1./samprate;
		else		
			newReqStartTime = actEndtime + 1./samprate; 
		
	}
	else /* No blocks at all ! */
	{
		logit("pt", "No MSEED blocks saved, move time pointer by 1 sample\n"); 
		newReqStartTime = trace->reqStarttime + 10./samprate;
	}

 	trace->reqStarttime = newReqStartTime;
	trace->reqEndtime = newReqStartTime - 1./samprate + timeAdvance; /*fix 01/09/01 */

	if(n_blocks > 0)
        {
#ifdef _SPARC
		actStarttime = 	int_to_tepoch(decode_time_sdr(curBlock->time, SEED_BIG_ENDIAN));
#endif
#ifdef _INTEL
		actStarttime = 	int_to_tepoch(decode_time_sdr(curBlock->time, SEED_LITTLE_ENDIAN));
#endif

		/* See if we have any TEARS?
	 	*************************************/
		if (*oldEndtime != -1) /* If prevEndtime is defined */
		{
			if (fabs(*oldEndtime - actStarttime) > (2./samprate))
			{
				/* There is a tear, protocol it 
		 		****************************/
				sprintf (tear_errmsg, "TEAR #%ld in %s %s %s (%f s from %s to %s)\n",
		  				*gapCount, wf->sta, wf->net, wf->chan, actStarttime - *oldEndtime, 
						strDate(*oldEndtime, d20), strDate(actStarttime, d20a));
/*				fprintf (stderr, "%s", tear_errmsg ); */
				logit ("pt", "%s", tear_errmsg);
				*gapCount = *gapCount + 1;
			} 

		}
		*oldStarttime = actStarttime;
		*oldEndtime = actEndtime;

		logit("pt", "<%ld> %s %s %s: %d  MSEED records: <%s -- %s>\n",
				*mseedSeqNo, trace->sta, trace->net, 
				trace->chan, (n_blocks == 1) ? n_blocks: n_blocks -1,
				 strDate (actStarttime, d20), strDate(actEndtime, d20a));
	}
	else
		logit("pt", "CreateMseed <%ld> %s %s %s: No MSEED records written this time\n", *mseedSeqNo,
			trace->sta, trace->net, trace->chan);	
	if (verbosity > 2) logit("pt", "%s %s %s: New WS req time set to: <%s -- %s>\n",
			trace->sta, trace->net, trace->chan, 
	strDate (trace->reqStarttime, d20),
	strDate  (trace->reqEndtime, d20a));

		
	/* Free temp structures 
	
 **************************************/
	if (mseed_hdr != NULL)
		free (mseed_hdr); 
	if (wf != NULL)
		free (wf);
	if (buffer != NULL)
		free(buffer);
	if (ms != NULL)
        	free (ms);
			
	return WS_ERR_NONE;
  }

