/****************************************************************************/
/*                                                                          */
/*                                  SOD.h                                   */
/*                                                                          */
/****************************************************************************/
/****************************************************************************/
/* This is a C language header file that defines the following routine for  */
/* reading SOD format data files into memory. The SOD format is a data      */
/* format used at Sodankyla Geophysical Observatory (SGO) for storing       */
/* magnetometer data. A SOD file is a simple text file containing data for  */
/* only one station.                                                        */
/*                                                                          */
/*  -------------------  Read data from a SOD-format file   --------------  */
/*                                                                          */
/* long ReadSOD(char *FileName,NetworkPtr Network,char *StartTimeStr,       */
/*               char *EndTimeStr,char *StationList)                        */
/*      FileName        Name of the SOD-file containing the data.           */
/*                      Contrary to other format reading routines ReadSOD   */
/*                      cannot read data from stdin. Therefore FileName     */
/*                      must not be nil nor zero length.                    */
/*      Network         Structure into which the data is read.              */
/*                      Network_struct is defined in file MagnData.h.       */
/*      StartTimeStr    String defining the first time to be read from      */
/*                      the data file (String delimited with '\0').         */
/*                      String format : YYMMDDHH. If HH is missing then     */
/*                      00 is assumed. If StartTimeStr is nil or zero       */
/*                      length then start of file is assumed.               */
/*      EndTimeStr      String defining the time not to be read anymore.    */
/*                      Format as before YYMMDDHH. If nil or zero length    */
/*                      then data is read until end of file.                */
/*      StationList     List of stations included in the data. This is      */
/*                      for compatibility reasons only as the SOD format    */
/*                      allows only one station in one data file.           */
/*                      Furthermore, as there is no information about the   */
/*                      station in the datafile itself (e.g. station name)  */
/*                      The StationList must contain exactly one station.   */
/*                                                                          */
/*      The function result indicates how successfull the file reading was: */
/*              0: OK           : File read successfully                    */
/*              1: FileError    : Failed to read the given file             */
/*              2: OutOfMemory  : Memory allocation failed during reading   */
/*              3: FormatError  : File to be read is not a SOD file         */
/*                                                                          */
/*  NOTE: The current version can handle only data files where the time     */
/*        step is a multiple of 10 seconds (i.e. 10,20,... seconds).        */
/*        Very probably a large number of error messages will be generated  */
/*        if the time step is something else (e.g. 15 seconds).             */
/****************************************************************************/
/****************************************************************************/
/*                            Lasse Hakkinen                                */
/*                    Finnish Meteorological Institute                      */
/*                        Department of Geophysics                          */
/*                              P.O.Box 503                                 */
/*                      FIN-00101, Helsinki, Finland                        */
/*                      e-mail: Lasse.Hakkinen@fmi.fi                       */
/*                      phone : (+358)-9-19294634                           */
/*                      fax   : (+358)-9-19294634                           */
/*                                                                          */
/*                      version 1.0         26.05.2000                      */
/****************************************************************************/
/****************************************************************************/
/*  Version history:                                                        */
/*                                                                          */
/*  1.0  26.05.2000 First official release                                  */
/****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#define MissingSOD 999999


/*--------------------------------------------------------------------------*/
/* Offsets of various fields from the start of data line in SOD format file.*/
/* In case that these may change in the future these offsets are dynamically*/
/* determined in the beginning of the program.                              */
/*--------------------------------------------------------------------------*/

long SOD_X,SOD_Y,SOD_Z,SOD_Date,SOD_Time;

static long LineNbr = 0;                /* Current line number              */



/*--------------------------------------------------------------------------*/
/* Function prototypes                                                      */
/*--------------------------------------------------------------------------*/

static void GetSODOffsets(char *Line);
static Time_sec GetSODTime(char *Line);
static int ReadSODLine(FILE *SODfile,char *Line);
static long GetSODFieldValue(char *p);
static void SetStationParamsSOD(StationPtr Station,Time_sec T,char *StatList);
static void GetSODData(StationPtr Station, char *Buff);
static void SetMissingDataPoint(StationPtr Station,Time_sec Time);
long ReadSOD(char *FileName,NetworkPtr Network,char *StartTimeStr,
              char *EndTimeStr,char *StationList);



