// include statements
#include "SlbmInterface.h"  // RSTT library
#include "SLBMException.h"  // RSTT error catching
#include <iostream>         // print to terminal
#include <string>           // strings
#include <cmath>            // math functions and constants

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

// declare namespace (for convenience)
using namespace slbm;

// main program
int main()
{
    // wrap everything in a `try-catch` block
    try
    {
        // declare the relative path to the model file we're going to be using
        string modelPath = "../models/pdu202009Du.geotess";

        // choose an arbitrary phase (Pn, Pg, Sn, or Lg)
        string phase = "Pn";

        // 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
        SlbmInterface slbm;

        // load the velocity model
        slbm.loadVelocityModel(modelPath);

        // create a great circle from source to the receiver
        slbm.createGreatCircle(phase,
            srcLatRad, srcLonRad, srcDepKm,
            rcvLatRad, rcvLonRad, rcvDepKm);

        // get the distance and travel time from source --> receiver
        double distRad, distDeg, travelTimeSec;  // instantiate variables
        slbm.getDistance(distRad);               // compute distance (rad) and store its value
        slbm.getTravelTime(travelTimeSec);       // compute travel time (sec) and store its value
        distDeg = distRad * DEG_PER_RAD;         // convert radians --> degrees

        // get the travel time uncertainty
        double travelTimeUncertSec,    // path-dependent travel time uncertainty (sec)
               travelTime1DUncertSec;  // old-style 1D travel time uncertainty (sec)
        slbm.getTravelTimeUncertainty(travelTimeUncertSec);      // compute uncertainty
        slbm.getTravelTimeUncertainty1D(travelTime1DUncertSec);  // compute 1D uncertainty

        // print our results
        cout << fixed << setprecision(4) << endl
             << "C++ Example:" << endl
             << "--------------------------------" << endl
             << "Distance:          " << setw(9) << distDeg << " deg" << endl
             << "Travel Time:       " << setw(9) << travelTimeSec << " sec" << endl
             << "Uncertainty:       " << setw(9) << travelTimeUncertSec << " sec" << endl
             << "Uncertainty (1D):  " << setw(9) << travelTime1DUncertSec << " sec" << endl
             << endl;

    }

    // catch possible exceptions
    catch (const SLBMException& e)
    {
        // print the exception
        cout << __FILE__ << ":" << __LINE__ << endl
             << e.emessage << endl;

        // return with error
        cout << "Exited with error code " << 1 << endl;
        return e.ecode;

    }
    catch (const GeoTessException& e)
    {
        // print the exception
        cout << __FILE__ << ":" << __LINE__ << endl
             << e.emessage << endl;

        // return with error
        cout << "Exited with error code " << e.ecode << endl;
        return e.ecode;

    }
    catch (...)
    {
        // print the exception
        cout << __FILE__ << ":" << __LINE__ << endl
             << "ERROR: An unidentified error has occurred." << endl;

        // return with error
        cout << "Exited with error code " << 1 << endl;
        return 1;
    }

    // return successfully
    return 0;

}
