/****************************************************************************/
/*                                                                          */
/*                              Dump_time_shift.c                           */
/*                                                                          */
/****************************************************************************/
/****************************************************************************/
/* This program fixes time errors in Dump-format files by shifting blocks   */
/* of data within the given file. The corrected data is then written into   */
/* standard output.                                                         */
/*                                                                          */
/* Usage:                                                                   */
/*    Dump_time_shift -s YYMMDDHHMMSS -e YYMMDDHHMMSS  -t TimeShift -a      */
/*                                  [-i] DumpFile > NewDumpFile             */
/*         -s YYMMDDHHMMSS  Time of the first block to be shifted           */
/*         -e YYMMDDHHMMSS  Time of the last block to be shifted.           */
/*         -t TimeShift     Time shift in seconds. Positive if clock in the */
/*                          particular station shows smaller reading for    */
/*                          some reference event.                           */
/*         -i               The gap which is created when data is shifted   */
/*                          is filled with interpolated data values. If     */
/*                          missing then the gap is filled with missing     */
/*                          data markers.                                   */
/*         -a               Shift all data points within the file and also  */
/*                          adjust the start and end times of the file.     */
/*         DumpFile         Name of Dump-format file.                       */
/*         NewDumpFile      Name of corrected Dump-format file.             */
/*                                                                          */
/* Note: The size of the data file is not changed, data is only shifted     */
/*       within the file.                                                   */
/*                                                                          */
/****************************************************************************/
/****************************************************************************/
/*                            Lasse Hakkinen                                */
/*                    Finnish Meteorological Institute                      */
/*                        Geophysical Research Division                     */
/*                              P.O.Box 503                                 */
/*                      FIN-00101, Helsinki, Finland                        */
/*                      e-mail: Lasse.Hakkinen@fmi.fi                       */
/*                      phone : (+358)-9-19294634                           */
/*                      fax   : (+358)-9-19294603                           */
/*                                                                          */
/*                      version 1.03    15.10.1999                          */
/****************************************************************************/
/****************************************************************************/
/*  Version history:                                                        */
/*                                                                          */
/*  1.03 15.10.1999 Modified Dump.h so that the last data is neglected if   */
/*                  it does not end with CR or LF. In some cases this       */
/*                  resulted in unrelated error messages.                   */
/*  1.02 02.07.1999 Fixed a Y2K bug in NewTime.h file which resulted in     */
/*                  incorrect year in date strings if year >= 2000.         */
/*  1.01 02.02.1999 Added -a option.                                        */
/*  1.0  18.01.1999 First official release                                  */
/****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Usage.h"
#include "MagnData.h"
#include "Dump.h"

char *version = "1.03";
char *date = "15.10.1999";

#define HeaderLineCount 5
#define UsageLineCount 18

char *HeaderText[HeaderLineCount] = {
"**************************************************************************",
"This program fixes time errors in an Dump format file by shifting selected",
"blocks within the Dump file by specified time shift. The size of the data ",
"file is not changed, the data is only shifted within the file.            ",
"**************************************************************************",
};

char *UsageText[UsageLineCount] = {
" [-s] [-e] -t [-o] [-i] [-a] [<] DumpFile > NewDumpFile",
"      [-s YYMMDDHHMMSS]    Time of the first record to be shifted.        ",
"                           If missing then start of file is assumed.      ",
"      [-e YYMMDDHHMMSS]    Time of the last record to be shifted.         ",
"                           If missing then end of file is assumed.        ",
"      -t TimeShift         Time shift in seconds. Positive if clock in the",
"                           particular station shows smaller reading for   ",
"                           some reference event.                          ",
"      [-i]                 Shifting the data values creates a gap.        ",
"                           Fill this gap by interpolating data values.    ",
"                           If missing then gap is filled with missing     ",
"                           data markers.                                  ",
"      [-a]                 Shift all data points within the file and also ",
"                           adjust the start and end times of the file     ",
"                           accordingly. If -a option is defined then -s,  ",
"                           -e and -i options are neglected.               ",
"      DumpFile             Name of Dump-format file.                      ",
"      NewDumpFile          Name of extracted Dump-format file.            ",
};



/*--------------------------------------------------------------------------*/
/*  Replace field value at time T1 with that at time T0.                    */
/*--------------------------------------------------------------------------*/

void ReplaceValue(StationPtr s, Time_sec T0, Time_sec T1, char Comp)
{
    long Value;
    
    /* GetDataValue and SetDataValue routines will take care that */
    /* the times are within the limits of existing data points in */
    /* the station data structure.                                */
    
    Value = GetDataValue(s,T0,Comp);
    SetDataValue(s,T1,Comp,Value);
}

/*--------------------------------------------------------------------------*/
/*  Fill block of memory with missing data                                  */
/*--------------------------------------------------------------------------*/

void FillMissingData(StationPtr s, Time_sec T0, Time_sec T1, char Comp)
{
    Time_sec T;
    
    for (T=T0; T<=T1; T += s->TimeStep)
        SetDataValue(s,T,Comp,MissingValue);
}