/*==========================================================================*/
/*              Routines for reading SOD-format files                       */
/*==========================================================================*/

/*--------------------------------------------------------------------------*/
/*  Get the offsets of different components and date and time in a SOD line */
/*  The components are in order XXX YYY ZZZ YYMMDD HHMMSS                   */
/*  Here are two sample lines from a typical SOD file                       */
/*  11210.2  1805.9 51200.0 000525 000110                                   */
/*  11211.0  1803.2 51199.8 000525 000120                                   */
/*  Number of decimals is hopefully always one.                             */
/*--------------------------------------------------------------------------*/

static void GetSODOffsets(char *Line)
{
    char *p = Line;

    SOD_X = 0;                  /* The first component is X     */

    while (*p++ != '.');        /* Find the decimal point in X  */
    while (*p++ != ' ');        /* Find the next space          */
    SOD_Y = (long) (p-Line);    /* The second component is Y    */

    while (*p++ != '.');        /* Find the decimal point in Y  */
    while (*p++ != ' ');        /* Find the next space          */
    SOD_Z = (long) (p-Line);    /* The third component is Z     */

    while (*p++ == ' ');        /* Find the start of Z          */
    while (*p++ != ' ');        /* Skip over Z                  */
    while (*p++ == ' ');        /* Skip spaces                  */
    SOD_Date = ((long) (p-Line)) -1; /* The fourth item is Date */

    while (*p++ != ' ');        /* Skip over Date               */
    while (*p++ == ' ');        /* Skip spaces                  */
    while (*p++ >= '0');        /* Find the end of Time string  */
    SOD_Time = ((long) (p-Line)) -7; /* The fourth item is Time */

}


/*--------------------------------------------------------------------------*/
/*  Get the time of the current data line (in seconds). The date in the SOD */
/*  file is in format YYMMDD HHMMSS. Leading zeros are not missing.         */
/*  It is quite probable the the time given is the end time of the average  */
/*  interval (= 10s). In IAGA format file the time is the start time of the */
/*  averaging period. Therefore we subtract 10 s from the given time.       */
/*--------------------------------------------------------------------------*/

static Time_sec GetSODTime(char *Line)
{
    char DateStr[14];

    strncpy(DateStr  ,Line+SOD_Date,6);
    strncpy(DateStr+6,Line+SOD_Time,6);
    DateStr[12] = 0;        /* String terminator */

/*  Round to 10 seconds
    DateStr[11] = '0';
*/
    return (StrToSecs(DateStr,12) - 10);
}


/*--------------------------------------------------------------------------*/
/* Read one line from the SOD-format file into the Buffer. This routine     */
/* works if the line end character is CR,NL or CR+NL.                       */
/* The routine will return 1 if the line was read succesfully , otherwise   */
/* 0 is returned.                                                           */
/*--------------------------------------------------------------------------*/

int ReadSODLine(FILE *SODfile,char *Line)
{
    int c;

    /* Handle the first character */
    if ((c = getc(SODfile)) == EOF) return(0);
    if (c < 32) {   /* Linefeed or carriage return */
        if ((c = getc(SODfile)) == EOF) return(0);
    }

    *Line++ = c;        /* Put the first char into Buffer */

    /* read the rest of the line (including CR or NL) */
    while (((c = getc(SODfile)) != EOF) && (c > 31)) {
        *Line++ = c;
    }
    *Line++ = c;
    LineNbr++;
    return(1);
}


/*--------------------------------------------------------------------------*/
/*  Fill some fields in the Station_struct by copying them from the         */
/*  StatonInfo structure defined in the StatInfo.h file.                    */
/*--------------------------------------------------------------------------*/

