// Boost.Geometry (aka GGL, Generic Geometry Library) 
// Unit Test

// Copyright (c) 2010-2011 Barend Gehrels, 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)

#ifndef BOOST_GEOMETRY_TEST_DISTANCE_HPP
#define BOOST_GEOMETRY_TEST_DISTANCE_HPP

#include <geometry_test_common.hpp>

#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/domains/gis/io/wkt/read_wkt.hpp>
#include <boost/geometry/strategies/strategies.hpp>


// Define a custom distance strategy
// For this one, the "taxicab" distance,
// see http://en.wikipedia.org/wiki/Taxicab_geometry

// For a point-point-distance operation, one typename Point is enough.
// For a point-segment-distance operation, there is some magic inside
// using another point type and casting if necessary. Therefore,
// two point-types are necessary.
template <typename P1, typename P2 = P1>
struct taxicab_distance
{
    static inline typename bg::coordinate_type<P1>::type apply(
                    P1 const& p1, P2 const& p2)
    {
        using bg::get;
        using bg::math::abs;
        return abs(get<0>(p1) - get<1>(p2))
            + abs(get<1>(p1) - get<1>(p2));
    }
};



namespace boost { namespace geometry { namespace strategy { namespace distance { namespace services
{

template <typename P1, typename P2>
struct tag<taxicab_distance<P1, P2> >
{
    typedef strategy_tag_distance_point_point type;
};


template <typename P1, typename P2>
struct return_type<taxicab_distance<P1, P2> >
{
    typedef typename coordinate_type<P1>::type type;
};


template<typename P1, typename P2, typename PN1, typename PN2>
struct similar_type<taxicab_distance<P1, P2>, PN1, PN2>
{
    typedef taxicab_distance<PN1, PN2> type;
};


template<typename P1, typename P2, typename PN1, typename PN2>
struct get_similar<taxicab_distance<P1, P2>, PN1, PN2>
{
    static inline typename similar_type
        <
            taxicab_distance<P1, P2>, PN1, PN2
        >::type apply(taxicab_distance<P1, P2> const& )
    {
        return taxicab_distance<PN1, PN2>();
    }
};

template <typename P1, typename P2>
struct comparable_type<taxicab_distance<P1, P2> >
{
    typedef taxicab_distance<P1, P2> type;
};

template <typename P1, typename P2>
struct get_comparable<taxicab_distance<P1, P2> >
{
    static inline taxicab_distance<P1, P2> apply(taxicab_distance<P1, P2> const& input)
    {
        return input;
    }
};

template <typename P1, typename P2>
struct result_from_distance<taxicab_distance<P1, P2> >
{
    template <typename T>
    static inline typename coordinate_type<P1>::type apply(taxicab_distance<P1, P2> const& , T const& value)
    {
        return value;
    }
};


}}}}} // namespace bg::strategy::distance::services





template <typename Geometry1, typename Geometry2>
void test_distance(Geometry1 const& geometry1,
            Geometry2 const& geometry2,
            long double expected_distance)
{
    typename bg::default_distance_result<Geometry1>::type distance = bg::distance(geometry1, geometry2);

#ifdef GEOMETRY_TEST_DEBUG
    std::ostringstream out;
    out << typeid(typename bg::coordinate_type<Geometry1>::type).name()
        << std::endl
        << typeid(typename bg::default_distance_result<Geometry1>::type).name()
        << std::endl
        << "distance : " << bg::distance(geometry1, geometry2)
        << std::endl;
    std::cout << out.str();
#endif

    BOOST_CHECK_CLOSE(distance, expected_distance, 0.0001);
}


template <typename Geometry1, typename Geometry2>
void test_geometry(std::string const& wkt1, std::string const& wkt2, double expected_distance)
{
    Geometry1 geometry1;
    bg::read_wkt(wkt1, geometry1);
    Geometry2 geometry2;
    bg::read_wkt(wkt2, geometry2);

    test_distance(geometry1, geometry2, expected_distance);
}


#endif
