//- ****************************************************************************
//-
//- Copyright 2009 National Technology & Engineering Solutions of Sandia, LLC
//- (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
//- Government retains certain rights in this software.
//-
//- BSD Open Source License
//- All rights reserved.
//-
//- Redistribution and use in source and binary forms, with or without
//- modification, are permitted provided that the following conditions are met:
//-
//-   1. Redistributions of source code must retain the above copyright notice,
//-      this list of conditions and the following disclaimer.
//-
//-   2. Redistributions in binary form must reproduce the above copyright
//-      notice, this list of conditions and the following disclaimer in the
//-      documentation and/or other materials provided with the distribution.
//-
//-   3. Neither the name of the copyright holder nor the names of its
//-      contributors may be used to endorse or promote products derived from
//-      this software without specific prior written permission.
//-
//- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
//- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//- POSSIBILITY OF SUCH DAMAGE.
//-
//- ****************************************************************************

// include statements
#include "slbm_C_shell.h"  // RSTT C library
#include <stdio.h>         // print to terminal
#include <math.h>          // math functions and constants
#include <stdlib.h>        // needed for exit codes

// declare some variables relevant to testing
int PASSED = 1;    // bool for whether all the tests passed (default true)
double EPS = 1e-6; // small value to allow for machine error differences

// define some constants for converting between degrees<-->radians
double DEG_PER_RAD = 180.0 / M_PI;
double RAD_PER_DEG = M_PI / 180.0;

// function to check if two floats are equal to some high precision
void assertIsEqual(double x, double y, const char *varname)
{
    // print status message
    printf("%-21s  %12.8f == %12.8f", varname, x, y);

    // if the two values are equal
    if (fabs(x-y) < EPS)
    {
        printf(" OK\n");
    }
    else
    {
        printf(" FAIL\n");
        PASSED = 0;  // set PASSED to false
    }
}

// error catching function
void try(int errorCode)
{
    // if there is an error code (i.e., function returns anything other than 0)
    if (errorCode != 0)
    {
        // initialize a character array to hold error messages
        char errorMsg[1000];

        // get the error message
        slbm_shell_getErrorMessage(errorMsg);

        // print the error message
        printf("ERROR %d:%s\n", errorCode, errorMsg);

        // exit with the error code
        exit(errorCode);

    }

}

// main program
int main()
{
    // print a quick message
    printf("======================================================\n"
           "Performing C tests...\n"
           "------------------------------------------------------\n"
           "Variable                 Calculated       Reference\n"
           "------------------------------------------------------\n");

    // arbitrary source (Meteor Crater, AZ)
    double srcLatDeg =   35.0274;  // latitude (degrees)
    double srcLonDeg = -111.0228;  // longitude (degrees)
    double srcDepKm  =    1.2345;  // depth (km)

    // arbitrary receiver (Albuquerque, NM)
    double rcvLatDeg =   35.1053;  // latitude (degrees)
    double rcvLonDeg = -106.6294;  // longitude (degrees)
    double rcvDepKm  =   -1.6000;  // depth (km)

    // convert lat/lon from degrees to radians
    double srcLatRad = srcLatDeg * RAD_PER_DEG;
    double srcLonRad = srcLonDeg * RAD_PER_DEG;
    double rcvLatRad = rcvLatDeg * RAD_PER_DEG;
    double rcvLonRad = rcvLonDeg * RAD_PER_DEG;

    // instantiate an RSTT object
    try(slbm_shell_create());

    // load the velocity model
    try(slbm_shell_loadVelocityModel("models/unittest.geotess"));

    // initialize variables for distance, travel time, and uncertainties
    double distRad, travelTimeSec, travelTimeUncertSec, travelTime1DUncertSec;

    // loop over each type of phase
    char *phases[4] = {"Pn", "Sn", "Pg", "Lg"};
    int i;
    for (i=0; i<4; ++i)
    {
        // print status message
        if (i > 0)
            printf("\n");
        printf("Computing values for %s phase...\n", phases[i]);

        // create a great circle from source to the receiver
        try(slbm_shell_createGreatCircle(phases[i],
            &srcLatRad, &srcLonRad, &srcDepKm,
            &rcvLatRad, &rcvLonRad, &rcvDepKm));

        // get the distance, travel time, and uncertainties from source --> receiver
        try(slbm_shell_getDistance(&distRad));
        try(slbm_shell_getTravelTime(&travelTimeSec));
        try(slbm_shell_getTTUncertainty(&travelTimeUncertSec));
        try(slbm_shell_getTTUncertainty1D(&travelTime1DUncertSec));

        // make sure all the values are correct
        switch (i)
        {
            case 0:
                assertIsEqual(distRad,                0.06290927, "distRad");
                assertIsEqual(travelTimeSec,         56.88549071, "travelTimeSec");
                assertIsEqual(travelTimeUncertSec,    3.12378957, "travelTimeUncertSec");
                assertIsEqual(travelTime1DUncertSec,  1.15423530, "travelTime1DUncertSec");
                break;
            case 1:
                assertIsEqual(distRad,                0.06290927, "distRad");
                assertIsEqual(travelTimeSec,         98.95213093, "travelTimeSec");
                assertIsEqual(travelTimeUncertSec,    6.29314026, "travelTimeUncertSec");
                assertIsEqual(travelTime1DUncertSec,  2.47666863, "travelTime1DUncertSec");
                break;
            case 2:
                assertIsEqual(distRad,                0.06290927, "distRad");
                assertIsEqual(travelTimeSec,         70.40335768, "travelTimeSec");
                assertIsEqual(travelTimeUncertSec,   15.58790453, "travelTimeUncertSec");
                assertIsEqual(travelTime1DUncertSec,  1.61320256, "travelTime1DUncertSec");
                break;
            case 3:
                assertIsEqual(distRad,                0.06290927, "distRad");
                assertIsEqual(travelTimeSec,        119.65236722, "travelTimeSec");
                assertIsEqual(travelTimeUncertSec,   20.08714726, "travelTimeUncertSec");
                assertIsEqual(travelTime1DUncertSec,  1.98573827, "travelTime1DUncertSec");
                break;
        }
    }

    // return
    if (PASSED)
    {
        printf("------------------------------------------------------\n"
               "C tests PASSED.\n"
               "======================================================\n");
        return 0;
    }
    else
    {
        printf("------------------------------------------------------\n"
               "C tests FAILED.\n"
               "======================================================\n");
        return 1;
    }

}