static void SetStationParamsSOD(StationPtr Station,Time_sec T,char *StatList)
{
    StationInfoPtr StationInfo;

    /* Set the station ID */
    strncpy(Station->StationID,StatList,3);
    Station->StationID[3] = '\0';

    StationInfo = FindStationInfo(StatList);

    /* Set the location of the station */
    Station->Latitude  = StationInfo->Latitude;
    Station->Longitude = StationInfo->Longitude;

    /* Set the component markers */
    strcpy(Station->Components,"XYZ");

    /* Set the start time. End time is updated by GetSODData routine */
    Station->StartTime = T;
}


/*--------------------------------------------------------------------------*/
/* Get the 6.1 character number from the Buffer. We could also use sscanf,  */
/* but this is considerably faster because no checkings are made.           */
/*--------------------------------------------------------------------------*/

static long GetSODFieldValue(char *p)
{
    long value = 0;
    long negative = 0;


    while (*p == ' ') p++;      /* scan leading zeros   */
    if (*p == '-') {            /* get the sign         */
        negative = 1;
        p++;
    }
    while (*p != '.') {         /* get the integer part */
        value = 10*value+(*p-'0');
        p++;
    }
    p++;
    value = 10*value+(*p-'0');  /* get the decimal part */

    p++;    /* If there are more than one decimal then round the result */
    if ((*p >= '5') && (*p <= '9')) value++;

    if (negative) value = -value;
    if (value >= MissingSOD) value = MissingValue;
    return (value);
}


/*--------------------------------------------------------------------------*/
/* Copy data from a single data line into proper OneDayBlock.               */
/*--------------------------------------------------------------------------*/

static void GetSODData(StationPtr Station, char *Line)
{
    OneDay_struct *p;
    long *XPtr,*YPtr,*ZPtr,*TPtr;

    /* Find the last OneDayBlock */
    for (p = Station->FirstDay; p->NextDay != NULL; p = p->NextDay);

    /* Set the temperature value */
    TPtr  = (p->T)+(p->TCount);
    *TPtr = MissingValue;
    p->TCount++;

    /* Set the pointers within the OneDayBlock */
    XPtr = (p->X)+(p->XCount);
    YPtr = (p->Y)+(p->YCount);
    ZPtr = (p->Z)+(p->ZCount);

    /* Copy the data */
    *XPtr = GetSODFieldValue(Line+SOD_X);
    *YPtr = GetSODFieldValue(Line+SOD_Y);
    *ZPtr = GetSODFieldValue(Line+SOD_Z);
    p->XCount++;
    p->YCount++;
    p->ZCount++;

    /* Update the end time */
    Station->EndTime = GetSODTime(Line)+Station->TimeStep;
}


/*--------------------------------------------------------------------------*/
/* Set a single missing data value at specified time.						*/
/*--------------------------------------------------------------------------*/

static void SetMissingDataPoint(StationPtr Station,Time_sec Time)
{
    long i,Count;
    OneDay_struct *p;
    long *XPtr,*YPtr,*ZPtr,*TPtr;

    /* Find the last OneDayBlock */
    for (p = Station->FirstDay; p->NextDay != NULL; p = p->NextDay);

    /* Set the pointers within the OneDayBlock */
    XPtr = (p->X)+(p->XCount);
    YPtr = (p->Y)+(p->YCount);
    ZPtr = (p->Z)+(p->ZCount);
    TPtr = (p->T)+(p->TCount);

    /* Set missing values */
	*XPtr++ = MissingValue;
	*YPtr++ = MissingValue;
	*ZPtr++ = MissingValue;
	*TPtr++ = MissingValue;

    /* Update counters */
    p->XCount++;
    p->YCount++;
    p->ZCount++;
    p->TCount++;
}



/*--------------------------------------------------------------------------*/
/*  Here is the routine for reading SOD-format data file into memory.       */
/*  The data is read into a Network structure (see MagnData.h for details). */
/*  See the comments at the beginning of this file for more details about   */
/*  the parameters for this routine.                                        */
/*                                                                          */
/*  The rounite works by reading one data line (38 bytes) at a time and     */
/*  adds that data into the particular stations data block. First           */
/*  for each new station that is encountered space for one day is allocated.*/
/*  If the day becomes full a new one day block is allocated. At the end of */
/*  the routine all one day blocks are combined into a single data block.   */
/*--------------------------------------------------------------------------*/

