/*
 * seisan_report.c
 *
 * Watches for TYPE_HYP2000ARC type messages saying that the sausage has located an event.
 * Extracts the parameters needed to write a seisan s-file but waits long enough that
 * trig2disk will have written a wavefile.  This allows the wavefile  name to be included
 * in the s-file when it is finally written out.
 *
 * Also watches for TYPE_MAGNITUDE messages from localmag.  Parameters from these will be
 * added to the appropriate s-file - which should not yet have been written at that point.
 *
 * If requested can write a TYPE_EVENT_ALARM message to ring for alarm modules to pick up.
 * This only happens upon receipt of a TYPE_MAGNITUDE message and so if locmag isn't
 * running no alarms will be sent.
 *
 * Originally based on menlo_report.c.
 * Written by David Scott (BGS)
 * Magnitude and wavefile name added Richard Luckett (BGS) 2007/6
 *
 */

/* VERSION number introduced by Paul F EW 7.7 Sept 4, 2013 */

#define VERSION "0.0.4 2013.09.09"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WINNT
#include <io.h>
#else
#include <dirent.h>
#endif
#include <time.h>
#include <earthworm.h>
#include <kom.h>
#include <transport.h>
#include <rw_mag.h>
#include <read_arc.h>
#include <chron3.h>
#include <fleng.h>

#define MAX_EVENTS             5       /* Number of events for which details are remembered */
#define FILE_NAME_LEN        150       /* Number of chars in name of sfiles */
#define SFILE_LINE_LEN        80       /* Line length for seisan S file */
#define MAX_PHASE            100       /* Maximum number of phases for an event */
#define QID_LEN               11       /* Length of qid in ARC message */
#define AGENCY_LEN             4       /* Length of agency in sfile */
#define MESSAGE_LEN          150       /* Length of messages put on ring */
#define HUGE_TIME      4294967295      /* Time bigger than any possible */

/* Structures to hold the details from an event
 **********************************************/
struct sta_mag {
    char sta[TRACE_STA_LEN];
    char chan[TRACE_CHAN_LEN];
    double amp;
    int used; /* Flag raised when channel has been written to sfile */
};

struct event_details {
    char qid[QID_LEN];
    time_t detect_time;
    struct Hsum hyp;
    double local_mag;
    struct Hpck pha[MAX_PHASE];
    int nphase;
    struct sta_mag stamag[MAX_PHASE];
    int nstamag;
};

/* Functions in this source file
 *******************************/
void seisan_report_hinvarc(char *msg);
int seisan_report_magnitude(char *msg, int msgLen);
void seisan_report_sfile(struct event_details* event);
void seisan_report_alarm(struct event_details* event);
void seisan_report_config(char *configfile);
int seisan_report_lookup(void);
void seisan_report_status(unsigned char type, short ierr, char *note);

/* Things to read from configuration file
 ****************************************/
#define NUM_COMMANDS 11                   /* Number of required commands in config file */
static char RingName[MAX_RING_STR]; /* Name of transport ring for i/o             */
static char MyModName[MAX_MOD_STR]; /* Speak as this module name/id               */
static int LogSwitch; /* 0 if no logfile should be written          */
static int Debug; /* If not 0 write diagnostic messages to log  */
static long HeartBeatInterval; /* Seconds between heartbeats                 */
static long WriteDelay; /* Seconds to wait between detecting an event and saving it */
static char SfileDir[MAX_DIR_LEN]; /* Directory to write sfiles to               */
static char WavDir[MAX_DIR_LEN]; /* Directory where wavefiles are              */
static char Agency[AGENCY_LEN]; /* 3 letter code for agency                   */
static int AlarmMsg; /* If 0 don't put alarm message on ring       */
static int UseMSEEDArchive = 0; /* RSL 2011.03: 0 do not use; 1 BUD; 2 SCP*/

#define   MAXLOGO   2
MSG_LOGO GetLogo[MAXLOGO]; /* Array for requesting module,type,instid */
short nLogo;

/* Things to look up in the earthworm.h tables with getutil.c functions
 **********************************************************************/
static long RingKey; /* Key of transport ring for i/o      */
static unsigned char InstId; /* Local installation id              */
static unsigned char MyModId; /* Module Id for this program         */
static unsigned char TypeHeartBeat;
static unsigned char TypeError;
static unsigned char TypeHyp2000Arc;
static unsigned char TypeMagnitude;
static unsigned char TypeAlarm; /* Type added by RL - read by event_alarm */

/* Error messages used by seisan_report
 *************************************/
#define  ERR_MISSMSG       0   /* message missed in transport ring       */
#define  ERR_TOOBIG        1   /* retreived msg too large for buffer     */
#define  ERR_NOTRACK       2   /* msg retreived; tracking limit exceeded */
static char Text[150]; /* string for log/error messages          */

static SHM_INFO Region; /* shared memory region to use for i/o    */
pid_t MyPid; /* Hold our process ID to be sent with heartbeats */

struct event_details event[MAX_EVENTS]; /* array of remembered events */
int event_index; /* current position in array  */

