package testslbmjava;

import gov.sandia.gnem.slbmjni.GridProfile;
import gov.sandia.gnem.slbmjni.Point;
import gov.sandia.gnem.slbmjni.SLBMException;
import gov.sandia.gnem.slbmjni.SlbmInterface;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import static java.lang.Math.*;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestSLBMFormats
{

    private static SlbmInterface slbm;

    private static int verbosity = 1;

    private static final double dtr = 3.1415926535897932384626 / 180.;
    private static final double rtd = 180. / 3.1415926535897932384626;

    private static HashMap<Point, GridProfile> profiles;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        File here = new File(".");
        System.out.println("Running from directory "+here.getCanonicalPath());

        // Load the c++ SLBM library libslbm.so into memory.
        System.loadLibrary("slbmjni");
        System.out.println("setUpBeforeClass()  slbmjni loaded");
        slbm = new SlbmInterface();

        System.out.println("SlbmInterface version " + slbm.getVersion());

        slbm.loadVelocityModel("models/rstt.2.3");

        profiles = new HashMap<Point, GridProfile>(slbm.getNGridNodes());

        for (int i=0; i<slbm.getNGridNodes(); ++i)
        {
            GridProfile profile = slbm.getGridData(i);
            profiles.put(new Point(profile), profile);
        }

        System.out.println("Profiles.size = "+profiles.size());

    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        slbm.close();
        if (verbosity > 0) System.out.println("Done");
    }

    @Test
    public void testGetVersion()
    {
        if (verbosity > 0)
            System.out.println("testGetVersion()");
        assert (slbm.getVersion().equals("3.2.0"));
    }

    @Test
    public void testFormat1() throws SLBMException, IOException
    {
        if (verbosity > 0)
            System.out.println("testFormat1()");

        File f = new File("format1.txt");
        f.delete();
        slbm.saveVelocityModel(f.getCanonicalPath(), 1);
        assert(f.exists());

        SlbmInterface fmt = new SlbmInterface();
        fmt.loadVelocityModel(f.getCanonicalPath());

        boolean eql = compareProfiles(fmt);
        assert(eql);

        f = new File("format12");
        f.delete();
        f.mkdir();
        fmt.saveVelocityModel(f.getCanonicalPath(), 2);
        File target = new File(f, "geostacks");
        assert(target.exists());

        SlbmInterface fmt2 = new SlbmInterface();
        fmt2.loadVelocityModel(f.getCanonicalPath());
        eql = compareProfiles(fmt2);
        assert(eql);

        fmt.close();
        fmt2.close();
    }

    @Test
    public void testFormat2() throws SLBMException, IOException
    {
        if (verbosity > 0)
            System.out.println("testFormat2()");

        File f = new File("format2");
        f.delete();
        f.mkdir();
        slbm.saveVelocityModel(f.getCanonicalPath(), 2);
        File target = new File(f, "geostacks");
        assert(target.exists());

        SlbmInterface fmt = new SlbmInterface();
        fmt.loadVelocityModel(f.getCanonicalPath());

        boolean eql = compareProfiles(fmt);
        assert(eql);

        f = new File("format22");
        f.delete();
        f.mkdir();
        fmt.saveVelocityModel(f.getCanonicalPath(), 2);
        target = new File(f, "geostacks");
        assert(target.exists());

        SlbmInterface fmt2 = new SlbmInterface();
        fmt2.loadVelocityModel(f.getCanonicalPath());
        eql = compareProfiles(fmt2);
        assert(eql);

        fmt.close();
        fmt2.close();
    }

    @Test
    public void testFormat3() throws SLBMException, IOException
    {
        if (verbosity > 0)
            System.out.println("testFormat3()");

        // slbm was loaded from main model directory rstt.2.3

        // save the model in format 3 (version 3 directory format)
        // in a directory called 'format3'
        File f = new File("format3");
        f.delete();
        f.mkdir();
        slbm.saveVelocityModel(f.getCanonicalPath(), 3);
        File target = new File(f, "geotessmodel");
        assert(target.exists());

        // make a new SlbmInterface and load the model from
        // format3 (version 3 directory format).
        SlbmInterface fmt = new SlbmInterface();
        fmt.loadVelocityModel(f.getCanonicalPath());
        boolean eql = compareProfiles(fmt);
        assert(eql);

        // save the model loaded from format3 to a new
        // directory in format 2 (version 2 binary directory format)
        f = new File("format32");
        f.delete();
        f.mkdir();
        fmt.saveVelocityModel(f.getCanonicalPath(), 2);
        target = new File(f, "geostacks");
        assert(target.exists());

        // load it back in.  This model will have originated from
        // rstt.2.3, been saved in format3, loaded from format 3,
        // saved in format 2, loaded from format 2.
        SlbmInterface fmt2 = new SlbmInterface();
        fmt2.loadVelocityModel(f.getCanonicalPath());
        // compare it to the original.
        eql = compareProfiles(fmt2);
        assert(eql);

        fmt.close();
        fmt2.close();
    }

    @Test
    public void testFormat4binary() throws SLBMException, IOException
    {
        if (verbosity > 0)
            System.out.println("testFormat4binary()");

        File f = new File("format4.binary");
        f.delete();
        slbm.saveVelocityModel(f.getCanonicalPath(), 4);
        assert(f.exists());

        SlbmInterface fmt = new SlbmInterface();
        fmt.loadVelocityModel(f.getCanonicalPath());

        boolean eql = compareProfiles(fmt);
        assert(eql);

        f = new File("format42b");
        f.delete();
        f.mkdir();
        fmt.saveVelocityModel(f.getCanonicalPath(), 2);
        File target = new File(f, "geostacks");
        assert(target.exists());

        SlbmInterface fmt2 = new SlbmInterface();
        fmt2.loadVelocityModel(f.getCanonicalPath());
        eql = compareProfiles(fmt2);
        assert(eql);

        fmt.close();
        fmt2.close();
    }

    @Test
    public void testFormat4ascii() throws SLBMException, IOException
    {
        if (verbosity > 0)
            System.out.println("testFormat4ascii()");

        File f = new File("format4.ascii");
        f.delete();
        slbm.saveVelocityModel(f.getCanonicalPath(), 4);
        assert(f.exists());

        SlbmInterface fmt = new SlbmInterface();
        fmt.loadVelocityModel(f.getCanonicalPath());

        boolean eql = compareProfiles(fmt);
        assert(eql);

        f = new File("format42a");
        f.delete();
        f.mkdir();
        fmt.saveVelocityModel(f.getCanonicalPath(), 2);
        File target = new File(f, "geostacks");
        assert(target.exists());

        SlbmInterface fmt2 = new SlbmInterface();
        fmt2.loadVelocityModel(f.getCanonicalPath());
        eql = compareProfiles(fmt2);
        assert(eql);

        fmt.close();
        fmt2.close();
    }

    private boolean compareProfiles(SlbmInterface slbm2) throws SLBMException
    {
        boolean passed = true;
        int nmissing = 0;
        int notEqual=0;

        if (slbm2.getNGridNodes() != slbm.getNGridNodes())
        {
            System.out.printf("NGridNodes are not equal  %d != %d%n",
                    slbm2.getNGridNodes(), slbm.getNGridNodes());
            return false;
        }

        for (int i=0; i<slbm2.getNGridNodes(); ++i)
        {
            GridProfile p2 = slbm2.getGridData(i);
            GridProfile profile = profiles.get(new Point(p2));

            //if (i < 50) System.out.println(p2.getLatLon());

            if (profile == null)
            {
                System.out.printf("Missing profile[%d] at %s%n", i, p2.getLatLon());
                passed = false;
                ++nmissing;
            }
            else if (!p2.equals(profile))
            {
                System.out.println("Profiles are not equal "+profile.getLatLon());
                System.out.println(profile);
                System.out.println(p2);
                passed = false;
                ++notEqual;
            }
            if (nmissing > 10 || notEqual > 10)
                return false;
        }
        //System.out.println("nmissing = "+nmissing);


//		GridProfile p2 = slbm2.getGridData(40948);
//		GridProfile profile = slbm.getGridData(40347);
//
//		Point x1 = new Point(profile);
//		Point x2 = new Point(p2);
//
//		System.out.println(String.format("%1.12f %1.12f", toDegrees(p2.lat), toDegrees(p2.lon)));
//		System.out.println(String.format("%1.12f %1.12f", toDegrees(profile.lat), toDegrees(profile.lon)));
//
//		System.out.println(String.format("%1.7f %1.7f %1.7f", x1.u[0], x1.u[1], x1.u[2]));
//		System.out.println(String.format("%1.7f %1.7f %1.7f", x2.u[0], x2.u[1], x2.u[2]));
//
//		System.out.printf("%1.15f%n", Point.dot(x1.u, x2.u));
//
//		System.out.println(p2);
//		System.out.println(profile);


        return passed;
    }

}
