/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2015      Nyall Dawson <nyall dot dawson at gmail dot com>
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

//
// Test Suite for geos::algorithm::MinimumDiameter

#include <tut/tut.hpp>
// geos
#include <geos/geom/Coordinate.h>
#include <geos/algorithm/MinimumDiameter.h>
#include <geos/io/WKTReader.h>
#include <geos/io/WKTWriter.h>
#include <geos/geom/Geometry.h>
// std
#include <sstream>
#include <string>
#include <memory>

namespace tut
{
    //
    // Test Group
    //

    // dummy data, not used
    struct test_minimumdiameter_data {
      typedef geos::geom::Geometry Geometry;
      typedef std::unique_ptr<geos::geom::Geometry> GeomPtr;

      typedef geos::geom::Coordinate Coordinate;
      typedef geos::algorithm::MinimumDiameter MinimumDiameter;

      geos::io::WKTReader reader;
      std::unique_ptr<Geometry> geom;

      test_minimumdiameter_data()
      {}

    };

    typedef test_group<test_minimumdiameter_data> group;
    typedef group::object object;

    group test_minimumdiameter_data_group("geos::algorithm::MinimumDiameter");

    //
    // Test Cases
    //

    // Test of getMinimumRectangle
    template<>
    template<>
    void object::test<1>()
    {
        GeomPtr geom(reader.read("POLYGON ((0 0, 0 20, 20 20, 20 0, 0 0))"));
        ensure(nullptr != geom.get());

        geos::algorithm::MinimumDiameter m(geom.get());
        GeomPtr minRect( m.getMinimumRectangle() );
        ensure(nullptr != minRect.get());

        GeomPtr expectedGeom(reader.read("POLYGON ((0 0, 20 0, 20 20, 0 20, 0 0))"));
        ensure(nullptr != expectedGeom.get());

        ensure( minRect.get()->equalsExact(expectedGeom.get()) );
    }

    // Test with expected rotated rectangle
    template<>
    template<>
    void object::test<2>()
    {
        GeomPtr geom(reader.read("POLYGON ((0 5, 5 10, 10 5, 5 0, 0 5))"));
        ensure(nullptr != geom.get());

        geos::algorithm::MinimumDiameter m(geom.get());
        GeomPtr minRect( m.getMinimumRectangle() );
        ensure(nullptr != minRect.get());

        GeomPtr expectedGeom(reader.read("POLYGON ((5 0, 10 5, 5 10, 0 5, 5 0))"));
        ensure(nullptr != expectedGeom.get());

        ensure( minRect.get()->equalsExact(expectedGeom.get()) );
    }

    // Test with EMPTY input
    template<>
    template<>
    void object::test<3>()
    {
        GeomPtr geom(reader.read("POLYGON EMPTY"));
        ensure(nullptr != geom.get());

        geos::algorithm::MinimumDiameter m(geom.get());
        GeomPtr minRect( m.getMinimumRectangle() );
        ensure(nullptr != minRect.get());

        GeomPtr expectedGeom(reader.read("POLYGON EMPTY"));
        ensure(nullptr != expectedGeom.get());

        ensure( minRect.get()->equalsExact(expectedGeom.get()) );
    }

    // Test with Point input
    template<>
    template<>
    void object::test<4>()
    {
        GeomPtr geom(reader.read("Point(1 2)"));
        ensure(nullptr != geom.get());

        geos::algorithm::MinimumDiameter m(geom.get());
        GeomPtr minRect( m.getMinimumRectangle() );
        ensure(nullptr != minRect.get());

        GeomPtr expectedGeom(reader.read("Point(1 2)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minRect.get()->equalsExact(expectedGeom.get()) );
    }

    // Test with LineString input
    template<>
    template<>
    void object::test<5>()
    {
        GeomPtr geom(reader.read("LineString(1 2, 2 4)"));
        ensure(nullptr != geom.get());

        geos::algorithm::MinimumDiameter m(geom.get());
        GeomPtr minRect( m.getMinimumRectangle() );
        ensure(nullptr != minRect.get());

        GeomPtr expectedGeom(reader.read("LineString(1 2, 2 4)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minRect.get()->equalsExact(expectedGeom.get()) );
    }

    // Test minimumDiameter with Point input
    template<>
    template<>
    void object::test<6>()
    {
        GeomPtr geom(reader.read("POINT (0 240)"));
        ensure(nullptr != geom.get());

        GeomPtr minDiameter( geos::algorithm::MinimumDiameter::getMinimumDiameter( geom.get() ) );
        ensure(nullptr != minDiameter.get());

        GeomPtr expectedGeom(reader.read("LineString (0 240, 0 240)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minDiameter.get()->equalsExact(expectedGeom.get()) );
    }

    // Test minimumDiameter with LineString input
    template<>
    template<>
    void object::test<7>()
    {
        GeomPtr geom(reader.read("LINESTRING (0 240, 220 240)"));
        ensure(nullptr != geom.get());

        GeomPtr minDiameter( geos::algorithm::MinimumDiameter::getMinimumDiameter( geom.get() ) );
        ensure(nullptr != minDiameter.get());

        GeomPtr expectedGeom(reader.read("LINESTRING (0 240, 0 240)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minDiameter.get()->equalsExact(expectedGeom.get()) );
    }

    // Test minimumDiameter with Polygon input
    template<>
    template<>
    void object::test<8>()
    {
        GeomPtr geom(reader.read("POLYGON ((0 240, 220 240, 220 0, 0 0, 0 240))"));
        ensure(nullptr != geom.get());

        GeomPtr minDiameter( geos::algorithm::MinimumDiameter::getMinimumDiameter( geom.get() ) );
        ensure(nullptr != minDiameter.get());

        GeomPtr expectedGeom(reader.read("LINESTRING (0 0, 220 0)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minDiameter.get()->equalsExact(expectedGeom.get()) );
    }

    // Test minimumDiameter with Polygon input
    template<>
    template<>
    void object::test<9>()
    {
        GeomPtr geom(reader.read("POLYGON ((0 240, 160 140, 220 0, 0 0, 0 240))"));
        ensure(nullptr != geom.get());

        GeomPtr minDiameter( geos::algorithm::MinimumDiameter::getMinimumDiameter( geom.get() ) );
        ensure(nullptr != minDiameter.get());

        GeomPtr expectedGeom(reader.read("LINESTRING (185.86206896551724 79.65517241379311, 0 0)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minDiameter.get()->equalsExact(expectedGeom.get(), 0.00000000001) );
    }

    // Test minimumDiameter with complex LineString
    template<>
    template<>
    void object::test<10>()
    {
        GeomPtr geom(reader.read("LINESTRING ( 39 119, 162 197, 135 70, 95 35, 33 66, 111 82, 97 131, 48 160, -4 182, 57 195, 94 202, 90 174, 75 134, 47 114, 0 100, 59 81, 123 60, 136 43, 163 75, 145 114, 93 136, 92 159, 105 175 )"));
        ensure(nullptr != geom.get());

        GeomPtr minDiameter( geos::algorithm::MinimumDiameter::getMinimumDiameter( geom.get() ) );
        ensure(nullptr != minDiameter.get());

        GeomPtr expectedGeom(reader.read("LINESTRING (64.46262341325811 196.41184767277855, 95 35)"));
        ensure(nullptr != expectedGeom.get());

        ensure( minDiameter.get()->equalsExact(expectedGeom.get(), 0.00000000001) );
    }

} // namespace tut