/* Main program starts here
 **************************/
int main(int argc, char **argv) {
    static char eqmsg[MAX_BYTES_PER_EQ]; /* array to hold event message    */
    long timeNow; /* current time                   */
    long timeLastBeat; /* time last heartbeat was sent   */
    long recsize; /* size of retrieved message      */
    MSG_LOGO reclogo; /* logo of retrieved message      */
    int res, i;

    /* Check command line arguments
     ******************************/
    if (argc != 2) {
        fprintf(stderr, "Usage: seisan_report <configfile>\n");
        fprintf(stderr, "Version: seisan_report %s\n", VERSION);
        exit(EW_FAILURE);
    }

    /* Initialize name of log-file & open it
     ***************************************/
    logit_init(argv[1], 0, 256, 1);

    /* Read the configuration file(s)
     ********************************/
    seisan_report_config(argv[1]);

    /* Set logit to LogSwitch read from configfile.
     *********************************************/
    logit_init(argv[1], 0, 256, LogSwitch);
    logit("", "seisan_report: Read command file <%s>\n", argv[1]);
    logit("", "seisan_report: Version %s\n", VERSION);

    /* Look up important info from earthworm.h tables
     ************************************************/
    if (seisan_report_lookup() != EW_SUCCESS) {
        logit("e", "seisan_report: Call to seisan_report_lookup failed \n");
        exit(EW_FAILURE);
    }

    /* Get our process ID
     ********************/
    if ((MyPid = getpid()) == -1) {
        logit("e", "seisan_report: Call to getpid failed. Exiting.\n");
        exit(EW_FAILURE);
    }

    /* Initialise array of events
     ***************************/
    for (i = 0; i < MAX_EVENTS; i++) {
        event[i].detect_time = (time_t) HUGE_TIME;
        strcpy(event[i].qid, "");
    }

    /* Attach to Input/Output shared memory ring
     *******************************************/
    tport_attach(&Region, RingKey);
    logit("", "seisan_report: Attached to public memory region %s: %d\n", RingName, RingKey);

    /* Force a heartbeat to be issued in first pass thru main loop
     *************************************************************/
    timeLastBeat = time(&timeNow) - HeartBeatInterval - 1;

    /* Flush the incomming transport ring
     *************************************/
    while (tport_getmsg(&Region, GetLogo, nLogo, &reclogo, &recsize, eqmsg, sizeof (eqmsg) - 1) != GET_NONE);


    /*----------------- setup done; start main loop ----------------------*/

    while (tport_getflag(&Region) != TERMINATE && tport_getflag(&Region) != MyPid) {
        /* Send seisan_report's heartbeat */
        if (time(&timeNow) - timeLastBeat >= HeartBeatInterval) {
            timeLastBeat = timeNow;
            seisan_report_status(TypeHeartBeat, 0, "");

            /* Ceck to see if any events are old enough to write */
            for (i = 0; i < MAX_EVENTS; i++)
                if ((timeNow - event[i].detect_time) > WriteDelay) {
                    seisan_report_sfile(&event[i]);
                    event[i].detect_time = (time_t) HUGE_TIME;
                    strcpy(event[i].qid, "");
                }
        }

        /* Process new hypoinverse archive and magnitude msgs */
        do {
            /* Get the next message from shared memory */
            res = tport_getmsg(&Region, GetLogo, nLogo, &reclogo, &recsize, eqmsg, sizeof (eqmsg) - 1);

            /* Check return code; report errors if necessary */
            if (res != GET_OK) {
                if (res == GET_NONE) {
                    break;
                } else if (res == GET_TOOBIG) {
                    sprintf(Text, "Retrieved msg[%ld] (i%u m%u t%u) too big for eqmsg[%ld]",
                            recsize, reclogo.instid, reclogo.mod, reclogo.type, sizeof (eqmsg) - 1);
                    seisan_report_status(TypeError, ERR_TOOBIG, Text);
                    continue;
                } else if (res == GET_MISS) {
                    sprintf(Text, "Missed msg(s)  i%u m%u t%u  %s.", reclogo.instid, reclogo.mod, reclogo.type, RingName);
                    seisan_report_status(TypeError, ERR_MISSMSG, Text);
                } else if (res == GET_NOTRACK) {
                    sprintf(Text, "Msg received (i%u m%u t%u); transport.h NTRACK_GET exceeded",
                            reclogo.instid, reclogo.mod, reclogo.type);
                    seisan_report_status(TypeError, ERR_NOTRACK, Text);
                }
            }

            /* Process new message (res==GET_OK,GET_MISS,GET_NOTRACK) */
            eqmsg[recsize] = '\0';

            if (reclogo.type == TypeHyp2000Arc) {
                if (Debug)
                    logit("t", "seisan_report: received TypeHyp2000Arc message\n");
                seisan_report_hinvarc(eqmsg);
            } else if (reclogo.type == TypeMagnitude) {
                if (Debug)
                    logit("t", "seisan_report: received TypeMagnitude message\n");
                i = seisan_report_magnitude(eqmsg, recsize + 1);

                if (AlarmMsg && i >= 0)
                    seisan_report_alarm(&event[i]);
            }

        } while (res != GET_NONE); /* End of message-processing-loop */

        sleep_ew(1000); /* No more messages; wait for new ones to arrive */
    }

    /*-----------------------------end of main loop-------------------------------*/

    /* Termination has been requested
     ********************************/
    tport_detach(&Region);
    logit("t", "seisan_report: Termination requested; exiting!\n");
    exit(EW_SUCCESS);
}