/*--------------------------------------------------------------------------*/
/*  Move data for all components in memory to adjust for the time shift and */
/*  fill the remaining blocks with missing data values or interpolate them. */
/*  Here StartTime is the first data point to be shifted and EndTime is the */
/*  last data point to be shifted.                                          */
/*--------------------------------------------------------------------------*/

void MoveDataForward(StationPtr s,Time_sec StartTime,Time_sec EndTime,
                     long TimeShift,long InterpolateFlag)
{
    Time_sec T;
    
    /* Move data in memory. ReplaceValue-routine will check that */
    /* times are within limits.                                  */
    for (T = EndTime; T >= StartTime; T -= s->TimeStep) {
        ReplaceValue(s,T,T+TimeShift,'X');
        ReplaceValue(s,T,T+TimeShift,'Y');
        ReplaceValue(s,T,T+TimeShift,'Z');
    }

    for (T = EndTime; T >= StartTime; T -= s->TempStep) {
        ReplaceValue(s,T,T+TimeShift,'T');
        ReplaceValue(s,T,T+TimeShift,'A');
    }

    if (InterpolateFlag == 0) {
        FillMissingData(s,StartTime,StartTime+TimeShift - s->TimeStep,'X');
        FillMissingData(s,StartTime,StartTime+TimeShift - s->TimeStep,'Y');
        FillMissingData(s,StartTime,StartTime+TimeShift - s->TimeStep,'Z');
        FillMissingData(s,StartTime,StartTime+TimeShift - s->TempStep,'T');
        FillMissingData(s,StartTime,StartTime+TimeShift - s->TempStep,'A');
    } else {
        InterpolateData(s,StartTime,StartTime+TimeShift - s->TimeStep,'X');
        InterpolateData(s,StartTime,StartTime+TimeShift - s->TimeStep,'Y');
        InterpolateData(s,StartTime,StartTime+TimeShift - s->TimeStep,'Z');
        InterpolateData(s,StartTime,StartTime+TimeShift - s->TempStep,'T');
    }
}


/*--------------------------------------------------------------------------*/
/*  Move data backward in memory to adjust for the time shift and fill the  */
/*  remaining blocks with missing data values. Note that TimeShift here is  */
/*  positive, since the routine is called with '-TimeShift'.                */
/*--------------------------------------------------------------------------*/

void MoveDataBackward(StationPtr s,Time_sec StartTime,Time_sec EndTime,
                      long TimeShift,long InterpolateFlag)
{
    Time_sec T;
    
    /* Move data in memory. ReplaceValue-routine will check that */
    /* times are within limits.                                  */
    for (T = StartTime; T <= EndTime; T += s->TimeStep) {
        ReplaceValue(s,T,T-TimeShift,'X');
        ReplaceValue(s,T,T-TimeShift,'Y');
        ReplaceValue(s,T,T-TimeShift,'Z');
    }

    for (T = StartTime; T <= EndTime; T += s->TempStep) {
        ReplaceValue(s,T,T-TimeShift,'T');
        ReplaceValue(s,T,T-TimeShift,'A');
    }

    if (InterpolateFlag == 0) {
        FillMissingData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'X');
        FillMissingData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'Y');
        FillMissingData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'Z');
        FillMissingData(s,EndTime-TimeShift+ s->TempStep,EndTime,'T');
        FillMissingData(s,EndTime-TimeShift+ s->TempStep,EndTime,'A');
    } else {
        InterpolateData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'X');
        InterpolateData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'Y');
        InterpolateData(s,EndTime-TimeShift+ s->TimeStep,EndTime,'Z');
        InterpolateData(s,EndTime-TimeShift+ s->TempStep,EndTime,'T');
    }
}


/*--------------------------------------------------------------------------*/
/*  Change the time in the parameter line.                                  */
/*--------------------------------------------------------------------------*/

void FixParamLineTime(long i,Time_sec TimeShift)
{
    char TimeStr[20];
    char *PLine;
    Time_sec T;
    
    PLine = ParamLine[i];
    T = GetDumpTime(PLine)+TimeShift;
    
    SecsToStr(T,TimeStr);
    if (T < YEAR2000)
        strncpy(PLine+Dump_Year,"19",2);
    else
        strncpy(PLine+Dump_Year,"20",2);
    
    strncpy(PLine+Dump_Year+2,TimeStr,2);
    strncpy(PLine+Dump_Month ,TimeStr+2,2);
    strncpy(PLine+Dump_Day   ,TimeStr+4,2);
    strncpy(PLine+Dump_Hour  ,TimeStr+6,2);
    strncpy(PLine+Dump_Minute,TimeStr+8,2);
    strncpy(PLine+Dump_Second,TimeStr+10,2);
}



