/** \file
 *
 *  Contains unit tests for the Location class
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef _LOCATION_TEST_H_
#define _LOCATION_TEST_H_


#include "Location.h"
#include "utils/AuvMath.h"

#include <cxxtest/TestSuite.h>

/**
 *  Unit tests for Location class
 *
 *  \ingroup data
 */
class Location_Test : public CxxTest::TestSuite
{
public:

    /// Test Location::GetBearing
    void testGetBearing( void )
    {
        float bearing = Location::GetBearing( D2R( 36.8 ), D2R( -121.8 ), D2R( 36.85 ), D2R( -121.85 ) );
        TS_ASSERT_DELTA( R2D( bearing ), -38.6611, 0.0002 );
    }

    /// Test Location::GetDistance
    void testGetDistance( void )
    {
        double shortDist = 7121.457;
        double distance = Location::GetDistance( D2R( 36.8 ), D2R( -121.8 ), D2R( 36.85 ), D2R( -121.85 ) );
        TS_ASSERT_DELTA( distance, shortDist, shortDist / 10000 );
        double medDist( 2280495.4 );
        distance = Location::GetDistance( D2R( 36.8 ), D2R( -121.8 ), D2R( 50 ), D2R( -100 ) );
        TS_ASSERT_DELTA( distance, medDist, medDist / 500 );
        double longDist( 15654367.0 );
        distance = Location::GetDistance( D2R( 36.8 ), D2R( -121.8 ), D2R( -5.0 ), D2R( 33.0 ) );
        TS_ASSERT_DELTA( distance, longDist, longDist / 500 );
    }

    /// Test Location::GetDistanceFast
    void testGetDistanceFast( void )
    {
        double shortDist = 7121.457;
        double distance = Location::GetDistanceFast( D2R( 36.8 ), D2R( -121.8 ), D2R( 36.85 ), D2R( -121.85 ) );
        TS_ASSERT_DELTA( distance, shortDist, shortDist / 10000 );
        double medDist( 2280495.4 );
        distance = Location::GetDistanceFast( D2R( 36.8 ), D2R( -121.8 ), D2R( 50 ), D2R( -100 ) );
        TS_ASSERT_DELTA( distance, medDist, medDist / 400 );
    }

    /// Test Location::AtBearing
    void testAtBearing( void )
    {
        double latitude = D2R( 36.8 );
        double longitude = D2R( -121.8 );
        Location::AtBearing( D2R( -38.6611 ), 7122.0, latitude, longitude );
        TS_ASSERT_DELTA( R2D( latitude ), 36.85, 0.0001 );
        TS_ASSERT_DELTA( R2D( longitude ), -121.85, 0.0001 );
    }

    /// Test Location::NorthingsDelta
    void testNorthingsDelta( void )
    {
        double latitude = D2R( 36.8 );
        float northingsDelta = 1000;
        latitude = Location::NorthingsDelta( latitude, northingsDelta );
        TS_ASSERT_DELTA( R2D( latitude ), 36.80889, 0.0002 );
    }

    /// Test Location::CalcVector
    void testCalcVector( void )
    {
        Location location( Units::DEGREE, 41.55743102, -70.71462296 ), location2;
        Point2D ne;
        double north, east;
        for( int i = 0 ; i < 200 ; ++i )
        {
            north = 2000.0 * ( ( double )rand() / RAND_MAX * 2.0 - 1.0 );
            east = 2000.0 * ( ( double )rand() / RAND_MAX * 2.0 - 1.0 );
            location2 = location;
            location2.delta( north, east );
            ne = location.calcVector( location2 );
            TS_ASSERT_DELTA( ne.getX(), north, 0.2 );
            TS_ASSERT_DELTA( ne.getY(), east, 0.2 );
        }
        location2 = Location( Units::DEGREE, 41.57301547, -70.69302245 );
        ne = location.calcVector( location2 );
        printf( "In testCalcVector, location3 n=%g, e=%g\n", ne.getX(), ne.getY() );
        location2 = Location( Units::DEGREE, 41.550804, -70.704618 );
        ne = location.calcVector( location2 );
        printf( "In testCalcVector, location4 n=%g, e=%g\n", ne.getX(), ne.getY() );
    }

    /// Test Location::IntersectingCircles
    void testIntersectingCircles( void )
    {
        Location c1;
        double r1;
        Location c2;
        double r2;
        Location intersect1, intersect2;
        bool intersecting = false;
        Location test1, test2;
        double d1, d2;

        c1 = Location( Units::DEGREE, 36.8, -121.8 );
        c2 = c1;
        c2.delta( 0, 150 );
        r1 = r2 = 100;
        test1 = test2 = c1;
        test1.delta( 66.144, 75 );
        test2.delta( -66.144, 75 );
        intersecting = Location::IntersectingCircles( c1, r1, c2, r2, intersect1, intersect2 );

        d1 = test1.getDistance( intersect1 );
        d2 = test2.getDistance( intersect2 );

        TS_ASSERT( intersecting );
        TS_ASSERT_LESS_THAN( d1, 0.01 );
        TS_ASSERT_LESS_THAN( d2, 0.01 );
    }

    /// Test Location::DistanceFromBaseline
    void testDistanceFromBaseline( void )
    {
        Location loc0, loc1, loc2;
        double dist;

        // A line due south
        loc0 = Location( Units::DEGREE, 36.8, -121.8 );
        loc1 = Location( Units::DEGREE, 36.7, -121.9 );
        loc2 = Location( Units::DEGREE, 36.7, -121.7 );
        dist = loc0.distanceFromBaseline( loc1, loc2 );

        TS_ASSERT_DELTA( dist, 111111 * 0.1, 10.0 );

        loc0 = Location( Units::DEGREE, 36.7, -121.8 );
        dist = loc0.distanceFromBaseline( loc1, loc2 );
        TS_ASSERT_DELTA( dist, 0, 5.0 );

        // A line due west
        loc0 = Location( Units::DEGREE, 36.8, -121.8 );
        loc1 = Location( Units::DEGREE, 36.7, -121.9 );
        loc2 = Location( Units::DEGREE, 36.9, -121.9 );
        dist = loc0.distanceFromBaseline( loc1, loc2 );
        TS_ASSERT_DELTA( dist, 111111 * 0.1 * cos( 36.8 * M_PI / 180 ), 100.0 );

        loc0 = Location( Units::DEGREE, 36.8, -121.9 );
        dist = loc0.distanceFromBaseline( loc1, loc2 );
        TS_ASSERT_DELTA( dist, 0, 1.0 );

    }
};

#endif // _LOCATION_TEST_H