/**************************************************************************
 *	seisan_report_hinvarc
 *
 *	Input:	*arcmsg   - pointer to message taken off ring by main loop.
 *
 *	Process a Hypoinverse archive message and populate structure to
 *      hold the parameters in memory.
 *
 **************************************************************************/
void seisan_report_hinvarc(char *arcmsg) {
    char *line, *shdw;
    int i;

    /* Allocate memory for lines read from message */
    line = calloc(200, sizeof (char));
    shdw = calloc(200, sizeof (char));

    /* Get first two lines from message - second is called the shadow */
    if ((line = strtok(arcmsg, "\n")) == NULL) {
        logit("et", "seisan_report: Trouble reading first line of arcmsg\n");
        return;
    }
    if ((shdw = strtok(NULL, "\n")) == NULL) {
        logit("et", "seisan_report: Trouble reading second line of arcmsg\n");
        return;
    }
    if (Debug)
        logit("", "hypocentre: %s\n", line);

    /* Use function from read_arc.c to read params into structure */
    if (read_hyp(line, shdw, &event[event_index].hyp)) {
        logit("et", "seisan_report: Trouble parsing hyp lines arcmsg\n");
        return;
    }

    /* Put QID in structure seperately */
    sprintf(event[event_index].qid, "%ld", event[event_index].hyp.qid);

    /* Read phases using another read_arc.c function */
    i = 0;
    while ((line = strtok(NULL, "\n")) != NULL) {
        if (Debug)
            logit("", "phase %d: %s\n", i, line);

        if ((shdw = strtok(NULL, "\n")) == NULL) {
            logit("et", "seisan_report: Phase %d only has one line\n", i);
            return;
        }
        if (read_phs(line, shdw, &event[event_index].pha[i])) {
            logit("et", "seisan_report: Trouble parsing phase %d\n", i);
            return;
        }
        if (++i == MAX_PHASE) {
            logit("et", "seisan_report: More than %d phase readings in message\n", MAX_PHASE);
            return;
        }
    }
    /* Final line and shadow just have the QID again */
    event[event_index].nphase = i - 1;

    /* Add current time to structure to show when event was detected */
    event[event_index].detect_time = time(NULL);

    /* Clear magnitude fields until magtype message comes alaong */
    event[event_index].local_mag = 0;
    event[event_index].nstamag = 0;

    if (Debug)
        logit("t", "seisan_report: Read %d phases for event %s\n", event[event_index].nphase, event[event_index].qid);

    /* Add to array of remembered events */
    if (++event_index == MAX_EVENTS)
        event_index = 0;

    return;
}

/**************************************************************************
 *	seisan_report_magnitude
 *
 *	Input:	*arcmsg   - pointer to message taken off ring by main loop.
 *               msgLen   - length of the message
 *
 *      Return:  i - index on event array of event this magnitude belongs to
 *                   returns -1 on error
 *
 *	Process a magnitude message and add parameters to event structure.
 *
 **************************************************************************/
int seisan_report_magnitude(char *msg, int msgLen) {
    MAG_INFO LocMag;
    MAG_CHAN_INFO *pStaMag;
    int i, j;

    /* Allocate array of structures for channel info */
    LocMag.pMagAux = calloc(MAX_PHASE, sizeof (MAG_CHAN_INFO));
    LocMag.size_aux = MAX_PHASE * sizeof (MAG_CHAN_INFO);

    /* Read params from message using fubctions from rw_mag.c */
    if (rd_mag(msg, msgLen, &LocMag)) {
        logit("et", "seisan_report: problem parsing TYPE_MAGNITUDE message\n");
        free(LocMag.pMagAux);
        return -1;
    }
    if (Debug)
        logit("t", "seisan_report: magnitude found: %3.1fL%s n=%d qid=%s\n", LocMag.mag, Agency, LocMag.nchannels, LocMag.qid);

    /* Find which recent event this magnitude belongs to */
    for (i = 0; i < MAX_EVENTS; i++)
        if (strcmp(event[i].qid, LocMag.qid) == 0)
            break;
    if (i == MAX_EVENTS) {
        logit("et", "seisan_report: no event in memory with qid=%s\n", LocMag.qid);
        free(LocMag.pMagAux);
        return -1;
    }

    /* Add magnitude to event structure */
    event[i].local_mag = LocMag.mag;

    /* Add channel info (amplitude) to event structure */
    event[i].nstamag = LocMag.nchannels;
    for (j = 0; j < event[i].nstamag; j++) {
        pStaMag = (MAG_CHAN_INFO*) (LocMag.pMagAux + j * sizeof (MAG_CHAN_INFO));
        strcpy(event[i].stamag[j].sta, pStaMag->sta);
        strcpy(event[i].stamag[j].chan, pStaMag->comp);
        event[i].stamag[j].used = 0;

        /* localmag either gives z2p in amp1 or min and max of p2p in amp1 and amp2 */
        if (pStaMag->Amp2 == MAG_NULL) /* MAG_NULL is -1 */
            event[i].stamag[j].amp = pStaMag->Amp1 * 2;
        else
            event[i].stamag[j].amp = pStaMag->Amp2 - pStaMag->Amp1;

        /* Convert from micrometres to nanometres for s-file */
        event[i].stamag[j].amp *= 1000;

        if (Debug)
            logit("", "%s %s %.4f\n",
                event[i].stamag[j].sta, event[i].stamag[j].chan, event[i].stamag[j].amp);
    }

    free(LocMag.pMagAux);
    return i;
}