long ReadSOD(char *FileName,NetworkPtr Network,char *StartTimeStr,
              char *EndTimeStr,char *StationList)
{
    FILE    *SODfile;               /* The data file to be processed        */
    char    Buffer[60];             /* Buffer for one SOD data line         */
    Time_sec Time;                  /* Time of the current data block       */
    Time_sec Time2;                 /* Time used in for loop			    */
    Time_sec OldTime;               /* Time of the previous data block      */
    Time_sec StartTime;             /* Time of first record to be read      */
    Time_sec EndTime;               /* Time of record not read anymore      */
    Time_sec TimeStep;              /* Sampling rate in the SOD file        */
    StationPtr Station = NULL;      /* Pointer to the current station       */
    long    FirstLine = 1;          /* Flag for first valid data line       */
    long Neglect = 0;


    InitNetwork(Network);           /* Initialize the fields of Network     */

    /* --- Set the start and end times --- */
    if ((StartTimeStr == NULL) || (*StartTimeStr == '\0'))
        StartTime = MIN_TIME;
    else StartTime = StrToSecs(StartTimeStr,0);

    if ((EndTimeStr == NULL) || (*EndTimeStr == '\0')) EndTime = MAX_TIME;
    else EndTime = StrToSecs(EndTimeStr,0);

    /* --- Try to open the file --- */
    if ((SODfile = fopen(FileName, "r")) == NULL) return FileError;


    /* --- Find the sampling step in the data file --- */

    ReadSODLine(SODfile,Buffer);
    GetSODOffsets(Buffer);
    Time = GetSODTime(Buffer);
    ReadSODLine(SODfile,Buffer);
    TimeStep = GetSODTime(Buffer) - Time;
    rewind(SODfile);

    if (TimeStep != 10) {
        fprintf(stderr,"###  Sampling rate not 10 seconds in file: \"%s\"\n",FileName);
        fprintf(stderr,"###  Time difference between first two lines: %d\n",TimeStep);
        return(FileError);
    }


    /* --- Read the rest of the data into one day blocks ---*/

    while (ReadSODLine(SODfile,Buffer))
    {

        if ((Time = GetSODTime(Buffer)) >= EndTime) break;


        if (Time >= StartTime) {
            if (FirstLine) {        /* First valid data line */
                Station = AddNewStation(Network,TimeStep,TimeStep);
                if (Station != NULL)
                    SetStationParamsSOD(Station,Time,StationList);
                else        /* Unable to allocate memory for station data   */
                    return(OutOfMemory);
                OldTime = Time;
                FirstLine = 0;
            }
            else {
                if (((Time - Station->StartTime) % TimeStep) != 0) {
                    fprintf(stderr,"### Illegal time %.14s\n",Buffer+SOD_Date);
                    fprintf(stderr,"File \"%s\" ; Line %d\n",FileName,LineNbr);
                    Neglect = 1;
                    // return(FileError);
                }
                else
                if (Time <= OldTime) {
                    fprintf(stderr,"###  Data lines not in chronological order\n");
                    fprintf(stderr,"File \"%s\" ; Line %d\n",FileName,LineNbr);
                    return(FileError);
                }
                else
                if (Time-OldTime != TimeStep) { /* Add missing data points */
                	for (Time2 = OldTime+TimeStep; Time2 < Time; Time2 += TimeStep) {
            			if (OneDayFull(Station,'Z'))  AddOneDay(Station);
    	                SetMissingDataPoint(Station,Time2);
        	        }
            	}
            }

			if (Neglect == 0) {
				if (OneDayFull(Station,'Z')) {
					if (AddOneDay(Station))
						return(OutOfMemory);       /* Failed to allocate memory */
				}
				GetSODData(Station,Buffer);
				OldTime = Time;
			}
			else Neglect = 0;
        }
    }
    fclose(SODfile);

    /* --- Combine one day blocks into one large block ---*/
    Station = Network->StationList;
    while (Station != NULL) {
        if (CombineData(Station) == OutOfMemory) return(OutOfMemory);
        Station = Station->Next;
    }

    return OK;
}


