// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2011 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2011 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)


#include <geometry_test_common.hpp>


#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/distance.hpp>

#include <boost/geometry/strategies/spherical/distance_haversine.hpp>
#include <boost/geometry/strategies/spherical/distance_cross_track.hpp>

#include <boost/geometry/strategies/concepts/distance_concept.hpp>

#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/segment.hpp>


// This test is GIS oriented. 


template <typename Point, typename LatitudePolicy>
void test_distance(
            typename bg::coordinate_type<Point>::type const& lon1, 
            typename bg::coordinate_type<Point>::type const& lat1,
            typename bg::coordinate_type<Point>::type const& lon2, 
            typename bg::coordinate_type<Point>::type const& lat2,
            typename bg::coordinate_type<Point>::type const& lon3, 
            typename bg::coordinate_type<Point>::type const& lat3,
            typename bg::coordinate_type<Point>::type const& radius, 
            typename bg::coordinate_type<Point>::type const& expected, 
            typename bg::coordinate_type<Point>::type const& tolerance)
{
    typedef bg::strategy::distance::cross_track
        <
            Point,
            Point
        > strategy_type;
    typedef typename bg::strategy::distance::services::return_type
        <
            strategy_type
        >::type return_type;


    BOOST_CONCEPT_ASSERT
        (
            (bg::concept::PointSegmentDistanceStrategy<strategy_type>)
        );


    Point p1, p2, p3;
    bg::assign_values(p1, lon1, LatitudePolicy::apply(lat1));
    bg::assign_values(p2, lon2, LatitudePolicy::apply(lat2));
    bg::assign_values(p3, lon3, LatitudePolicy::apply(lat3));


    strategy_type strategy;
    return_type d = strategy.apply(p1, p2, p3);

    BOOST_CHECK_CLOSE(radius * d, expected, tolerance);

    // Test specifying radius explicitly
    strategy_type strategy_radius(radius);
    d = strategy_radius.apply(p1, p2, p3);
    BOOST_CHECK_CLOSE(d, expected, tolerance);


    // Test the "default strategy" registration
    bg::model::referring_segment<Point const> segment(p2, p3);
    d = bg::distance(p1, segment);
    BOOST_CHECK_CLOSE(radius * d, expected, tolerance);
}


template <typename Point, typename LatitudePolicy>
void test_all()
{
    typename bg::coordinate_type<Point>::type const average_earth_radius = 6372795.0;

    // distance (Paris <-> Amsterdam/Barcelona), 
    // with coordinates rounded as below ~87 km
    // is equal to distance (Paris <-> Barcelona/Amsterdam)
    typename bg::coordinate_type<Point>::type const p_to_ab = 86.798321 * 1000.0;
    test_distance<Point, LatitudePolicy>(2, 48, 4, 52, 2, 41, average_earth_radius, p_to_ab, 0.1);
    test_distance<Point, LatitudePolicy>(2, 48, 2, 41, 4, 52, average_earth_radius, p_to_ab, 0.1);
}


int test_main(int, char* [])
{
    test_all<bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> >, geographic_policy >();

    // NYI: haversine for mathematical spherical coordinate systems
    // test_all<bg::model::point<double, 2, bg::cs::spherical<bg::degree> >, mathematical_policya >();

#if defined(HAVE_TTMATH)
    typedef ttmath::Big<1,4> tt;
    //test_all<bg::model::point<tt, 2, bg::cs::geographic<bg::degree> >, geographic_policy>();
#endif

    return 0;
}
