//
// Test Suite for Geometry's normalize()

// tut
#include <tut/tut.hpp>
// geos
#include <geos/geom/Geometry.h>
#include <geos/geom/Polygon.h>
#include <geos/io/WKTReader.h>
#include <geos/io/WKTWriter.h>
// std
#include <memory>
#include <string>
#include <iostream>

namespace tut {

//
// Test Group
//

struct test_geometry_normalize_data {
    typedef std::unique_ptr<geos::geom::Geometry> GeomPtr;
    geos::io::WKTReader reader;
    geos::io::WKTWriter writer;

    test_geometry_normalize_data()
        : reader(), writer()
    {
    }

    void
    runTest(const char* from, const char* exp)
    {
        GeomPtr g1(reader.read(from));
        ensure("g1 is parsed", g1.get() != 0);
        GeomPtr g2(g1->clone());
        ensure("g2 is parsed", g2.get() != 0);
        g2->normalize();


        GeomPtr ge(reader.read(exp));

        ensure_equals(g2->hasZ(), ge->hasZ());
        ensure_equals(g2->hasM(), ge->hasM());

        bool eq = g2->equalsExact(ge.get());
        if(! eq) {
            using namespace std;
            cout << endl;
            cout << "Expected: " << exp << endl;
            cout << "Obtained: " << writer.write(g2.get()) << endl;
        }
        ensure(eq);

        // Clone and re-normalize, check it doesn't change anymore
        ge = g2->clone();
        g2->normalize();
        ensure(g2->equalsExact(ge.get()));
    }
};

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

group test_geometry_normalize_data("geos::geom::Geometry::normalize");

//
// Test Cases
//

template<>
template<>
void object::test<1>
()
{
    const char* inp = "POINT (0 100)";
    const char* exp = inp; // point does not normalize to anything else
    runTest(inp, exp);
}

template<>
template<>
void object::test<2>
()
{
    const char* inp =
        "LINESTRING(100 0,100 100,0 100,0 0)";
    const char* exp =
        "LINESTRING (0 0, 0 100, 100 100, 100 0)";
    runTest(inp, exp);
}

template<>
template<>
void object::test<3>
()
{
    const char* inp =
        "POLYGON ("
        "(0 0,100 0,100 100,0 100,0 0)," // CCW
        "(10 10,20 10,20 20,10 20,10 10)," // CCW, leftmost
        "(40 10,40 20,60 20,60 10,40 10)" // CW, rightmost
        ")" ;
    const char* exp =
        "POLYGON("
        "(0 0,0 100,100 100,100 0,0 0)," // CW
        "(40 10,60 10,60 20,40 20,40 10)," // CCW, rightmost
        "(10 10,20 10,20 20,10 20,10 10)" // CCW, leftmost
        ")";

    runTest(inp, exp);
}

template<>
template<>
void object::test<4>
()
{
    const char* inp =
        "MULTIPOINT ("
        "(0 100)," // leftmost
        "(5 6)"    // rightmost
        ")";
    const char* exp =
        "MULTIPOINT ("
        "(5 6),"   // rightmost
        "(0 100)"  // leftmost
        ")";
    runTest(inp, exp);
}

template<>
template<>
void object::test<5>
()
{
    const char* inp =
        "MULTILINESTRING("
        "(15 25, 25 52),"               // rightmost vertex @ 25
        "(0 0, 0 100, 100 100, 100 0)"  // rightmost vertex @ 100
        ")";
    const char* exp =
        "MULTILINESTRING("
        "(0 0, 0 100, 100 100, 100 0)," // rightmost vertex @ 100
        "(15 25, 25 52)"                // rightmost vertex @ 25
        ")";
    runTest(inp, exp);
}

template<>
template<>
void object::test<6>
()
{
    const char* inp =
        "MULTIPOLYGON("
        "((0 0, 0 1, 1 1, 1 0, 0 0))," // leftmost
        "((2 0, 2 1, 3 1, 3 0, 2 0))"  // rightmost
        ")";
    const char* exp =
        "MULTIPOLYGON("
        "((2 0, 2 1, 3 1, 3 0, 2 0))," // rightmost
        "((0 0, 0 1, 1 1, 1 0, 0 0))"  // leftmost
        ")";
    runTest(inp, exp);
}

template<>
template<>
void object::test<7>
()
{
    const char* inp =
        "GEOMETRYCOLLECTION("
        "MULTIPOINT ("
        "(0 100)," // leftmost
        "(5 6)"    // rightmost
        "),"
        "POINT(10 4)," // more on the right than the multipoint
        "MULTILINESTRING("
        "(15 25, 25 52),"               // rightmost vertex @ 25
        "(0 0, 0 100, 100 100, 100 0)"  // rightmost vertex @ 100
        "),"
        "LINESTRING(100 0,100 100,0 100,0 0),"
        "MULTIPOLYGON("
        "((0 0, 0 1, 1 1, 1 0, 0 0))," // leftmost
        "((2 0, 2 1, 3 1, 3 0, 2 0))"  // rightmost
        "),"
        "POLYGON ("
        "(0 0,100 0,100 100,0 100,0 0)," // CCW
        "(10 10,20 10,20 20,10 20,10 10)," // CCW, leftmost
        "(40 10,40 20,60 20,60 10,40 10)" // CW, rightmost
        ")"
        ")";
    const char* exp =
        "GEOMETRYCOLLECTION("
        "MULTIPOLYGON("
        "((2 0, 2 1, 3 1, 3 0, 2 0))," // rightmost
        "((0 0, 0 1, 1 1, 1 0, 0 0))"  // leftmost
        "),"
        "POLYGON("
        "(0 0,0 100,100 100,100 0,0 0)," // CW
        "(40 10,60 10,60 20,40 20,40 10)," // CCW, rightmost
        "(10 10,20 10,20 20,10 20,10 10)" // CCW, leftmost
        "),"
        "MULTILINESTRING("
        "(0 0, 0 100, 100 100, 100 0)," // rightmost vertex @ 100
        "(15 25, 25 52)"                // rightmost vertex @ 25
        "),"
        "LINESTRING (0 0, 0 100, 100 100, 100 0),"
        "MULTIPOINT ("
        "(5 6),"   // rightmost
        "(0 100)"  // leftmost
        "),"
        "POINT(10 4)" // more on the right than the multipoint
        ")";
    runTest(inp, exp);
}

// closed LineString
template<>
template<>
void object::test<8>
()
{
    // This behavior differs from JTS but has existed in GEOS since at
    // least version 3.8.

    runTest("LINESTRING (8 15, 15 8, 22 15, 15 22, 8 15)",
            "LINESTRING (8 15, 15 22, 22 15, 15 8, 8 15)");
}

// zero-length linestring
template<>
template<>
void object::test<9>
()
{
    runTest("LINESTRING (0 1, 0 1)",
            "LINESTRING (0 1, 0 1)");

    runTest("LINESTRING (0 1, 0 1, 0 1)",
            "LINESTRING (0 1, 0 1, 0 1)");
}

// closed LineString M
template<>
template<>
void object::test<10>
()
{
    runTest("LINESTRING M (8 15 0, 15 8 1, 22 15 2, 15 22 3, 8 15 0)",
            "LINESTRING M (8 15 0, 15 22 3, 22 15 2, 15 8 1, 8 15 0)");
}

// non-closed LineString M
template<>
template<>
void object::test<11>
()
{
    runTest("LINESTRING M (100 0 0, 100 100 1, 0 100 2, 0 0 3)",
            "LINESTRING M (0 0 3, 0 100 2, 100 100 1, 100 0 0)");
}

template<>
template<>
void object::test<12>
()
{
    set_test_name("closed CircularString");

    runTest("CIRCULARSTRING (120 152.3864417907085, 109.7594383790319 155.56129134453136, 100 160, 90 190, 100 220, 109.7594383790319 224.43870865546864, 120 227.6135582092915, 150.70007323113106 229.3238307529279, 180 220, 190 190, 180 160, 150.70007323113106 150.6761692470721, 120 152.3864417907085)",
        "CIRCULARSTRING (100 160, 90 190, 100 220, 109.7594383790319 224.43870865546864, 120 227.6135582092915, 150.70007323113106 229.3238307529279, 180 220, 190 190, 180 160, 150.70007323113106 150.6761692470721, 120 152.3864417907085, 109.7594383790319 155.56129134453136, 100 160)");
}


} // namespace tut