#define WAV_FILE_COMPARE_STRING_SIZE 16
/**************************************************************************
 *	seisan_report_sfile
 *
 *	Input:	*ep   - pointer to event structure with parameters to write.
 *
 *     Write an s-file to disk.
 *     This is done WriteDelay seconds after arc message is received to
 *     allow wavefile to be written so that its name can be put in sfile.
 *
 **************************************************************************/
void seisan_report_sfile(struct event_details* ep) {
    FILE *fp_s;

#ifdef _WINNT
    WIN32_FIND_DATA findData; /* Structure for directory entries */
    HANDLE fileHandle; /* Handle of first file for FindNextFile */
    char findFile[FILE_NAME_LEN]; /* Path/filename pattern to look for */
#else
    DIR *pDir;
    struct dirent *pDirEnt;
#endif
    time_t timeNow;
    struct tm utc_time;
    struct Greg ot, at, wt; /* Greg structure for times is in read_arc.h */
    char sfile_name[FILE_NAME_LEN];
    char wavefileRoot1[FILE_NAME_LEN];
    char wavefileRoot2[FILE_NAME_LEN];
    char wavefileName[FILE_NAME_LEN];
    double timeWav; /* Time in seconds since 1600 like read_arc times */
    int i, j;
    int refDuration;
    struct Greg rt; //Structure for reference time

    if (Debug)
        logit("t", "seisan_report: Writing sfile for event %s\n", ep->qid);

    /* Put origin time into structure - epoch is 1600 not 1900 */
    ot = *datime(ep->hyp.ot, &ot);

    /* Open S-file */
    sprintf(sfile_name, "%s/%02d-%02d%02d-%02dR.S%4d%02d", SfileDir, ot.day, ot.hour, ot.minute, (int) ot.second, ot.year, ot.month);
    if ((fp_s = fopen(sfile_name, "w")) == (FILE *) NULL) {
        logit("et", "seisan_report: error opening file <%s>\n", sfile_name);
        return;
    }

    /* Write line type 1 */
    fprintf(fp_s, " %4d %2d%2d %2d%2d %4.1f R %7.3f%8.3f%5.1f  %3s%3d%4.1f ",
            ot.year, ot.month, ot.day, ot.hour, ot.minute, ot.second, ep->hyp.lat,
            ep->hyp.lon, ep->hyp.z, Agency, ep->nphase, ep->hyp.rms);

    /*RSL 2011.03: Changed to include duration magnitude*/
    if (ep->hyp.Md == 0.0) {
        if (ep->local_mag)
            fprintf(fp_s, "%3.1fL%s                1\n", ep->local_mag, Agency);
        else
            fprintf(fp_s, "                       1\n");
    } else {
        if (ep->local_mag)
            fprintf(fp_s, "%3.1fL%s%3.1fC%s         1\n", ep->local_mag, Agency, ep->hyp.Md, Agency);
        else
            fprintf(fp_s, "%3.1fC%s                1\n", ep->hyp.Md, Agency);
    }

    /* Write ID line */
    time(&timeNow);
    utc_time = *gmtime(&timeNow);
    fprintf(fp_s, " ACTION:NEW %02d-%02d-%02d %02d:%02d OP:ew   STATUS:               ID:%4d%02d%02d%02d%02d%02d     I\n",
            utc_time.tm_year % 100, utc_time.tm_mon + 1, utc_time.tm_mday, utc_time.tm_hour, utc_time.tm_min,
            ot.year, ot.month, ot.day, ot.hour, ot.minute, (int) ot.second);

    /* RSL 2011.03: Write special comment line with the binder ID */
    fprintf(fp_s," BINDERID: %10ld                                                          3\n", ep->hyp.qid);

    /* RSL 2011.03: Skip this part if using reference to mseed archives */
    if (UseMSEEDArchive==0) {

    /* Write line type 6, wavefile name usually based on time of first phase less 30 secs */
    timeWav = HUGE_TIME;
    for (i = 0; i < ep->nphase; i++)
        if (ep->pha[i].Pat < timeWav)
            timeWav = ep->pha[i].Pat;
    timeWav -= 30;
    wt = *datime(timeWav, &wt);
    sprintf(wavefileRoot1, "%4d-%02d-%02d-%02d%02d-%02dS", wt.year, wt.month, wt.day, wt.hour, wt.minute, (int) wt.second);

    /* Sometimes (too few phases?) name based on last phase less 30 seconds */
    timeWav = 0;
    for (i = 0; i < ep->nphase; i++)
        if (ep->pha[i].Pat > timeWav && (ep->pha[i].Plabel == 'P' || ep->pha[i].Ponset == 'P')) {
            timeWav = ep->pha[i].Pat;
	} else {
            timeWav = ep->pha[i].Sat;
	}
    timeWav -= 30;
    wt = *datime(timeWav, &wt);
    sprintf(wavefileRoot2, "%4d-%02d-%02d-%02d%02d-%02dS", wt.year, wt.month, wt.day, wt.hour, wt.minute, (int) wt.second);
    strcpy(wavefileName, "");

    /* Need to read directory to find complete filename - no other way to know number of chans */
#ifdef _WINNT

    /* Windows looks for pattern so could do this differently but make it similar to UNIX */
    sprintf(findFile, "%s/*", WavDir);
    if ((fileHandle = FindFirstFile(findFile, &findData)) == INVALID_HANDLE_VALUE) {
        logit("et", "seisan_report: error opening dir <%s>\n", WavDir);
        return;
    }
    do {

        if (strncmp(wavefileRoot1, findData.cFileName, WAV_FILE_COMPARE_STRING_SIZE) == 0) {
            strcpy(wavefileName, findData.cFileName);
            break;
        }
        if (strncmp(wavefileRoot2, findData.cFileName, WAV_FILE_COMPARE_STRING_SIZE) == 0) {
            strcpy(wavefileName, findData.cFileName);
            break;
        }
    } while (FindNextFile(WavDir, &findData));

    FindClose(fileHandle);

#else

    if ((pDir = opendir(WavDir)) == NULL) {
        logit("et", "seisan_report: error opening dir <%s>\n", WavDir);
        return;
    }
    while ((pDirEnt = readdir(pDir)) != NULL) {
        if (strncmp(wavefileRoot1, pDirEnt->d_name, WAV_FILE_COMPARE_STRING_SIZE) == 0) {
            strcpy(wavefileName, pDirEnt->d_name);
            break;
        }
        if (strncmp(wavefileRoot2, pDirEnt->d_name, WAV_FILE_COMPARE_STRING_SIZE) == 0) {
            strcpy(wavefileName, pDirEnt->d_name);
            break;
        }
    }
    closedir(pDir);

#endif

    if (strcmp(wavefileName, "") == 0) {
        logit("et", "seisan_report: cant find wavefile starting %s or %s\n", wavefileRoot1, wavefileRoot2);
    } else {
        if (Debug)
            logit("", "seisan_report: wavefile found <%s>\n", wavefileName);
        fprintf(fp_s, " %s                                                 6\n", wavefileName);
    }
    } else {
        /* RSL 2011.03 Using mseed archive references... Note that these are not verified in any way */
        double refStart = HUGE_TIME; /*Set this to the earliest start of a phase*/
        double latestEnd = 0; /*Set this to the latest end of all the phases*/
        for (i = 0; i < ep->nphase; i++) {
            if (ep->pha[i].Pat<refStart) {
                refStart = ep->pha[i].Pat;
            }
            if ((ep->pha[i].Pat + ep->pha[i].codalen) > latestEnd) {
                latestEnd = ep->pha[i].Pat + ep->pha[i].codalen;
            }
            //printf("DATA: phaseStart: %f codaLen: %d refStart: %f latestEnd: %f\n", ep->pha[i].Pat, ep->pha[i].codalen, refStart, latestEnd);
        }
        refStart -= 30; //30 seconds margin of the start
        refDuration = (int)(latestEnd + 15 - refStart); //15 seconds margin to coda length
        rt = *datime(refStart, &rt);

        for (i = 0; i < ep->nphase; i++) {
            /* Produce reference line for each station*/
            fprintf(fp_s, " %s %-5s %3s %2s %2s %4d %2d%2d %2d%2d %2d %4d                                    6\n",
                (UseMSEEDArchive==1)?"BUD":"SCP",
                ep->pha[i].site, ep->pha[i].comp, ep->pha[i].net, 
                (strcmp(ep->pha[i].loc,"--") == 0)?"  ":ep->pha[i].loc,
                rt.year, rt.month, rt.day, rt.hour, rt.minute, (int) rt.second,
                refDuration);
        }

    }

    /* Phase header */
    fprintf(fp_s, " STAT SP IPHASW D HRMM SECON CODA AMPLIT PERI AZIMU VELO SNR AR TRES W  DIS CAZ7\n");

    /* Phase lines */
    for (i = 0; i < ep->nphase; i++) {
        /* Put arrival time into structure - epoch is 1600 not 1900 */
        if (ep->pha[i].Ponset == 'P' || ep->pha[i].Plabel == 'P') {
        	at = *datime(ep->pha[i].Pat, &at);
	} else {
        	at = *datime(ep->pha[i].Sat, &at);
	}

        /* Look out for phase in day after event */
        if (ot.day != at.day) {
            if (ot.day == at.day - 1)
                at.hour += 24;
            else {
                logit("et", "seisan_report: something odd about time of phase %d\n", i);
                return;
            }
        }
        if (ep->pha[i].Ponset == 'P' || ep->pha[i].Plabel == 'P') {
            fprintf(fp_s, " %-5s%c%c %c%c%c  %1d %c %02d%2d %5.2f                                   %5.1f  %5.0f %3d \n",
                ep->pha[i].site, ep->pha[i].comp[0], ep->pha[i].comp[2], 'I', ep->pha[i].Ponset, ep->pha[i].Plabel,
		ep->pha[i].Pqual,
                ep->pha[i].Pfm, at.hour, at.minute, at.second, ep->pha[i].Pres, ep->pha[i].dist, ep->pha[i].azm);
	} else {
            fprintf(fp_s, " %-5s%c%c %c%c%c  %1d %c %02d%2d %5.2f                                   %5.1f  %5.0f %3d \n",
                ep->pha[i].site, ep->pha[i].comp[0], ep->pha[i].comp[2], ' ', ep->pha[i].Sonset, ep->pha[i].Slabel,
		ep->pha[i].Squal,
                ep->pha[i].Sfm, at.hour, at.minute, at.second, ep->pha[i].Sres, ep->pha[i].dist, ep->pha[i].azm);
	}

        /* Add extra channels for this station with amplitude */
        for (j = 0; j < ep->nstamag; j++)
            if (strcmp(ep->pha[i].site, ep->stamag[j].sta) == 0 && ep->stamag[j].used == 0) {
                fprintf(fp_s, " %-5s%c%c                          %6.1f                              %5.0f %3d \n",
                        ep->stamag[j].sta, ep->stamag[j].chan[0], ep->stamag[j].chan[2], ep->stamag[j].amp,
                        ep->pha[i].dist, ep->pha[i].azm);
                ep->stamag[j].used = 1;
            }
    }

    /* Add any amplitude lines for channels which weren't picked */
    for (i = 0; i < ep->nstamag; i++)
        if (ep->stamag[i].used == 0)
            fprintf(fp_s, " %-5s%c%c                          %6.1f                                        \n",
                ep->stamag[i].sta, ep->stamag[i].chan[0], ep->stamag[i].chan[2], ep->stamag[i].amp);

    /* Phase lines must be followed by a blank line */
    fprintf(fp_s, "\n");

    fclose(fp_s);
    if (Debug)
        logit("t", "seisan_report: sfile written <%s>\n", sfile_name);

    return;
}

