/******************************************************************************
 *                                EWHTMLEMAIL                                 *
 *                                                                            *
 * Simplified graphical email alert for earthworm using google APIs.          *
 *                                                                            *
 *                                                                            *
 * Description:                                                               *
 * Produces an web file (html) containing graphical information on detected   *
 * earthquakes. The presented graphical information consists on a map of the  *
 * computed hypocenter and reporting stations as well as the seismic traces   *
 * retrieved from a WaveServer. The graphical information is produced using   * 
 * google maps and google charts APIs. As a consequence, correct visualization*
 * of the web file requires an internet connection. The web files may be sent *
 * to a set of recipients via email. In this case, ewhtmlemail uses           *
 * sendmail, which is common with several linux distributions.                *
 * The code is a combination of seisan_report to retrieve hyp2000arc          *
 * messages, and gmew to access and retrieve data from a set of waveservers.  *
 * The code includes custom functions to generate the requests to the google  *
 * APIs and embeded these on the output html file.                            *
 * Besides the typical .d configuration file, ewhtmlemail requires a css file *
 * with some basic style configurations to include in the output html.        *
 *                                                                            *
 * Written by R. Luis from CVARG in July, 2011                                *
 * Contributions and testing performed by Jean-Marie Saurel, from OVSM/IPGP   *
 *                                                                            *
 *****************************************************************************/


/* Includes
 **********/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include <earthworm.h>
#include <transport.h>
#include <ws_clientII.h>
#include <trace_buf.h>
#include <swap.h>
#include <kom.h>
#include <read_arc.h>
#include <site.h>
#include <unistd.h>


/* Defines
 *********/
#define MAX_STATIONS 200
#define MAX_GET_SAMP 700
#define MAX_GET_CHAR 2500
#define MAX_WAVE_SERVERS 10
#define MAX_ADDRESS 80
#define MAX_PORT 6
#define MAX_EMAIL_RECIPIENTS 20
#define MAX_EMAIL_CHAR 60


/* Structures
 ************/
 
// Waveservers 
typedef struct {
   char wsIP[MAX_ADDRESS];
   char port[MAX_PORT];
} WAVESERV;

// Email Recipients
// TODO: Upgrade to have different parameters for each email recipient
typedef struct {
   char address[MAX_EMAIL_CHAR];
} EMAILREC;


/* Functions in this file
 ************************/
void config(char*);
void lookup(void);
void status( unsigned char, short, char* );
int process_message(char*, int);
void GoogleMapRequest(char*, int*, SITE**, int, double, double);
void GoogleChartRequest(char*, int*, double*, int, int, char, int);
char simpleEncode(int);
int getWSData(SITE*, double, double, double*, int);
int searchSite(char*, char*, char*, char*);
char* getWSErrorStr(int errorCode, char* msg);

/* Globals
 *********/
static SHM_INFO   InRegion; // shared memory region to use for input
static pid_t      MyPid;    // Our process id is sent with heartbeat

/* Things to read or derive from configuration file
 **************************************************/
static int        LogSwitch;              // 0 if no logfile should be written 
static long       HeartbeatInt;           // seconds between heartbeats        
static long       MaxMessageSize = 4096;  // size (bytes) of largest msg     
static int        Debug = 0;              // 0=no debug msgs, non-zero=debug    
static MSG_LOGO  *GetLogo = NULL;         // logo(s) to get from shared memory  
static short      nLogo = 0;              // # of different logos
static int        MAX_SAMPLES = 60000;    // Number of samples to get from ws 
static char       ipAdr[16];              // Waveserver IP 
static char       port[6];                // Waveserver Port  
static long       wstimeout = 5;          // Waveserver Timeout in Seconds  
static char       HTMLFile[200];          // Base name of the out files
static char       EmailProgram[200];      // Path to the email program
static char       StyleFile[200];         // Path to the style (css) file
static int        nwaveservers = 0;       // Number of waveservers
static int        nemailrecipients = 0;   // Number of email recipients
static double     TimeMargin = 10;        // Margin to download samples.
// Array of waveservers
static WAVESERV   waveservers[MAX_WAVE_SERVERS];
// Array of email recipients
static EMAILREC   emailrecipients[MAX_EMAIL_RECIPIENTS];


/* Things to look up in the earthworm.h tables with getutil.c functions
 **********************************************************************/
static long               InRingKey;       // key of transport ring for input
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      TypeTraceBuf2;


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


/* Main program starts here
 **************************/
