// import statements
import java.io.File;  // file, path, and environment variable stuff
import static java.lang.Math.*;  // mathematics (degrees<-->radians)
import gov.sandia.gnem.slbmjni.SlbmInterface;  // RSTT library
import gov.sandia.gnem.slbmjni.SLBMException;  // RSTT error catching


// main class
public class java_example
{
    // annoyingly long method which will run at startup and try to load slbmjni,
    // first from the system paths, then by searching environment variables
    // ($RSTT_ROOT, $RSTT_HOME, $SLBM_ROOT, $SLBM_HOME). this also has to take
    // into account multiple possible file extensions on MacOS (.dylib, .jnilib)
    static
    {
        // first we'll just try and load the library
        try
        {
            System.loadLibrary("slbmjni");
        }

        // if that didn't work, we'll start checking environmental variables
        catch (java.lang.UnsatisfiedLinkError e)
        {
            // get the filename of the library we're looking for
            String libName = System.mapLibraryName("slbmjni");  // e.g., "libslbmjni.so"
            String libBase = libName.split("\\.")[0];  // file basename
            String libExt  = libName.split("\\.")[1];  // file extension

            // make our list of env vars to search, in preferred order
            String envVars[] = {"RSTT_ROOT", "RSTT_HOME", "SLBM_ROOT", "SLBM_HOME"};

            // initialize a boolean for when the library has loaded. if we have
            // successfully loaded it, we'll end the whole method
            boolean jniLoaded = false;

            // loop through each environment variable and look for slbmjni
            for (String env : envVars)
            {
                // try and get the environment variable
                String rootDir = System.getenv(env);

                // move on if it wasn't set
                if (rootDir == null)
                    continue;

                // first check if libName exists
                if (new File(rootDir + "/lib/" + libBase + "." + libExt).exists())
                    System.load(rootDir + "/lib/" + libBase + "." + libExt);  // load it

                // if that file doesn't exist, look for libslbmjni.jnilib
                else if (new File(rootDir + "/lib/" + libBase + ".jnilib").exists())
                    System.load(rootDir + "/lib/" + libBase + ".jnilib");  // load it

                // if that doesn't exist, we'll move onto the next variable
                else
                    continue;

                // we made it this far, so we must have loaded the library!
                jniLoaded = true;  // set our boolean to true
                break;             // break out of the loop

            }

            // if, we still haven't loaded slbmjni, throw a helpful error message
            if (!jniLoaded)
            {
                // append some helpful info to the error message
                String errMsg = e.getMessage() + " or [$RSTT_ROOT/lib, $RSTT_HOME/lib, $SLBM_ROOT/lib, $SLBM_HOME/lib]";

                // make a new UnsatisfiedLinkError with our updated message
                UnsatisfiedLinkError ex = new UnsatisfiedLinkError(errMsg);

                // print out the stacktrace, some helpful info, and exit
                ex.printStackTrace();
                System.out.println("Did you try adding '-Djava.library.path=\"/path/to/rstt/lib\"' to your 'java' command?");
                System.out.println("Alternatively, set $RSTT_ROOT, $RSTT_HOME, $SLBM_ROOT, or $SLBM_HOME environment variables.");
                System.exit(1);
            }
        }
    }

    // main method
    public static void main(String args[])
    {
        // 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 = toRadians(srcLatDeg);
            double srcLonRad = toRadians(srcLonDeg);
            double rcvLatRad = toRadians(rcvLatDeg);
            double rcvLonRad = toRadians(rcvLonDeg);

            // instantiate an RSTT object
            SlbmInterface slbm = new SlbmInterface();

            // load 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
            distRad       = slbm.getDistance();      // compute distance (rad) and store its value
            travelTimeSec = slbm.getTravelTime();    // compute travel time (sec) and store its value
            distDeg       = toDegrees(distRad);      // convert radians --> degrees

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

            // print our results
            System.out.format("\n");
            System.out.format("Java Example:\n");
            System.out.format("--------------------------------\n");
            System.out.format("Distance:          %9.4f deg\n", distDeg);
            System.out.format("Travel Time:       %9.4f sec\n", travelTimeSec);
            System.out.format("Uncertainty:       %9.4f sec\n", travelTimeUncertSec);
            System.out.format("Uncertainty (1D):  %9.4f sec\n", travelTime1DUncertSec);
            System.out.format("\n");

        }

        // catch possible exceptions
        catch (SLBMException e)
        {
            // print info about the exception and exit
            e.printStackTrace();
            System.exit(1);
        }

        // catch other exceptions
        catch (Exception e)
        {
            // print info about the exception and exit
            e.printStackTrace();
            System.exit(1);
        }

        // return successfully
        System.exit(0);

    }

}