/**************************************************************************
 *	seisan_report_alarm
 *
 *	Input:	*ep   - pointer to event structure with parameters to write.
 *
 *     Put a TYPE_EVENT_ALARM message onto ring for other modules to act on.
 *     Does this for any event with a magnitude.
 *
 **************************************************************************/
void seisan_report_alarm(struct event_details* ep) {
    struct Greg ot; /* Greg structure for times is in read_arc.h */
    char *grname[36]; /* Flinn-Engdahl region name */
    char alarmText[150];

    /* Get Flinn-Engdahl geographical region name */
    FlEngLookup(ep->hyp.lat, ep->hyp.lon, grname, NULL);

    /* Put origin time into structure - epoch is 1600 not 1900 */
    ot = *datime(ep->hyp.ot, &ot);

    /* Write message */
    sprintf(alarmText, "%-36s %3.1f %+6.2f %+6.2f %d/%02d/%02d %02d:%02d %3d %3.1f", *grname, ep->local_mag,
            ep->hyp.lat, ep->hyp.lon, ot.year, ot.month, ot.day, ot.hour, ot.minute, ep->nphase, ep->hyp.rms);

    seisan_report_status(TypeAlarm, 0, alarmText);

    if (Debug)
        logit("t", "seisan_report: alarm written to ring:\n\t<%s>\n", alarmText);

    return;
}