int main(int argc, char **argv) 
{

   time_t        timeNow;          // current time               
   time_t        timeLastBeat;     // time last heartbeat was sent
   char         *msgbuf;           // buffer for msgs from ring    
   long          recsize;          // size of retrieved message    
   MSG_LOGO      reclogo;          // logo of retrieved message    
   unsigned char seq;
   int           res;


   /* Check command line arguments
    ******************************/
   if (argc != 2) 
   {
      fprintf(stderr, "Usage: ewhtmlemail <configfile>\n");
      return EW_FAILURE;
   }
   

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

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

   /* Lookup important information from earthworm.d
    ***********************************************/
   lookup();
   

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


   /* Get our own process ID for restart purposes
    *********************************************/
   if( (MyPid = getpid()) == -1 )
   {
      logit ("e", "ewhtmlemail: Call to getpid failed. Exiting.\n");
      free( GetLogo );
      exit( -1 );
   }
   

   /* Allocate the message input buffer
    ***********************************/
   if ( !( msgbuf = (char *) malloc( (size_t)MaxMessageSize+1 ) ) )
   {
      logit( "et",
         "ewhtmlemail: failed to allocate %d bytes"
         " for message buffer; exiting!\n", MaxMessageSize+1 );
      free( GetLogo );
      exit( -1 );
   }
   

   /* Attach to shared memory rings
    *******************************/
   tport_attach( &InRegion, InRingKey );
   logit( "", "ewhtmlemail: Attached to public memory region: %ld\n",
      InRingKey );
      

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

   /* Flush the incoming transport ring on startup
    **********************************************/
   while( tport_copyfrom(&InRegion, GetLogo, nLogo,  &reclogo,
      &recsize, msgbuf, MaxMessageSize, &seq ) != GET_NONE );


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


   while ( tport_getflag( &InRegion ) != TERMINATE  &&
      tport_getflag( &InRegion ) != MyPid ) 
   {
      /* send heartbeat
       ***************************/
      if( HeartbeatInt  &&  time(&timeNow)-timeLastBeat >= HeartbeatInt ) 
      {
         timeLastBeat = timeNow;
         status( TypeHeartBeat, 0, "" );
      }


      /* Get msg & check the return code from transport
       ************************************************/
      res = tport_copyfrom( &InRegion, GetLogo, nLogo, &reclogo,
                              &recsize, msgbuf, MaxMessageSize, &seq );
      switch( res )
      {
         case GET_OK:      /* got a message, no errors or warnings         */
            break;

         case GET_NONE:    /* no messages of interest, check again later   */
            sleep_ew(1000); /* milliseconds */
            continue;

         case GET_NOTRACK: /* got a msg, but can't tell if any were missed */
            sprintf( Text,
               "Msg received (i%u m%u t%u); transport.h NTRACK_GET exceeded",
               reclogo.instid, reclogo.mod, reclogo.type );
            status( TypeError, ERR_NOTRACK, Text );
            break;

         case GET_MISS_LAPPED:     /* got a msg, but also missed lots      */
            sprintf( Text,
               "Missed msg(s) from logo (i%u m%u t%u)",
               reclogo.instid, reclogo.mod, reclogo.type );
            status( TypeError, ERR_MISSLAP, Text );
            break;

         case GET_MISS_SEQGAP:     /* got a msg, but seq gap               */
            sprintf( Text,
               "Saw sequence# gap for logo (i%u m%u t%u s%u)",
               reclogo.instid, reclogo.mod, reclogo.type, seq );
            status( TypeError, ERR_MISSGAP, Text );
            break;

         case GET_TOOBIG:  /* next message was too big, resize buffer      */
            sprintf( Text,
               "Retrieved msg[%ld] (i%u m%u t%u) too big for msgbuf[%ld]",
               recsize, reclogo.instid, reclogo.mod, reclogo.type,
               MaxMessageSize );
            status( TypeError, ERR_TOOBIG, Text );
            continue;

         default:         /* Unknown result                                */
            sprintf( Text, "Unknown tport_copyfrom result:%d", res );
            status( TypeError, ERR_TOOBIG, Text );
            continue;
      }

      /* Received a message. Start processing
       **************************************/
      msgbuf[recsize] = '\0'; /* Null terminate for ease of printing */
      
      
      if ( reclogo.type == TypeHYP2000ARC )
         process_message(msgbuf, recsize);

   }

   /* free allocated memory */
   free( GetLogo );
   free( msgbuf  );

   /* detach from shared memory */
   tport_detach( &InRegion );

   /* write a termination msg to log file */
   logit( "t", "ewhtmlemail: Termination requested; exiting!\n" );
   fflush( stdout );
   return( 0 );
}

/*****************************************************************************
 *  config() processes command file(s) using kom.c functions;                *
 *                    exits if any errors are encountered.                   *
 *****************************************************************************/