/*--------------------------------------------------------------------------*/
/*                          The main procedure                              */
/*--------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
    long    params;
    long    status = 0;
    long    FileCount = 0;
    char    FileName[100] = "";         /* Name of the original data file   */
    Time_sec StartTime = MIN_TIME;      /* Time of first data block (secs)  */
    Time_sec EndTime   = MAX_TIME;      /* Time of last block to be moved   */
    long    TimeShift       = 99999;    /* Time shift in seconds            */
    Network_struct IMAGE;               /* Structure containing the data    */
                                        /* for all stations.                */
    StationPtr  Station;                /* Pointer to the corrected station */
    long    InterpolateFlag = 0;        /* Interpolate data values or fill  */
                                        /* with missing data values (def)   */
    long    AllFlag = 0;                /* Shift all data lines within the  */
                                        /* file and also modify the start   */
                                        /* and end times accordingy         */
    long    i;                          /* Dummy index                      */

    /*==========================*/
    /* Analyse the command line */
    /*==========================*/
    if (argc == 1) {        /* No arguments, show the usage text */
        PrintUsage(argv[0],0,HeaderLineCount,UsageLineCount);
        return OK;
    }

    for (params = 1; params < argc; params++) {
        if (*argv[params] != '-') {
            strcpy(FileName,argv[params]);
            FileCount++;
        } else {
            switch (*(argv[params]+1)) {
                case 's' : StartTime = StrToSecs(argv[++params],0); break;
                case 'e' : EndTime   = StrToSecs(argv[++params],0); break;
                case 't' : TimeShift = atol(argv[++params]);        break;
                case 'i' : InterpolateFlag = 1;                     break;
                case 'a' : AllFlag = 1;                             break;
                default  :
                    fprintf(stderr,"\n### %s: \"%s\" is not an option.\n\n",
                            argv[0], argv[params]);
                    PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount);
                    return FAIL;
                    break;
            }
        }
    }


    /*=================================================*/
    /* Check that all necessary parameters are defined */
    /*=================================================*/
    
    if ((FileCount>1) || (TimeShift == 99999))
    {
        PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount);
        return FAIL;
    }

    /*====================================================*/
    /* Read the data from an Dump format file into memory */
    /*====================================================*/

    status = ReadDump(FileName,&IMAGE,NULL,NULL,NULL);

    if (status != 0) {
        if (status == FileError)
            fprintf(stderr,"Error in reading file %s\n",FileName);
        if (status == OutOfMemory)
            fprintf(stderr,"Out of memory while reading file %s\n",FileName);
        if (status == FileFormatError)
            fprintf(stderr,"%s is not an Dump format file\n",FileName);
        return FAIL;
    }

    /*=======================================================*/
    /* Check that the values of the parameters are resonable */
    /*=======================================================*/

    Station = IMAGE.StationList;

    if (StartTime > EndTime) {
        fprintf(stderr,"StartTime > EndTime !?\n");
        return FAIL;    
    }

    if ((StartTime != MIN_TIME) &&
       (((StartTime-Station->StartTime) % Station->TimeStep) != 0)) {
        fprintf(stderr,
            "Illegal StartTime. There is no such data point in the datafile\n");
        return FAIL;    
    }

    if ((EndTime != MAX_TIME) &&
       (((EndTime-Station->StartTime) % Station->TimeStep) != 0)) {
        fprintf(stderr,
            "Illegal EndTime. There is no such data point in the datafile\n");
        return FAIL;    
    }

    if ((TimeShift % Station->TimeStep) != 0) {
        fprintf(stderr,
            "Illegal TimeShift. Must be multiple of datafile time step\n");
        return FAIL;    
    }

    if ((AllFlag == 1) && (StartTime != MIN_TIME) && (EndTime != MAX_TIME)) {
        fprintf(stderr,
            "StartTime and EndTime must be undefined when -a option is used\n");
        return FAIL;    
    }


    /*======================================================*/
    /* Move the data blocks in memory to fix the time error */
    /*======================================================*/

    if (AllFlag == 0) {
        if (StartTime == MIN_TIME) StartTime = Station->StartTime;
        if (EndTime == MAX_TIME) EndTime = Station->EndTime;
        
        if (TimeShift > 0)
            MoveDataForward(Station,StartTime,EndTime,TimeShift,InterpolateFlag);
        else
            MoveDataBackward(Station,StartTime,EndTime,-TimeShift,InterpolateFlag);
    }
    else {  /* Just modify start and end times and fix the parameter line times */
        Station->StartTime += TimeShift; 
        Station->EndTime += TimeShift;
        
        for (i=0; i< ParamLineCount; i++) {
            if (ParamLineTime[i] != MIN_TIME)
                ParamLineTime[i] += TimeShift;
            if ((ParamLine[i][0] != 'i') && (ParamLine[i][0] != 'c'))
                FixParamLineTime(i,TimeShift);
        }
    }

    /*==========================================*/
    /* Write the corrected file into stdout     */
    /*==========================================*/
    
    WriteDump(NULL,&IMAGE);

    FreeNetwork(&IMAGE);
    return OK;
}