/**************************************************************************
 *	seisan_report_config
 *
 *	Input:	*configfile - name of main command file to read.
 *
 *	Processes command file(s) using kom.c functions.
 *
 **************************************************************************/
void seisan_report_config(char *configfile) {
    char init[NUM_COMMANDS]; /* init flags, one byte for each required command */
    int nmiss; /* number of required commands that were missed   */
    char *com;
    char *str;
    int nfiles;
    int success;
    int i;

    /* Set to zero one init flag for each required command
     *****************************************************/
    for (i = 0; i < NUM_COMMANDS; i++)
        init[i] = 0;
    nLogo = 0;

    /* Open the main configuration file
     **********************************/
    nfiles = k_open(configfile);
    if (nfiles == 0) {
        logit("e", "seisan_report: Error opening command file <%s>; exiting!\n", configfile);
        exit(-1);
    }

    /* Process all command files
     ***************************/
    while (nfiles > 0) /* While there are command files open */ {
        while (k_rd()) /* Read next line from active file  */ {
            com = k_str(); /* Get the first token from line */

            /* Ignore blank lines & comments
             *******************************/
            if (!com) continue;
            if (com[0] == '#') continue;

            /* Open a nested configuration file
             **********************************/
            if (com[0] == '@') {
                success = nfiles + 1;
                nfiles = k_open(&com[1]);
                if (nfiles != success) {
                    logit("e",
                            "seisan_report: Error opening command file <%s>; exiting!\n",
                            &com[1]);
                    exit(-1);
                }
                continue;
            }                /* Process anything else as a command
                 ************************************/

                /*0*/ else if (k_its("MyModuleId")) {
                str = k_str();
                if (str) strcpy(MyModName, str);
                init[0] = 1;
            } /*1*/ else if (k_its("RingName")) {
                str = k_str();
                if (str) strcpy(RingName, str);
                init[1] = 1;
            } /*2*/ else if (k_its("HeartBeatInterval")) {
                HeartBeatInterval = k_long();
                init[2] = 1;
            } /*3*/ else if (k_its("LogFile")) {
                LogSwitch = k_int();
                init[3] = 1;
            } /*4*/ else if (k_its("Debug")) {
                Debug = k_int();
                init[4] = 1;
            } /*5*/ else if (k_its("WriteDelay")) {
                WriteDelay = k_long();
                init[5] = 1;
            }/* Enter installation & module to get event messages from
             ********************************************************/
                /*6*/ else if (k_its("GetEventsFrom")) {
                if (nLogo + 1 >= MAXLOGO) {
                    logit("e",
                            "seisan_report: Too many <GetEventsFrom> commands in <%s>",
                            configfile);
                    logit("e", "; max=%d; exiting!\n", (int) MAXLOGO / 2);
                    exit(-1);
                }
                if ((str = k_str())) {
                    if (GetInst(str, &GetLogo[nLogo].instid) != 0) {
                        logit("e",
                                "seisan_report: Invalid installation name <%s>", str);
                        logit("e", " in <GetEventsFrom> cmd; exiting!\n");
                        exit(-1);
                    }
                    GetLogo[nLogo + 1].instid = GetLogo[nLogo].instid;
                }
                if ((str = k_str())) {
                    if (GetModId(str, &GetLogo[nLogo].mod) != 0) {
                        logit("e",
                                "seisan_report: Invalid module name <%s>", str);
                        logit("e", " in <GetEventsFrom> cmd; exiting!\n");
                        exit(-1);
                    }
                    GetLogo[nLogo + 1].mod = GetLogo[nLogo].mod;
                }
                if (GetType("TYPE_HYP2000ARC", &GetLogo[nLogo].type) != 0) {
                    logit("e",
                            "seisan_report: Invalid message type <TYPE_HYP2000ARC>");
                    logit("e", "; exiting!\n");
                    exit(-1);
                }
                if (GetType("TYPE_MAGNITUDE", &GetLogo[nLogo + 1].type) != 0) {
                    logit("e",
                            "seisan_report: Invalid message type <TYPE_MAGNITUDE>");
                    logit("e", "; exiting!\n");
                    exit(-1);
                }
                nLogo += 2;
                init[6] = 1;
            }/* Enter name of local directory to write files to
             *************************************************/
                /*7*/ else if (k_its("SfileDir")) {
                str = k_str();
                if (str) {
                    if (strlen(str) >= MAX_DIR_LEN) {
                        logit("e", "seisan_report: SfileDir <%s> too long; ", str);
                        exit(-1);
                    }

                    strcpy(SfileDir, str);
                    init[7] = 1;
                }
            }/* Enter name of directory where wave files are
             *********************************************/
                /*8*/ else if (k_its("WavDir")) {
                str = k_str();
                if (str) {
                    if (strlen(str) >= MAX_DIR_LEN) {
                        logit("e", "seisan_report: SfileDir <%s> too long; ", str);
                        exit(-1);
                    }
                    strcpy(WavDir, str);
                    init[8] = 1;
                }
            }/* Agency code for locations and magnitudes
             *****************************************/
                /*9*/ else if (k_its("Agency")) {
                str = k_str();
                if (str) {
                    if (strlen(str) >= AGENCY_LEN) {
                        logit("e", "seisan_report: Agency <%s> too long; ", str);
                        exit(-1);
                    }
                    strcpy(Agency, str);
                    init[9] = 1;
                }
            } /*10*/ else if (k_its("AlarmMsg")) {
                AlarmMsg = k_int();
                init[10] = 1;
            }/* RSL 2011.03: Optional commands */
            else if (k_its("UseMSEEDArchive")) {
                UseMSEEDArchive = k_int();
                if (UseMSEEDArchive < 0 || UseMSEEDArchive > 2) {
                    logit("e", "seisan_report: Invalid MSEED archive format %d\n", UseMSEEDArchive);
                    exit(-1);
                }
                if (UseMSEEDArchive != 0) {
                    logit("o", "seisan_report: Using MSEED archive reference format\n");
                }
            }/* Unknown command     *****************/
            else {
                logit("e", "seisan_report: <%s> Unknown command in <%s>.\n", com, configfile);
                continue;
            }

            /* See if there were any errors processing the command
             *****************************************************/
            if (k_err()) {
                logit("e", "seisan_report: Bad <%s> command in <%s>; exiting!\n", com, configfile);
                exit(-1);
            }
        }
        nfiles = k_close();
    }

    /* After all files are closed, check init flags for missed commands
     ******************************************************************/
    nmiss = 0;
    for (i = 0; i < NUM_COMMANDS; i++)
        if (!init[i]) nmiss++;
    if (nmiss) {
        logit("e", "seisan_report: ERROR, no ");
        if (!init[0]) logit("e", "<MyModuleId> ");
        if (!init[1]) logit("e", "<RingName> ");
        if (!init[2]) logit("e", "<HeartBeatInterval> ");
        if (!init[3]) logit("e", "<LogFile> ");
        if (!init[4]) logit("e", "<Debug> ");
        if (!init[5]) logit("e", "<WriteDelay> ");
        if (!init[6]) logit("e", "<GetEventsFrom> ");
        if (!init[7]) logit("e", "<SfileDir> ");
        if (!init[8]) logit("e", "<WavDir> ");
        if (!init[9]) logit("e", "<Agency> ");
        if (!init[10]) logit("e", "<AlarmMsg> ");
        logit("e", "command(s) in <%s>; exiting!\n", configfile);
        exit(-1);
    }

    return;
}