#define ncommand 7        /* # of required commands you expect to process */
void config(char *configfile) 
{
   char init[ncommand]; /* 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;
   char processor[15];
   int i;

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

   /* Open the main configuration file
    **********************************/
   nfiles = k_open(configfile);
   if (nfiles == 0) 
   {
      logit("e",
         "ewhtmlemail: 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",
                  "ewhtmlemail: Error opening command file <%s>; exiting!\n",
                  &com[1]);
               exit(-1);
            }
            continue;
         }

         /* Process anything else as a command
          ************************************/
   /*0*/ if (k_its("LogFile")) 
         {
            LogSwitch = k_int();
            if (LogSwitch < 0 || LogSwitch > 2) 
            {
               logit("e",
                  "ewhtmlemail: Invalid <LogFile> value %d; "
                  "must = 0, 1 or 2; exiting!\n", LogSwitch);
               exit(-1);
            }
            init[0] = 1;
         } 
   /*1*/ else if (k_its("MyModuleId"))
         {
            if (str = k_str()) {
               if (GetModId(str, &MyModId) != 0) 
               {
                   logit("e",
                      "ewhtmlemail: Invalid module name <%s> "
                      "in <MyModuleId> command; exiting!\n", str);
                   exit(-1);
               }
            }
            init[1] = 1;
            }
   /*2*/ else if (k_its("InRing")) 
         {
            if (str = k_str()) 
            {
               if ((InRingKey = GetKey(str)) == -1) 
               {
                  logit("e",
                     "ewhtmlemail: Invalid ring name <%s> "
                     "in <InRing> command; exiting!\n", str);
                  exit(-1);
               }
            }
            init[2] = 1;
         } 
   /*3*/ else if (k_its("HeartbeatInt"))
         {
            HeartbeatInt = k_long();
            init[3] = 1;
         }
   /*4*/ else if (k_its("GetLogo")) 
         {
            MSG_LOGO *tlogo = NULL;
            tlogo = (MSG_LOGO *)
               realloc(GetLogo, (nLogo + 4) * sizeof (MSG_LOGO));
            if (tlogo == NULL) 
            {
               logit("e", "ewhtmlemail: GetLogo: error reallocing"
                  " %d bytes; exiting!\n",
                  (nLogo + 2) * sizeof (MSG_LOGO));
               exit(-1);
            }
            GetLogo = tlogo;

            if (str = k_str()) 
            {
               if (GetInst(str, &GetLogo[nLogo].instid) != 0) 
               {
                  logit("e",
                     "ewhtmlemail: Invalid installation name <%s>"
                     " in <GetLogo> cmd; exiting!\n", str);
                  exit(-1);
               }
               GetLogo[nLogo + 1].instid = GetLogo[nLogo].instid;
               GetLogo[nLogo + 2].instid = GetLogo[nLogo].instid;
               GetLogo[nLogo + 3].instid = GetLogo[nLogo].instid;
               if (str = k_str()) 
               {
                  if (GetModId(str, &GetLogo[nLogo].mod) != 0) 
                  {
                     logit("e",
                        "ewhtmlemail: Invalid module name <%s>"
                        " in <GetLogo> cmd; exiting!\n", str);
                     exit(-1);
                  }
                  GetLogo[nLogo + 1].mod = GetLogo[nLogo].mod;
                  GetLogo[nLogo + 2].mod = GetLogo[nLogo].mod;
                  GetLogo[nLogo + 3].mod = GetLogo[nLogo].mod;
                  if (GetType("TYPE_HYP2000ARC", &GetLogo[nLogo].type) != 0) 
                  {
                     logit("e",
                        "ewhtmlemail: Invalid message type <TYPE_HYP2000ARC>"
                        "; exiting!\n");
                     exit(-1);
                  }
               }
            }
            nLogo += 4;
            init[4] = 1;
         }
   /*5*/ else if (k_its("Debug")) 
         {
            Debug = k_int();
            init[5] = 1;
         }
         else if ( k_its("MaxMessageSize") ) 
         {
            MaxMessageSize = k_long();
         }
         else if ( k_its("MAX_SAMPLES") ) 
         {
            MAX_SAMPLES = k_int();
         }
         else if ( k_its("WSTimeout") ) 
         {
            wstimeout = k_int() * 1000;
         }
   /*6*/ else if ( k_its("HTMLFile") ) 
         {
            strcpy(HTMLFile, k_str());
            init[6] = 1;
         }
         else if ( k_its("EmailProgram") ) 
         {
            strcpy(EmailProgram, k_str());
         }
         else if ( k_its("StyleFile") ) 
         {
            strcpy(StyleFile, k_str());
         }
         else if ( k_its("TimeMargin") ) 
         {
            TimeMargin = k_val();
         }
         else if ( k_its("WaveServer") )
         {
            if (nwaveservers < MAX_WAVE_SERVERS)
            {
               char cp[80];
               char *token;
               strcpy(cp,k_str());
               strcpy(waveservers[nwaveservers].wsIP, strtok (cp, ":"));
               strcpy(waveservers[nwaveservers].port, strtok (NULL, ":"));
               nwaveservers++;
            }
            else
            {
               logit("e", "ewhtmlemail: Excessive number of waveservers. Exiting.\n");
               exit(-1);
            }
         }
         else if ( k_its("EmailRecipient") )
         {
		    if (nemailrecipients<MAX_EMAIL_RECIPIENTS)
			{
				strcpy(emailrecipients[nemailrecipients++].address, k_str());
			} 
			else
			{
				logit("e", "ewhtmlemail: Excessive number of email recipients. Exiting.\n");
                exit(-1);
			}
         }
         /* Some commands may be processed by other functions
          ***************************************************/
         else if( site_com() )  strcpy( processor, "site_com" );
         /* Unknown command
          *****************/
         else 
         {
            logit("e", "ewhtmlemail: <%s> Unknown command in <%s>.\n",
               com, configfile);
            continue;
         }

         /* See if there were any errors processing the command
          *****************************************************/
         if (k_err())
         {
            logit("e",
               "ewhtmlemail: 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 < ncommand; i++) if (!init[i]) nmiss++;
   if (nmiss) 
   {
      logit("e", "ewhtmlemail: ERROR, no ");
      if (!init[0]) logit("e", "<LogFile> ");
      if (!init[1]) logit("e", "<MyModuleId> ");
      if (!init[2]) logit("e", "<InRing> ");
      if (!init[3]) logit("e", "<HeartbeatInt> ");
      if (!init[4]) logit("e", "<GetLogo> ");
      if (!init[5]) logit("e", "<Debug> ");
      if (!init[6]) logit("e", "<HTMLFile> ");
      logit("e", "command(s) in <%s>; exiting!\n", configfile);
      exit(-1);
   }
   return;
}

/*********************************************************************
 *  lookup( )   Look up important info from earthworm.h tables       *
 *********************************************************************/
void lookup(void) {
    /* Look up installations of interest
     *********************************/
    if (GetLocalInst(&InstId) != 0) {
        logit("e",
                "ewhtmlemail: error getting local installation id; exiting!\n");
        exit(-1);
    }

    /* Look up message types of interest
     *********************************/
    if (GetType("TYPE_HEARTBEAT", &TypeHeartBeat) != 0) {
        logit("e",
                "ewhtmlemail: Invalid message type <TYPE_HEARTBEAT>; exiting!\n");
        exit(-1);
    }
    if (GetType("TYPE_ERROR", &TypeError) != 0) {
        logit("e",
                "ewhtmlemail: Invalid message type <TYPE_ERROR>; exiting!\n");
        exit(-1);
    }
    if (GetType("TYPE_HYP2000ARC", &TypeHYP2000ARC) != 0) {
        logit("e",
                "ewhtmlemail: Invalid message type <TYPE_HYP2000ARC>; exiting!\n");
        exit(-1);
    }
    if (GetType("TYPE_TRACEBUF2", &TypeTraceBuf2) != 0) {
        logit("e",
                "ewhtmlemail: Invalid message type <TYPE_TRACEBUF2>; exiting!\n");
        exit(-1);
    }
    return;
}

/******************************************************************************
 * status() builds a heartbeat or error message & puts it into                *
 *                   shared memory.  Writes errors to log file & screen.      *
 ******************************************************************************/
void status(unsigned char type, short ierr, char *note) {
    MSG_LOGO logo;
    char msg[256];
    long size;
    time_t t;

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

    time(&t);

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

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

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

    return;
}

















/******************************************************************************
 * process_message() Processes a message to find if its a real event or not   *
 ******************************************************************************/
int process_message(char* msgbuf, int recsize) {
   
   struct Hsum sumP;
   struct Hpck pckP;
   double starttime = 0;
   double endtime = 0;

 
   int i, j, pos;                /* Generic counters */
   char *line, *shdw;
   double *trace;                   /* Traces to be plotted */
   int nsamples = MAX_GET_SAMP;  /* Number of samples in each trace */
   char cp[MaxMessageSize+1];    /* Buffer to hold the message */
   SITE *sites[MAX_STATIONS];    //Holds the selected sites
   char phases[MAX_STATIONS];    //Holds the phase types
   double arrivalTimes[MAX_STATIONS]; //Holds the arrival times
   int codaLen;                      //Holds the coda length absolute
   int nsites = 0;
   char system_command[200];
   
   FILE *cssfile;
   FILE *htmlfile;
   char ch;
   char req[MAX_GET_CHAR];
   int  nreq = 0;
   
   time_t ot;
   struct tm *timeinfo;
   char timestr[80];
   char fullFilename[250];
   
   

   /* Get first two lines from message - second is called the shadow
    ****************************************************************/
   strcpy(cp, msgbuf);
   if ((line = strtok(cp, "\n")) == NULL) 
   {
      logit("et", "ewhtmlemail: Trouble reading first line of arcmsg\n");
      return;
   }

   if ((shdw = strtok(NULL, "\n")) == NULL) 
   {
      logit("et", "ewhtmlemail: 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, &sumP)) 
   {
      logit("et", "ewhtmlemail: Trouble parsing hyp lines arcmsg\n");
      return;
   }
   starttime = sumP.ot;
   

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

      if ((shdw = strtok(NULL, "\n")) == NULL) 
      {
         logit("et", "ewhtmlemail: Phase %d only has one line\n", nsites);
         return FALSE;
      }
      
      if (read_phs(line, shdw, &pckP)) 
      {
         logit("et", "ewhtmlemail: Trouble parsing phase %d\n", nsites);
         return FALSE;
      }
      
      
      /* Check if this is a valid channel
       **********************************/
      if (strlen(pckP.site)==0)
         continue;
         
      pos = searchSite(pckP.site, pckP.comp, pckP.net, pckP.loc);
      if (pos == -1)
      {
         logit("et", "ewhtmlemail: Unable to find %s.%s.%s.%s on the site file\n", 
            pckP.site, pckP.comp, pckP.net, pckP.loc);
         continue;
      }      
      
      
      /* New station, store its pointer
       ********************************/
      sites[nsites] = &Site[pos];
      
      /* Select phase type and arrival time
       ************************************/
      if (pckP.Plabel == 'P')
      {
         phases[nsites] = 'P';
         arrivalTimes[nsites] = pckP.Pat - 11676096000.0;
      }
      else
      {
         phases[nsites] = 'S';
         arrivalTimes[nsites] = pckP.Sat - 11676096000.0;
      }
      
      nsites++;
      
          
      /* Update endtime
       ****************/
      codaLen = pckP.codalen;
      if (codaLen<0) codaLen = -codaLen;
      
      if (Debug)
         logit("o", "Coda length: %d\n", pckP.codalen);
         
      if ((pckP.Pat + codaLen) > endtime)   //using P arrival or
         endtime = pckP.Pat + codaLen;       //
      if ((pckP.Sat + codaLen) > endtime)   //using S arrival
         endtime = pckP.Sat + codaLen;
         

      if (nsites >= MAX_STATIONS) 
      {
         logit("et", "ewhtmlemail: More than %d stations in message\n", MAX_STATIONS);
         break;
      }
   }

   
   /* Correct times for epoch 1970
    ******************************/
   starttime -= 11676096000.0;
   starttime -= TimeMargin;
   endtime -= 11676096000.0;
   endtime += TimeMargin;
   
   if (Debug)
   {
      logit("o", "Available channels:\n");
      for (i=0; i<nsites; i++)
         logit("o", "%5s.%3s.%2s.%2s\n",
            sites[i]->name, sites[i]->comp,
            sites[i]->net, sites[i]->loc);
            
      logit("o", "Time margin: %f\n", TimeMargin);
      
      ot = (time_t)starttime;
      timeinfo = localtime ( &ot);
      strftime (timestr,80,"%Y.%m.%d %H:%M:%S",timeinfo);
      logit("o", "Waveform starttime: %s\n", timestr);
      
      ot = (time_t)endtime;
      timeinfo = localtime ( &ot);
      strftime (timestr,80,"%Y.%m.%d %H:%M:%S",timeinfo);
      logit("o", "Waveform endtime:   %s\n", timestr);
   }
   
   
   /* Allocate memory for trace
    ***************************/
   trace = (double*) malloc(MAX_SAMPLES * sizeof(int));
   if (trace==NULL)
   {
      logit("e", "ewhtmlemail: Unable to alocate memory for processed traces\n");
      exit(-1);
   }
   
      
   /* Start html email file
    ***********************/
   sprintf(fullFilename, "%s_%d.html", HTMLFile, sumP.qid );
   if (Debug) logit("ot", "Creating html file %s\n", fullFilename);

   if (htmlfile = fopen(fullFilename, "w"))
   {
      /* Save file header
       ******************/
      if (strlen(EmailProgram)>0)
      {
         if (Debug) logit("ot", "Writing file header\n", fullFilename);
         fprintf(htmlfile, "Subject: ewAlert - Event ID: %d\nContent-Type: text/html\n", sumP.qid);
         fprintf(htmlfile, "<html>\n<head>\n<style type=\"text/css\">");
         if (Debug)
            logit("o", "Subject: ewAlert - Event ID: %d\nContent-Type: text/html\n", sumP.qid);
      }
      
         
      /* Copy content of css file to html file
       ***************************************/   
      if (strlen(StyleFile)>0) 
      {  
         if (Debug) logit("ot", "Copying css file to html header: %s\n", StyleFile);
         if (cssfile=fopen(StyleFile,"r"))
         {
         while(!feof(cssfile)) 
         {
            ch = getc(cssfile);
            if(ferror(cssfile))
            {
               if (Debug) logit("e","Read css error\n");
               clearerr(cssfile);
               break;
            }
            else
            {
               if(!feof(cssfile)) putc(ch, htmlfile);
               if(ferror(htmlfile))
               {
                  if (Debug) logit("e","Write css error\n");
                  clearerr(htmlfile);
                  break;
               }
            }
         }
         fclose(cssfile);
         }
         else
         {
            logit("et","Unable to open css file\n");
         }
      }
      
      
      /* Close html header
       *******************/
      fprintf(htmlfile, "\n</style>\n</head>\n");
      
      
      /* Start email body
       ******************/
      if (Debug) logit("ot", "Starting html body\n");
      fprintf(htmlfile, "<body>\n");
      
      
      /* Create table with reference information
       *****************************************/
      ot = (time_t)(sumP.ot - 11676096000.0);
      timeinfo = localtime ( &ot);
      strftime (timestr,80,"%Y.%m.%d %H:%M:%S",timeinfo);
      fprintf(htmlfile, "<table id=\"DataTable\">\n");
      fprintf(htmlfile, "<tr><th>Location<th><tr>\n");
      fprintf(htmlfile, "<tr class=\"alt\"><td>Origin time</td><td>%s</td><tr>\n",timestr);
      fprintf(htmlfile, "<tr><td>Latitude</td><td>%7.4f</td><tr>\n", sumP.lat);
      fprintf(htmlfile, "<tr class=\"alt\"><td>Longitude</td><td>%8.4f</td><tr>\n", sumP.lon);
      fprintf(htmlfile, "<tr><td>Depth</td><td>%4.1f</td><tr>\n", sumP.z);
      fprintf(htmlfile, "<tr class=\"alt\"><td>Pref. Magnitude</td><td>%4.1f</td><tr>\n", sumP.Mpref);
      fprintf(htmlfile, "</table><br><br>\n");
         
         
      /* Introduce google map with the stations and hypocenter
       *******************************************************/
      if (Debug) logit("ot", "Computing Google map\n");
      GoogleMapRequest(req, &nreq, sites, nsites, sumP.lat, sumP.lon);
      fprintf(htmlfile, "%s\n<br><br>", req);
      
   
      /* Introduce google chars with station traces
       ********************************************/
      if (Debug) logit("ot", "Computing Google charts\n");
      fprintf(htmlfile, "<table id=\"WaveTable\">\n"); 
      fprintf(htmlfile, "<tr><th>Waveforms</th></tr>\n"); 
      for (i=0; i<nsites; i++)
      {
         /* Download trace for the site
          *****************************/
         if (getWSData(sites[i],starttime, endtime, trace, nsamples)!=0)
         {
            logit("t", "ewhtmlemail: Unable to retrieve data from waveserver\n");
            continue;
         }
      
         /* Create trace image request
          ****************************/
         nreq = 0; //Actual number of characters of the request
         GoogleChartRequest(req, &nreq, trace, nsamples, i,
            phases[i],
            (int)((arrivalTimes[i]-starttime)/(endtime-starttime)*100+0.5));
         if (Debug) logit("o", "Produced trace for %s.%s.%s.%s with %d characters\n",
            sites[i]->name, sites[i]->comp,
            sites[i]->net, sites[i]->loc,
            nreq);
      
         /* Add to email file
          *******************/
         fprintf(htmlfile, "<tr class=\"WaveTableTextRowClass\"><td>%5s.%3s.%2s.%2s</td></tr>\n",
            sites[i]->name, sites[i]->comp,
            sites[i]->net, sites[i]->loc);
         fprintf(htmlfile, "<tr class=\"WaveTableTraceRowClass\"><td>%s</td></tr>\n", req); 
      }
      fprintf(htmlfile, "</table>\n");
      
      
      /* Finish file
       *************/
       if (Debug) logit("ot", "Closing file\n");
      fprintf(htmlfile,"</body>\n</html>\n");
      fclose(htmlfile);
      
      
      /* Execute command
       *****************/
      if (strlen(EmailProgram)>0 && nemailrecipients>0)
      {
         logit("ot", "ewhtmlemail: Sending email alert.\n");
         for (i=0; i<nemailrecipients; i++)
         {
            
            sprintf(system_command,"cat %s | %s %s",
               fullFilename, EmailProgram, emailrecipients[i].address);
            system(system_command);
         }
      }
   }
   else
   {
      logit("et", "ewhtmlemail: Unable to write html file\n");
   }
   /* Release memory
    ****************/
   free(trace);
   return FALSE;
}

/*******************************************************************************
 * GoogleMapRequest: Produce a google map request for a given set of stations  *
 *                   and a hypocenter                                          *
 ******************************************************************************/
void GoogleMapRequest(char *request, int *nchars, SITE **sites, int nsites,
   double hypLat, double hypLon)
{
   int i;
   char markerstr[200];

   
   /* Base of the google static map
    *******************************/
   *nchars = sprintf(request, "<img class=\"MapClass\" alt=\"\" src=\"http://maps.google.com/maps/api/staticmap?"
      "size=600x400&format=png8&maptype=hybrid&sensor=false");   

   /* Add icon for hypocenter
    *************************/
   if (hypLat!=0.0 || hypLon!=0.0)
   {                         //         http://      maps.google.com/   mapfiles/   kml/   paddle/   ylw-stars.png
      sprintf(markerstr, "&markers=icon:http:%%2F%%2Fmaps.google.com%%2Fmapfiles%%2Fkml%%2Fpaddle%%2Fylw-stars.png|shadow:true|%f,%f", hypLat, hypLon);
      strcat(request, markerstr);
      *nchars += strlen(markerstr);
   }

   /* Add icons for stations
    ************************/
   sprintf(markerstr,"&markers=icon:http:%%2F%%2Fmaps.google.com%%2Fmapfiles%%2Fkml%%2Fshapes%%2Fplacemark_circle.png|shadow:false");
   strcat(request, markerstr);
   *nchars += strlen(markerstr);
   for (i=0; i<nsites; i++)
   {
      sprintf(markerstr, "|%f,%f", sites[i]->lat, sites[i]->lon);
      if ((*nchars + strlen(markerstr))>=MAX_GET_CHAR)
         break;
      *nchars += strlen(markerstr);
      strcat(request, markerstr);
   }
   
   strcat(request, "\"/>");
   printf("\n");
   
}


/*******************************************************************************
 * makeGoogleChartRequest: Produce a google chary request for a given station  *
 ******************************************************************************/
 /* 
 * Input Parameters
 * request:         String to save the request in 
 * nchars:          Corresponding number of charanters
 * samples:         Array with the samples
 * nsamples:        Number of samples
 * counter:         A counter to return the actual number of characters
 * phaseName:       A character to indicate the name of the phase (P or S)
 * phasePos:        The relative position of the phase label 0%-100%
 */
void GoogleChartRequest(char *request, int *nchars, 
   double *samples, int nsamples, int counter,
   char phaseName, int phasePos) 
{
   int i, intVal;
   double maxSamp = 0;
   
   
   /* Compute maximum of the samples
    ********************************/
   for (i=0; i<nsamples; i++)
      if (samples[i]>maxSamp)
      {
         maxSamp = samples[i];
      }
      else if (-samples[i]>maxSamp)
      {
         maxSamp = -samples[i];
      }
         
   /* Introduce request header
    **************************/
   *nchars = sprintf(request, "<img class=\"TraceClass\" alt=\"\" src=\"http://chart.apis.google.com/chart?chs=%dx61&cht=ls&chxt=x&chxl=0:|%c&chxp=0,%d&chxtc=0,-600&chco=000060&chma=0,0,0,0&chls=2&chd=s:",
         600, phaseName, phasePos);//nsamples);
   
   for (i=0; i<nsamples; i++)
   {
      request[(*nchars)++] = simpleEncode((int)(((samples[i]/maxSamp)/2 + 0.5) * 60));
      //request[(*nchars)++] = ',';
   }
   request[(*nchars)++] = '\"';
   request[(*nchars)++] = '/';
   request[(*nchars)++] = '>';
   request[(*nchars)++] = '\0'; //Terminate string;
   
   return;
}


/*******************************************************************************
 * simpleEncode: This is a simple integer encoder based on googles function    *
 *******************************************************************************/
char simpleEncode(int i)
{
   char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
   
   /* truncate input
    ****************/
   if (i<0)
      i=0;
   if (i>61)
      i=61;
      
   
   return base[i];
}



/*******************************************************************************
 * searchSite: To replace site index, which does not seem to work well         *
 *******************************************************************************/
int searchSite(char *S, char *C, char *N, char *L)
{
   int i;
   
   for (i=0; i<nSite; i++)
   {
      if (
         strcmp(S,Site[i].name)==0 &&
         strcmp(C,Site[i].comp)==0 &&
         strcmp(N,Site[i].net)==0 &&
         strcmp(L,Site[i].loc)==0 )
         return i;
      //printf("Testing: %s.%s.%s.%s <-> %s.%s.%s.%s\n",
      //   S,C,N,L,
      //   Site[i].name,Site[i].comp,Site[i].net,Site[i].loc);
   }

   return -1;
}




/*******************************************************************************
 * getWSData: Retrieve data from the waveserver and store it in the stations   *
 *            structure. The trace data is high-pass filtered and sub-sampled  *
 *******************************************************************************/
int getWSData(SITE *site, 
   double starttime, double endtime, 
   double *trace, int nsamples)
{
   char *buffer;                   /* Buffer to store data from the waveserver */
   WS_MENU_QUEUE_REC menu_queue;
   TRACE_REQ trace_req;
   int wsResponse;
   TRACE2_HEADER *trace_header;
   double lastEndtime = 0;
   char* sampP;
   int bps;
   int i;
   int samples[(MAX_TRACEBUF_SIZ-64)/2]; /* Samples for a single tracebuf2 */
   double sampleAvg = 0;           /* To remove average */
   int samplePos = 0;              /* Current sample on trace */
   double deltaT = (endtime - starttime)/(double)nsamples;
   double curSampleTime = starttime;
   double curTime;
   int completedAcquisition = 0;
   char WSErrorMsg[80]; /* To write ws error messages */
   
   
   /* Allocate memory for buffer
    ****************************/
   buffer = (char*) malloc(MAX_SAMPLES * 4);
   if (buffer==NULL)
   {
      logit( "et", "ewhtmlemail: Cannot allocate buffer for trace\n" );
      return -1;
   }
   
   
   /* Initialize trace
    ******************/
   for (i=0; i<nsamples; i++)
   {
      trace[i] = 0;
   }
   
   
   /* Initialize menu queue
    ***********************/
   menu_queue.head = NULL;
   menu_queue.tail = NULL;
   
   
   /* Make menu request
    *******************/
   int atLeastOne = 0;
   for (i=0; i<nwaveservers; i++)
   {
      wsResponse = wsAppendMenu(
         waveservers[i].wsIP, waveservers[i].port, 
         &menu_queue, wstimeout);
      if (wsResponse!=WS_ERR_NONE)
      {
         logit( "et", "ewhtmlemail: Cannot contact waveserver %s:%s - ",
            waveservers[i].wsIP, waveservers[i].port, wsResponse);
         logit( "et", "%s\n",
            getWSErrorStr(wsResponse, WSErrorMsg));
         continue;
      }
      else
      {
         atLeastOne++;
      }
   }
   if (atLeastOne == 0)
   {
      logit( "et", "ewhtmlemail: Unable to contact any waveserver.\n");
      return -1;
   }
   
   
   /* Make request structure
    ************************/
   strcpy(trace_req.sta, site->name);
   strcpy(trace_req.chan, site->comp);
   strcpy(trace_req.net, site->net);
   strcpy(trace_req.loc, site->loc);
   trace_req.reqStarttime = starttime;
   trace_req.reqEndtime = endtime;
   trace_req.partial = 1;
   trace_req.pBuf = buffer;
   trace_req.bufLen = MAX_SAMPLES*4;
   trace_req.timeout = wstimeout;
   trace_req.fill = 0;
   
   
   /* Pull data from ws
    *******************/
   wsResponse = wsGetTraceBinL( &trace_req, &menu_queue, wstimeout );
   if (wsResponse!=WS_ERR_NONE)
   {
      logit( "et", "ewhtmlemail: Error loading data from waveserver - %s\n",
         getWSErrorStr(wsResponse, WSErrorMsg));
      return -1;
   }
   
   
   /* Prepare for tracebuf2 analyzing cycle
    ***************************************/
   trace_header = (TRACE2_HEADER*)buffer;
   
   
   /* Tracebuf analyzing cycle
    **************************/
   while (trace_header < (TRACE2_HEADER*)(buffer + trace_req.actLen) &&
      completedAcquisition == 0)
   {
      /* If necessary, swap bytes in tracebuf message
       ***********************************************/
      if ( WaveMsg2MakeLocal( trace_header ) < 0 )
      {
         logit( "et", "ewhtmlemail: WaveMsg2MakeLocal error.\n" );
         continue;
      }
     
     /* Check for gaps
      ****************/
     if ((trace_header->starttime - lastEndtime) > (1/trace_header->samprate))
     {
        /* Detected GAP... Do nothing
         ****************************/
     }
     lastEndtime = trace_header->endtime;
      
      
     /* Convert samples to int
      ************************/
     sampP = (char*)trace_header + 64; //Position of the first sample
     sampleAvg = 0;
     if ( strcmp(trace_header->datatype, "i2")==0 ||
        strcmp(trace_header->datatype, "s2")==0 )
     {   
        bps = 2;
        for (i = 0; i<trace_header->nsamp; i++)
        {
           samples[i] = (int)(*((short*)sampP));
           sampleAvg += (double)samples[i];
           sampP += bps;
        }
     }
     else
     {
        bps = 4;
        for (i = 0; i<trace_header->nsamp; i++)
        {
           samples[i] = *((int*)sampP);
           sampleAvg += (double)samples[i];
           sampP += bps;
        }
     }
     sampleAvg /= (double)trace_header->nsamp;
     
     /* Process samples
      *****************/
     double processedSample = 0;
     double sampleMaximum = 0;
     int getMaximum = 0;
     for (i=0; i<trace_header->nsamp; i++)
     {
        processedSample = (double)samples[i] - sampleAvg;
        if (getMaximum==1)
        {
           if (processedSample>sampleMaximum)
              sampleMaximum = processedSample;
        }
        else
        {
           if (processedSample<sampleMaximum)
              sampleMaximum = processedSample;
        }
     
        /* Check if this sample shold be stored
         **************************************/
        curTime = trace_header->starttime + (double)i/trace_header->samprate;
        if (((curTime-curSampleTime) <= 1/(2*trace_header->samprate)) &&
           ((curTime-curSampleTime) > -1/(2*trace_header->samprate)))
        {
           /* Store sample in output array and update time
            **********************************************/
           //trace[samplePos++] = (double)samples[i] - sampleAvg;
           trace[samplePos++] = sampleMaximum;
           getMaximum = 1-getMaximum;
           sampleMaximum = 0;
           
           curSampleTime += deltaT;
           
           
           /* Check if number of required samples has been reached
            ******************************************************/
           if (samplePos>=nsamples)
           {
              completedAcquisition = 1;
              break;
           }
        }
     }
     
   
      /* Update trace_header pointer
       *****************************/
      trace_header = (TRACE2_HEADER*)sampP; 
   }
   
   /* Terminate connection
    **********************/
   wsKillMenu(&menu_queue);
   
   
   /* Free memory
    *************/
   free(buffer);
   
   /* Done!
    *******/
   return 0;
}







char* getWSErrorStr(int errorCode, char* msg)
{
   switch (errorCode)
   {
      case 1:
         strcpy(msg,"Reply flagged by server");
         return msg;
      case -1:
         strcpy(msg,"Faulty or missing input");
         return msg;
      case -2:
         strcpy(msg,"Unexpected empty menu");
         return msg;
      case -3:
         strcpy(msg,"Server should have been in menu");
         return msg;
      case -4:
         strcpy(msg,"SCNL not found in menu");
         return msg;
      case -5:
         strcpy(msg,"Reply truncated at buffer limit");
         return msg;
      case -6:
         strcpy(msg,"Couldn't allocate memory");
         return msg;
      case -7:
         strcpy(msg,"Couldn't parse server's reply");
         return msg;
      case -10:
         strcpy(msg,"Socket transaction timed out");
         return msg;
      case -11:
         strcpy(msg,"An open connection was broken");
         return msg;
      case -12:
         strcpy(msg,"Problem setting up socket");
         return msg;
      case -13:
         strcpy(msg,"Could not make connection");
         return msg;
   }
   //Unknown error
   return NULL;
}

















