/** @file  tst_psTime_02.c
 *
 *  @brief Test driver for psTime functions
 *
 *  This test driver contains the following tests for psTime:
 *     1) Convert psTime to Local Mean Sidereal Time (LMST)
 *     2) Calculate leap second delta between times
 *     3) Creation of psTime of type TT
 *     4) Creation of psTime of type UTC
 *
 *  @author  Ross Harman, MHPCC
 *  @author  Eric Van Alst, MHPCC
 *
 *  @version $Revision: 1.2 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2005-09-13 01:39:13 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include "pslib_strict.h"
#include "psTest.h"

#define ERROR_TOL  0.001

static psS32 testTimeLMST(void);
static psS32 testTimeLeapSecondDelta(void);
static psS32 testTimeIsLeapSecond(void);
static psS32 testTimeFromTT(void);
static psS32 testTimeFromUTC(void);

// Test Time 1 : May 9, 2005  00:00:00,0
//               MJD = 53499.000
//               JD = 2453499.5
// UTC Test Time 1
const psS64 testTime1SecondsUTC     = 1115596900;
const psU32 testTime1NanosecondsUTC = 0;
// Expected LMST  15:09:18
const psF64 testTime1LMST0          = 3.967604;

// Test Time 2 : May 9, 1995 00:00:00,0
//               MJD = 49846.00
//               JD = 2449846.5
// UTC Test Time 2
const psS64 testTime2SecondsUTC     = 799977600;
const psU32 testTime2NanosecondsUTC = 0;
// Expected leap second delta
const psS64 testTimeLeapSecondDelta1 = 3;

// Test Time 3: Jan 1, 1999 00:00:00,0
//              MJD = 51179
//              JD = 2451179.5
const psS64 testTime3SecondsUTC     = 915148800;
const psU32 testTime3NanosecondsUTC = 0;

testDescription tests[] = {
                              {testTimeLMST,000,"psTimeToLMST",0,false},
                              {testTimeLeapSecondDelta,000,"psTimeLeapSecondDelta",0,false},
                              {testTimeIsLeapSecond,000,"psTimeIsLeapSecond",0,false},
                              {testTimeFromTT,000,"psTimeFromTT",0,false},
                              {testTimeFromUTC,000,"psTimeFromUTC",0,false},
                              {NULL}
                          };
psS32 main(psS32 argc, char* argv[])
{
    psLogSetLevel(PS_LOG_INFO);

    // Initialize library internal structures
    psLibInit("pslib.config");

    if( !runTestSuite(stderr,"psTime",tests,argc,argv)) {
        return 1;
    }

    // Clean up library
    psLibFinalize();

    return 0;
}

psS32 testTimeLMST(void)
{
    psTime*       time      = NULL;
    psF64         lmst      = 0.0;

    // Attempt to get LMST with NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    lmst = psTimeToLMST(time,0);
    if(!isnan(lmst)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NaN as expected %lf",lmst);
        return 1;
    }

    // Attempt to get LMST with valid test time
    // Allocate test time
    time = psTimeAlloc(PS_TIME_UTC);
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    time->leapsecond = false;
    lmst = psTimeToLMST(time,0.0);
    if(fabs(lmst-testTime1LMST0) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"LMST %lf not as expected %lf",lmst,testTime1LMST0);
        return 2;
    }

    // Attempt to get LMST with invalid input time UT1
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for incorrect type");
    time->type = PS_TIME_UT1;
    lmst = psTimeToLMST(time,0.0);
    if(!isnan(lmst)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NaN as expected %lf",lmst);
        return 3;
    }
    psFree(time);

    return 0;
}

psS32 testTimeLeapSecondDelta(void)
{
    psTime*       time1   = NULL;
    psTime*       time2   = NULL;
    psS64         delta   = 0;

    // Attempt to get delta with NULL time1 argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    delta = psTimeLeapSecondDelta(time1,time2);
    if(delta != 0) {
        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
        return 1;
    }

    // Set test time 1
    time1 = psTimeAlloc(PS_TIME_UTC);
    time1->sec = testTime1SecondsUTC;
    time1->nsec = testTime1NanosecondsUTC;
    time1->leapsecond = false;

    // Attempt to get delta with NULL time2 argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    delta = psTimeLeapSecondDelta(time1,time2);
    if(delta != 0) {
        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
        return 2;
    }

    // Set test time 2 with invalid time
    time2 = psTimeAlloc(PS_TIME_UTC);
    time2->sec = 0;
    time2->nsec = 2e9;
    time2->leapsecond = false;

    // Attempt to get delta with invalid time2
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    delta = psTimeLeapSecondDelta(time1,time2);
    if(delta != 0) {
        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
        return 3;
    }

    // Set test time 2 valid
    time2->sec = testTime2SecondsUTC;
    time2->nsec = testTime2NanosecondsUTC;

    // Set test time 1 invalid
    time1->sec = 0;
    time1->nsec = 2e9;

    // Attempt to get delta with invalid time1
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    delta = psTimeLeapSecondDelta(time1,time2);
    if(delta != 0) {
        psError(PS_ERR_UNKNOWN,true,"Delta of %ld not as expected 0",(long int)delta);
        return 4;
    }

    // Set test time 1 to greater time
    time1->sec = testTime1SecondsUTC;
    time1->nsec = testTime1NanosecondsUTC;
    delta = psTimeLeapSecondDelta(time1,time2);
    if(delta != testTimeLeapSecondDelta1) {
        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected %ld",
                (long int)delta,(long int)testTimeLeapSecondDelta1);
        return 5;
    }

    // Set test time 1 to lesser time
    delta = psTimeLeapSecondDelta(time2,time1);
    if(delta != testTimeLeapSecondDelta1) {
        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected %ld",
                (long int)delta,(long int)testTimeLeapSecondDelta1);
        return 6;
    }

    // Attempt to get delta with times equal
    delta = psTimeLeapSecondDelta(time1,time1);
    if(delta != 0) {
        psError(PS_ERR_UNKNOWN,true,"Delta %ld not as expected 0",(long int)delta);
        return 7;
    }

    psFree(time2);
    psFree(time1);

    return 0;
}

psS32 testTimeIsLeapSecond(void)
{
    psTime*     time        = NULL;
    psBool      leapsecond  = false;

    // Attempt to determine if leap second with NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    leapsecond = psTimeIsLeapSecond(time);
    if(leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
        return 1;
    }

    // Set time
    time = psTimeAlloc(PS_TIME_TAI);
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    time->leapsecond = false;

    // Attempt to determine if leap second with non-UTC time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
    leapsecond = psTimeIsLeapSecond(time);
    if(leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
        return 2;
    }

    // Set time to UTC
    time->type = PS_TIME_UTC;

    // Attempt to determine if leap second with valid time
    leapsecond = psTimeIsLeapSecond(time);
    if(leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not a expected 0",leapsecond);
        return 3;
    }

    // Set time to UTC with leap second
    time->sec = testTime3SecondsUTC;
    time->nsec = testTime3NanosecondsUTC;
    leapsecond = psTimeIsLeapSecond(time);
    if(!leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not a expected 1",leapsecond);
        return 4;
    }

    // Set time to 1 second before a known leap second
    time->sec--;
    leapsecond = psTimeIsLeapSecond(time);
    if(leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
        return 5;
    }

    // Set time to 1 second after a known leap second
    time->sec +=2;
    leapsecond = psTimeIsLeapSecond(time);
    if(leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",leapsecond);
        return 6;
    }

    psFree(time);

    return 0;
}

psS32 testTimeFromTT(void)
{
    psTime*       time = NULL;

    // Attempt to create psTime with invalid time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    time = psTimeFromTT(0,2e9);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 1;
    }

    // Attempt to create psTime with valid time
    time = psTimeFromTT(testTime1SecondsUTC,testTime1NanosecondsUTC);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
        return 2;
    }
    if(time->type != PS_TIME_TT) {
        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
                time->type,PS_TIME_TT);
        return 3;
    }
    if((time->sec != testTime1SecondsUTC) || (time->nsec != testTime1NanosecondsUTC)) {
        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
        return 4;
    }
    psFree(time);

    return 0;
}

psS32 testTimeFromUTC(void)
{
    psTime*       time = NULL;

    // Attempt to create psTime with invalid time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    time = psTimeFromUTC(0,2e9,true);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 1;
    }

    // Attempt to create psTime with valid non-leapsecond time and leapsecond flag true
    time = psTimeFromUTC(testTime1SecondsUTC,testTime1NanosecondsUTC,true);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
        return 2;
    }
    if(time->type != PS_TIME_UTC) {
        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
                time->type,PS_TIME_UTC);
        return 3;
    }
    if((time->sec != testTime1SecondsUTC) || (time->nsec != testTime1NanosecondsUTC)) {
        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
        return 4;
    }
    if(time->leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 0",time->leapsecond);
        return 5;
    }
    psFree(time);

    // Attempt to create psTime with valid leapsecond time but leapsecond flag false
    time = psTimeFromUTC(testTime3SecondsUTC,testTime3NanosecondsUTC,false);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Return NULL when not expected");
        return 6;
    }
    if(time->type != PS_TIME_UTC) {
        psError(PS_ERR_UNKNOWN,true,"Type of time object %d not as expected %d",
                time->type,PS_TIME_UTC);
        return 7;
    }
    if((time->sec != testTime3SecondsUTC) || (time->nsec != testTime3NanosecondsUTC)) {
        psError(PS_ERR_UNKNOWN,true,"Seconds %ld nanoseconds %d not as expected seconds %ld nanoseconds %d",
                (long int)time->sec,time->nsec,(long int)testTime1SecondsUTC,testTime1NanosecondsUTC);
        return 8;
    }
    if(!time->leapsecond) {
        psError(PS_ERR_UNKNOWN,true,"Leapsecond flag %d not as expected 1",time->leapsecond);
        return 9;
    }


    psFree(time);

    return 0;
}