/**************************************************************************
 *	seisan_report_lookup
 *
 *	Look up important info from earthworm.h tables.
 *
 **************************************************************************/
int seisan_report_lookup(void) {
    /* Look up keys to shared memory regions
     *************************************/
    if ((RingKey = GetKey(RingName)) == -1) {
        logit("e", "seisan_report:  Invalid ring name <%s>; exiting!\n", RingName);
        return (EW_FAILURE);
    }

    /* Look up installations of interest
     *********************************/
    if (GetLocalInst(&InstId) != 0) {
        logit("e", "seisan_report: error getting local installation id; exiting!\n");
        return (EW_FAILURE);
    }

    /* Look up modules of interest
     ***************************/
    if (GetModId(MyModName, &MyModId) != 0) {
        logit("e", "seisan_report: Invalid module name <%s>; exiting!\n", MyModName);
        return (EW_FAILURE);
    }

    /* Look up message types of interest
     *********************************/
    if (GetType("TYPE_HEARTBEAT", &TypeHeartBeat) != 0) {
        logit("e", "seisan_report: Invalid message type <TYPE_HEARTBEAT>; exiting!\n");
        return (EW_FAILURE);
    }
    if (GetType("TYPE_ERROR", &TypeError) != 0) {
        logit("e", "seisan_report: Invalid message type <TYPE_ERROR>; exiting!\n");
        return (EW_FAILURE);
    }
    if (GetType("TYPE_HYP2000ARC", &TypeHyp2000Arc) != 0) {
        logit("e", "seisan_report: Invalid message type <TYPE_HYP2000ARC>; exiting!\n");
        return (EW_FAILURE);
    }
    if (GetType("TYPE_MAGNITUDE", &TypeMagnitude) != 0) {
        logit("e", "seisan_report: Invalid message type <TYPE_MAGNITUDE>; exiting!\n");
        return (EW_FAILURE);
    }
    if (GetType("TYPE_EVENT_ALARM", &TypeAlarm) != 0) {
        logit("e", "seisan_report: Invalid message type <TYPE_EVENT_ALARM>; exiting!\n");
        return (EW_FAILURE);
    }
    return (EW_SUCCESS);
}

/*****************************************************************************
 * seisan_report_status
 *
 *	Input:	type - message type to send.
 *          ierr - error code, only used if type is TypeError.
 *          note - string to put in message.
 *
 *  Builds a message & puts it into shared memory.
 *
 *****************************************************************************/
void seisan_report_status(unsigned char type, short ierr, char *note) {
    MSG_LOGO logo;
    char msg[256];
    long size;
    long t;

    /* Build the message
     *******************/
    logo.instid = InstId;
    logo.mod = MyModId;
    logo.type = type;

    time(&t);

    if (type == TypeHeartBeat) {
        sprintf(msg, "%ld %d\n", t, MyPid);
    } else if (type == TypeError) {
        sprintf(msg, "%ld %hd %s\n", t, ierr, note);
        logit("et", "seisan_report: %s\n", note);
    } else if (type == TypeAlarm) {
        sprintf(msg, "%ld %s\n", t, note);
    }

    size = strlen(msg); /* don't include the null byte in the message */

    /* Write the message to shared memory
     ************************************/
    if (tport_putmsg(&Region, &logo, size, msg) != PUT_OK) {
        if (type == TypeHeartBeat) {
            logit("et", "seisan_report:  Error sending heartbeat.\n");
        } else if (type == TypeError) {
            logit("et", "seisan_report:  Error sending error:%d.\n", ierr);
        } else if (type == TypeAlarm) {
            logit("et", "seisan_report:  Error sending alarm:%d.\n", ierr);
        }
    }
    return;
}


