//
// Test Suite for geos::geom::GeometryFactory class.

// tut
#include <tut/tut.hpp>
#include <utility.h>
// geos
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/GeometryCollection.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateArraySequence.h>
#include <geos/geom/CoordinateArraySequenceFactory.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/Dimension.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/Point.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/io/WKTReader.h>
#include <geos/util/IllegalArgumentException.h>
// std
#include <vector>
#include <cstring> // std::size_t

/*!
 * \brief
 * Write brief comment for tut here.
 *
 * Write detailed description for tut here.
 *
 * \remarks
 * Write remarks for tut here.
 *
 * \see
 * Separate items with the '|' character.
 */
namespace tut
{
	//
	// Test Group
	//

	// Common data used by tests
    struct test_geometryfactory_data
    {
        // Test data
        const int x_;
        const int y_;
        const int z_;

        const int srid_;
        geos::geom::PrecisionModel pm_;
        geos::geom::GeometryFactory::Ptr factory_;
        geos::io::WKTReader reader_;

        test_geometryfactory_data()
            : x_(5), y_(10), z_(15), srid_(666), pm_(1.0),
factory_(geos::geom::GeometryFactory::create(&pm_, srid_)),
reader_(factory_.get())
        {}
    private:
        // Declare type as noncopyable
        test_geometryfactory_data(const test_geometryfactory_data& other) = delete;
        test_geometryfactory_data& operator=(const test_geometryfactory_data& rhs) = delete;
    };

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

	group test_geometryfactory_group("geos::geom::GeometryFactory");

	//
	// Test Cases
	//

	// Test of default constructor
	template<>
	template<>
	void object::test<1>()
	{
		using geos::geom::GeometryFactory;
		GeometryFactory::Ptr gf = GeometryFactory::create();

		ensure_equals( gf->getSRID(), 0 );
		ensure_equals( gf->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FLOATING );

		geos::geom::Geometry* geo = gf->createEmptyGeometry();
		ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
		ensure_equals( geo->getSRID() , gf->getSRID() );
		ensure_equals( geo->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FLOATING );

		// FREE MEMORY
		gf->destroyGeometry(geo);
	}

	// Test of user's constructor
	template<>
	template<>
	void object::test<2>()
	{
		using geos::geom::GeometryFactory;
		using geos::geom::PrecisionModel;
		using geos::geom::CoordinateArraySequenceFactory;

		CoordinateArraySequenceFactory csf;

		{
			PrecisionModel pm(1.0);
			GeometryFactory::Ptr gf = GeometryFactory::create(&pm, srid_, &csf);

			ensure_equals( gf->getSRID(), srid_ );
			ensure_equals( gf->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FIXED );

			ensure_equals( &csf, gf->getCoordinateSequenceFactory() );

			GeometryPtr geo = gf->createEmptyGeometry();
			ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
			ensure_equals( geo->getSRID() , gf->getSRID() );
			ensure_equals( geo->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FIXED );

			// FREE MEMORY
			gf->destroyGeometry(geo);
		}
		// csf lifetime must exceed lifetime of the GeometryFactory instance

	} // test<2>

	// Test of user's constructor
	template<>
	template<>
	void object::test<3>()
	{
		using geos::geom::GeometryFactory;
		using geos::geom::CoordinateArraySequenceFactory;

		CoordinateArraySequenceFactory csf;

		{
			GeometryFactory::Ptr gf = GeometryFactory::create(&csf);

			ensure_equals( gf->getSRID(), 0 );
			ensure_equals( gf->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FLOATING );

			GeometryPtr geo = gf->createEmptyGeometry();
			ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
			ensure_equals( geo->getSRID() , gf->getSRID() );
			ensure_equals( geo->getPrecisionModel()->getType(), geos::geom::PrecisionModel::FLOATING );

			// FREE MEMORY
			gf->destroyGeometry(geo);
		}
		// csf lifetime must exceed lifetime of the GeometryFactory instance
	}

	// Test of user's constructor
	template<>
	template<>
	void object::test<4>()
	{
		using geos::geom::PrecisionModel;
		using geos::geom::GeometryFactory;

		PrecisionModel pm(PrecisionModel::FIXED);
		GeometryFactory::Ptr gf(GeometryFactory::create(&pm));

		ensure_equals( gf->getSRID(), 0 );
		ensure_equals( gf->getPrecisionModel()->getType(), PrecisionModel::FIXED );

		GeometryPtr geo = gf->createEmptyGeometry();
		ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
		ensure_equals( geo->getSRID() , gf->getSRID() );
		ensure_equals( geo->getPrecisionModel()->getType(), PrecisionModel::FIXED );

		// FREE MEMORY
		gf->destroyGeometry(geo);
	}

	// Test of user's constructor
	template<>
	template<>
	void object::test<5>()
	{
		using geos::geom::PrecisionModel;
		using geos::geom::GeometryFactory;

		PrecisionModel pm(PrecisionModel::FIXED);
		GeometryFactory::Ptr gf(GeometryFactory::create(&pm, srid_));

		ensure_equals( gf->getSRID(), srid_ );
		ensure_equals( gf->getPrecisionModel()->getType(), PrecisionModel::FIXED );

		GeometryPtr geo = gf->createEmptyGeometry();
		ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
		ensure_equals( geo->getSRID() , gf->getSRID() );
		ensure_equals( geo->getPrecisionModel()->getType(), PrecisionModel::FIXED );

		// FREE MEMORY
		gf->destroyGeometry(geo);
	}

	// Test of copy constructor
	template<>
	template<>
	void object::test<6>()
	{
		using geos::geom::GeometryFactory;
		GeometryFactory::Ptr gf(GeometryFactory::create(*factory_));

		ensure_equals( factory_->getSRID(), gf->getSRID() );
		ensure_equals( factory_->getPrecisionModel()->getType(), gf->getPrecisionModel()->getType() );
	}

	// Test of createEmptyGeometry() const
	template<>
	template<>
	void object::test<7>()
	{
		GeometryPtr geo = factory_->createEmptyGeometry();

		ensure( "createEmptyGeometry() returned null pointer.", geo != nullptr );
		ensure( "createEmptyGeometry() returned non-empty geometry.", geo->isEmpty() );

		// TODO - mloskot
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/001960.html
		/*
		ensure( geo->isSimple() );
		ensure( geo->isValid() );

		ensure( geo->getCentroid() == 0 );
		ensure( geo->getCoordinate() == 0 );
		*/

		// FREE MEMORY
		factory_->destroyGeometry(geo);
	}

	// Test of createPoint() const
	template<>
	template<>
	void object::test<8>()
	{
		PointPtr pt = factory_->createPoint();

		ensure( "createPoint() returned null pointer.", pt != nullptr );
		ensure( "createPoint() returned non-empty point.", pt->isEmpty() );
		ensure( pt->isSimple() );
		ensure( pt->isValid() );
		ensure( pt->getCentroid() == nullptr );
		ensure( pt->getCoordinate() == nullptr );

		GeometryPtr geo = nullptr;
		geo = pt->getEnvelope();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->convexHull();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( pt->getGeometryTypeId(), geos::geom::GEOS_POINT );
		ensure_equals( pt->getDimension(), geos::geom::Dimension::P );
		ensure_equals( pt->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( pt->getNumPoints(), 0u );
		ensure_equals( pt->getLength(), 0.0 );
		ensure_equals( pt->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(pt);
	}

	// Test of createPoint(const Coordinate &coordinate) const
	template<>
	template<>
	void object::test<9>()
	{
		geos::geom::Coordinate coord(x_, y_, z_);

		PointPtr pt = factory_->createPoint(coord);

		ensure( "createPoint() returned null pointer.", pt != nullptr );
		ensure( "createPoint() returned empty point.", !pt->isEmpty() );
		ensure( pt->isSimple() );
		ensure( pt->isValid() );
		ensure( pt->getCoordinate() != nullptr );

		CoordinateCPtr pcoord = pt->getCoordinate();
		ensure( pcoord != nullptr );
		ensure_equals( pcoord->x, x_ );
		ensure_equals( pcoord->y, y_ );
		ensure_equals( pcoord->z, z_ );

		GeometryPtr geo = nullptr;
		geo = pt->getEnvelope();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getCentroid();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->convexHull();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( pt->getGeometryTypeId(), geos::geom::GEOS_POINT );
		ensure_equals( pt->getDimension(), geos::geom::Dimension::P );
		ensure_equals( pt->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( pt->getNumPoints(), 1u );
		ensure_equals( pt->getLength(), 0.0 );
		ensure_equals( pt->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(pt);
	}

	// Test of createPoint(CoordinateSequence *coordinates) const
	template<>
	template<>
	void object::test<10>()
	{
		geos::geom::Coordinate coord(x_, y_, z_);

		CoordArrayPtr sequence = new geos::geom::CoordinateArraySequence();

		ensure( "sequence is null pointer.", sequence != nullptr );
		sequence->add(coord);

		PointPtr pt = factory_->createPoint(sequence);

		ensure( "createPoint() returned null pointer.", pt != nullptr );
		ensure( "createPoint() returned empty point.", !pt->isEmpty() );
		ensure( pt->isSimple() );
		ensure( pt->isValid() );
		ensure( pt->getCoordinate() != nullptr );

		CoordinateCPtr pcoord = pt->getCoordinate();
		ensure( pcoord != nullptr );
		ensure_equals( pcoord->x, x_ );
		ensure_equals( pcoord->y, y_ );
		ensure_equals( pcoord->z, z_ );

		GeometryPtr geo = nullptr;
		geo = pt->getEnvelope();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getCentroid();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->convexHull();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( pt->getGeometryTypeId(), geos::geom::GEOS_POINT );
		ensure_equals( pt->getDimension(), geos::geom::Dimension::P );
		ensure_equals( pt->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( pt->getNumPoints(), 1u );
		ensure_equals( pt->getLength(), 0.0 );
		ensure_equals( pt->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(pt);
	}

	// Test of createPoint(const CoordinateSequence &coordinates) const
	template<>
	template<>
	void object::test<11>()
	{
		geos::geom::Coordinate coord(x_, y_, z_);

		geos::geom::CoordinateArraySequence sequence;
		sequence.add(coord);

		PointPtr pt = factory_->createPoint(sequence);

		ensure( "createPoint() returned null pointer.", pt != nullptr );
		ensure( "createPoint() returned empty point.", !pt->isEmpty() );
		ensure( pt->isSimple() );
		ensure( pt->isValid() );
		ensure( pt->getCoordinate() != nullptr );

		CoordinateCPtr pcoord = pt->getCoordinate();
		ensure( pcoord != nullptr );
		ensure_equals( pcoord->x, x_ );
		ensure_equals( pcoord->y, y_ );
		ensure_equals( pcoord->z, z_ );

		GeometryPtr geo = nullptr;
		geo = pt->getEnvelope();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getCentroid();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = pt->convexHull();
		ensure( geo != nullptr );
		ensure( !geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( pt->getGeometryTypeId(), geos::geom::GEOS_POINT );
		ensure_equals( pt->getDimension(), geos::geom::Dimension::P );
		ensure_equals( pt->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( pt->getNumPoints(), 1u );
		ensure_equals( pt->getLength(), 0.0 );
		ensure_equals( pt->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(pt);
	}

	// Test of createLinearRing() const
	template<>
	template<>
	void object::test<12>()
	{
		LinearRingPtr lr = factory_->createLinearRing();

		ensure( "createLinearRing() returned null pointer.", lr != nullptr );
		ensure( "createLinearRing() returned non-empty point.", lr->isEmpty() );
		ensure( lr->isEmpty() );
		ensure( lr->isSimple() );
		ensure( lr->isValid() );
		ensure( lr->getCoordinate() == nullptr );

		// TODO - mloskot
		//http://geos.osgeo.org/pipermail/geos-devel/2006-March/001961.html
		//ensure( lr->isClosed() );

		// TODO - mloskot
		//http://geos.osgeo.org/pipermail/geos-devel/2006-March/001962.html
		//ensure_equals( lr->getStartPoint(), lr->getEndPoint() );

		ensure_equals( lr->getGeometryTypeId(), geos::geom::GEOS_LINEARRING );
		ensure_equals( lr->getDimension(), geos::geom::Dimension::L );
		ensure_equals( lr->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( lr->getNumPoints(), 0u );
		ensure_equals( lr->getLength(), 0.0 );
		ensure_equals( lr->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(lr);
	}

	// Test of createLinearRing(CoordinateSequence* newCoords) const
	template<>
	template<>
	void object::test<13>()
	{
		const std::size_t size = 5;
		CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(size);
		ensure( coords != nullptr );
		ensure_equals( coords->getSize(), size );

		LinearRingPtr lr = factory_->createLinearRing(coords);
		ensure( "createLinearRing() returned null pointer.", lr != nullptr );
		ensure( "createLinearRing() returned empty point.", !lr->isEmpty() );
		ensure( lr->isSimple() );
		ensure( lr->getCoordinate() != nullptr );

		// TODO - mloskot - is this correct?
		//ensure( !lr->isValid() );

		ensure_equals( lr->getGeometryTypeId(), geos::geom::GEOS_LINEARRING );
		ensure_equals( lr->getDimension(), geos::geom::Dimension::L );
		ensure_equals( lr->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( lr->getNumPoints(), size );
		ensure_equals( lr->getLength(), 0.0 );
		ensure_equals( lr->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(lr);
	}

	// Test of createLinearRing(const CoordinateSequence& coordinates) const
	template<>
	template<>
	void object::test<14>()
	{
		const std::size_t size = 5;
		geos::geom::CoordinateArraySequence coords(size);
		ensure_equals( coords.getSize(), size );

		LinearRingPtr lr = factory_->createLinearRing(coords);
		ensure( "createLinearRing() returned empty point.", !lr->isEmpty() );
		ensure_equals( lr->getNumPoints(), size );
		ensure( lr->isSimple() );
		ensure( lr->getCoordinate() != nullptr );

		ensure_equals( lr->getGeometryTypeId(), geos::geom::GEOS_LINEARRING );
		ensure_equals( lr->getDimension(), geos::geom::Dimension::L );
		ensure_equals( lr->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( lr->getNumPoints(), size );
		ensure_equals( lr->getLength(), 0.0 );
		ensure_equals( lr->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(lr);
	}

	// Test of createLineString() const
	template<>
	template<>
	void object::test<15>()
	{
		LineStringPtr line = factory_->createLineString();

		ensure( "createLineString() returned null pointer.", line != nullptr );
		ensure( "createLineString() returned non-empty point.", line->isEmpty() );
		ensure( line->isSimple() );
		ensure( line->isValid() );
		ensure( line->getCentroid() == nullptr );

		// TODO - mloskot - waiting for some decision
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/002006.html
		//ensure( line->getCoordinate() == 0 );

		GeometryPtr geo = nullptr;
		geo = line->getEnvelope();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = line->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = line->convexHull();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( line->getGeometryTypeId(), geos::geom::GEOS_LINESTRING );
		ensure_equals( line->getDimension(), geos::geom::Dimension::L );
		ensure_equals( line->getBoundaryDimension(), geos::geom::Dimension::P ); // empty linestring
		ensure_equals( line->getNumPoints(), 0u );
		ensure_equals( line->getLength(), 0.0 );
		ensure_equals( line->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(line);
	}

	// Test of createLineString(CoordinateSequence* coordinates) const
	template<>
	template<>
	void object::test<16>()
	{
		const std::size_t size = 5;
		CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(size);
		ensure( coords != nullptr );
		ensure_equals( coords->getSize(), size );

		LineStringPtr line = factory_->createLineString(coords);
		ensure( "createLineString() returned null pointer.", line != nullptr );
		ensure( "createLineString() returned empty point.", !line->isEmpty() );
		ensure( line->isSimple() );
		ensure( line->getCoordinate() != nullptr );

		// TODO - mloskot - is this correct?
		//ensure( line->isValid() );

		ensure_equals( line->getGeometryTypeId(), geos::geom::GEOS_LINESTRING );
		ensure_equals( line->getDimension(), geos::geom::Dimension::L );
		ensure_equals( line->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( line->getNumPoints(), size );
		ensure_equals( line->getLength(), 0.0 );
		ensure_equals( line->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(line);
	}

	// Test of createLineString(const CoordinateSequence& coordinates) const
	template<>
	template<>
	void object::test<17>()
	{
		const std::size_t size = 5;
		geos::geom::CoordinateArraySequence coords(size);
		ensure_equals( coords.getSize(), size );

		LineStringPtr line = factory_->createLineString(coords);
		ensure( "createLineString() returned empty point.", !line->isEmpty() );
		ensure_equals( line->getNumPoints(), size );
		ensure( line->isSimple() );
		ensure( line->getCoordinate() != nullptr );

		ensure_equals( line->getGeometryTypeId(), geos::geom::GEOS_LINESTRING );
		ensure_equals( line->getDimension(), geos::geom::Dimension::L );
		ensure_equals( line->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( line->getNumPoints(), size );
		ensure_equals( line->getLength(), 0.0 );
		ensure_equals( line->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(line);
	}
	// Test of createPolygon() const
	template<>
	template<>
	void object::test<18>()
	{
		PolygonPtr poly = factory_->createPolygon();

		ensure( "createPolygon() returned null pointer.", poly != nullptr );
		ensure( "createPolygon() returned non-empty point.", poly->isEmpty() );
		ensure( poly->isSimple() );
		ensure( poly->isValid() );
		ensure( poly->getCentroid() == nullptr );

		// TODO - mloskot - waiting for some decision
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/002006.html
		//ensure( poly->getCoordinate() == 0 );

		GeometryPtr geo = nullptr;
		// TODO - mloskot - waiting for resolution
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/002011.html
		//geo = poly->getEnvelope();
		//ensure( geo != 0 );
		//ensure( geo->isEmpty() );
		//factory_->destroyGeometry(geo);

		geo = poly->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = poly->convexHull();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( poly->getGeometryTypeId(), geos::geom::GEOS_POLYGON );
		ensure_equals( poly->getDimension(), geos::geom::Dimension::A );
		ensure_equals( poly->getBoundaryDimension(), geos::geom::Dimension::L );
		ensure_equals( poly->getNumPoints(), 0u );
		ensure_equals( poly->getLength(), 0.0 );
		ensure_equals( poly->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(poly);
	}

	// Test of createPolygon(LinearRing* shell, std::vector<Geometry*>* holes) const
	template<>
	template<>
	void object::test<19>()
	{
		using geos::geom::Coordinate;
		const std::size_t size = 7;

		// Create sequence of coordiantes
		CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(size);
		ensure( coords != nullptr );
		coords->setAt(Coordinate(0, 10), 0);
		coords->setAt(Coordinate(5, 5), 1);
		coords->setAt(Coordinate(10, 5), 2);
		coords->setAt(Coordinate(15, 10), 3);
		coords->setAt(Coordinate(10, 15), 4);
		coords->setAt(Coordinate(5, 15), 5);
		coords->setAt(Coordinate(0, 10), 6);
		ensure_equals( coords->getSize(), size );

		// Create exterior ring
		LinearRingPtr exterior = factory_->createLinearRing(coords);
		ensure( "createLinearRing returned null pointer.", exterior != nullptr );
		ensure( "createLinearRing() returned empty point.", !exterior->isEmpty() );
		ensure( exterior->isSimple() );
		ensure_equals( exterior->getGeometryTypeId(), geos::geom::GEOS_LINEARRING );
		ensure_equals( exterior->getDimension(), geos::geom::Dimension::L );
		ensure_equals( exterior->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( exterior->getNumPoints(), size );
		ensure_equals( exterior->getArea(), 0.0 );
		ensure( exterior->getLength() != 0.0 );

		// Create polygon
		PolygonPtr poly = factory_->createPolygon(exterior, nullptr);
		ensure( "createPolygon returned null pointer.", poly != nullptr );
		ensure( "createPolygon() returned empty point.", !poly->isEmpty() );
		ensure( poly->isSimple() );
		ensure_equals( poly->getGeometryTypeId(), geos::geom::GEOS_POLYGON );
		ensure_equals( poly->getDimension(), geos::geom::Dimension::A );
		ensure_equals( poly->getBoundaryDimension(), geos::geom::Dimension::L );
		ensure_equals( poly->getNumPoints(), size );
		ensure( poly->getArea() != 0.0 );
		ensure( poly->getLength() != 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(poly);
	}

	// Test of createPolygon(const LinearRing& shell, const std::vector<Geometry*>& holes) const
	template<>
	template<>
	void object::test<20>()
	{
		using geos::geom::Coordinate;
		const std::size_t exteriorSize = 7;
		const std::size_t interiorSize = 5;

		// Create sequence of coordiantes
		CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(exteriorSize);
		ensure( coords != nullptr );
		coords->setAt(Coordinate(0, 10), 0);
		coords->setAt(Coordinate(5, 5), 1);
		coords->setAt(Coordinate(10, 5), 2);
		coords->setAt(Coordinate(15, 10), 3);
		coords->setAt(Coordinate(10, 15), 4);
		coords->setAt(Coordinate(5, 15), 5);
		coords->setAt(Coordinate(0, 10), 6);
		ensure_equals( coords->getSize(), exteriorSize );

		// Create exterior ring
		LinearRingPtr exterior = factory_->createLinearRing(coords);
		ensure( "createLinearRing returned null pointer.", exterior != nullptr );
		ensure( "createLinearRing() returned empty point.", !exterior->isEmpty() );
		ensure( exterior->isSimple() );
		ensure_equals( exterior->getGeometryTypeId(), geos::geom::GEOS_LINEARRING );
		ensure_equals( exterior->getDimension(), geos::geom::Dimension::L );
		ensure_equals( exterior->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( exterior->getNumPoints(), exteriorSize );
		ensure_equals( exterior->getArea(), 0.0 );
		ensure( exterior->getLength() != 0.0 );

		// Create collection of holes
		GeometryPtr geo = reader_.read(("LINEARRING(7 7, 12 7, 12 12, 7 12, 7 7)"));
		ensure( geo != nullptr );

		LinearRingPtr hole = dynamic_cast<LinearRingPtr>(geo);
		ensure( hole != nullptr );
		ensure( hole->isRing() );
		ensure_equals( hole->getNumPoints(), interiorSize );

		// REMEMBER TO DEALLOCATE THIS COLLECTION
		std::vector<GeometryPtr> holes;
		holes.push_back(hole);

		// Create polygon using copy ctor
		PolygonPtr poly = factory_->createPolygon((*exterior), holes);
		ensure( "createPolygon returned null pointer.", poly != nullptr );
		ensure( "createPolygon() returned empty point.", !poly->isEmpty() );
		ensure( poly->isSimple() );
		ensure_equals( poly->getGeometryTypeId(), geos::geom::GEOS_POLYGON );
		ensure_equals( poly->getDimension(), geos::geom::Dimension::A );
		ensure_equals( poly->getBoundaryDimension(), geos::geom::Dimension::L );
		ensure_equals( poly->getNumPoints(), exteriorSize + interiorSize );
		ensure( poly->getArea() != 0.0 );
		ensure( poly->getLength() != 0.0 );

		ensure_equals( poly->getNumGeometries(), 1u );
		ensure_equals( poly->getNumInteriorRing(), 1u );

		// FREE MEMORY
		std::vector<GeometryPtr>::const_iterator it;
		for (it = holes.begin(); it != holes.end(); ++it)
		{
			delete (*it);
		}
		holes.clear();

		factory_->destroyGeometry(exterior);
		factory_->destroyGeometry(poly);
	}

	// Test of createGeometryCollection() const
	template<>
	template<>
	void object::test<21>()
	{
		GeometryColPtr col = factory_->createGeometryCollection();

		ensure( "createGeometryCollection() returned null pointer.", col != nullptr );
		ensure( col->isEmpty() );
		ensure( col->isValid() );

		try
		{
			ensure( !col->isSimple() );
			fail("IllegalArgumentException expected");
		}
		catch ( geos::util::IllegalArgumentException const& e )
		{
			const char* msg = e.what(); // ok
			ensure( msg != nullptr );
		}

		ensure( col->getCentroid() == nullptr );
		ensure_equals( col->getGeometryTypeId(), geos::geom::GEOS_GEOMETRYCOLLECTION );
		ensure_equals( col->getDimension(), geos::geom::Dimension::False );
		ensure_equals( col->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( col->getNumPoints(), 0u );
		ensure_equals( col->getLength(), 0.0 );
		ensure_equals( col->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(col);
	}

	// Test of createGeometryCollection(std::vector<Geometry*>* newGeoms) const
	template<>
	template<>
	void object::test<22>()
	{
		using geos::geom::Coordinate;

		// Buffer for geometries
		std::vector<GeometryPtr>* vec = new std::vector<GeometryPtr>();

		// Add single point
		Coordinate coord(x_, y_, z_);
		GeometryPtr point = factory_->createPoint(coord);
		ensure( point != nullptr );
		vec->push_back(point);

		// Add single LineString
		CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(3);
		ensure( coords != nullptr );
		coords->setAt(Coordinate(0, 0), 0);
		coords->setAt(Coordinate(5, 5), 1);
		coords->setAt(Coordinate(10, 5), 2);
		ensure_equals( coords->getSize(), 3u );
		GeometryPtr line = factory_->createLineString(coords);
		vec->push_back(line);

		// Create geometry collection
		GeometryColPtr col = factory_->createGeometryCollection(vec);
		ensure( coords != nullptr );
		ensure_equals( col->getGeometryTypeId(), geos::geom::GEOS_GEOMETRYCOLLECTION );
		ensure_equals( col->getNumGeometries(), 2u );

		// FREE MEMORY
		factory_->destroyGeometry(col);
	}

	// Test of createGeometryCollection(const std::vector<Geometry*>& newGeoms) const
	template<>
	template<>
	void object::test<23>()
	{
		const std::size_t size = 3;
		geos::geom::Coordinate coord(x_, y_, z_);

		std::vector<GeometryPtr> vec;

		GeometryPtr geo = nullptr;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 2;
		coord.y *= 2;
		coord.z *= 2;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 3;
		coord.y *= 3;
		coord.z *= 3;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		// Factory creates copy of the vec collection
		GeometryColPtr col = factory_->createGeometryCollection(vec);
		ensure( col != nullptr );
		ensure_equals( col->getGeometryTypeId() , geos::geom::GEOS_GEOMETRYCOLLECTION );
		ensure_equals( col->getNumGeometries() , size );

		// FREE MEMORY
		factory_->destroyGeometry(col);
		std::vector<GeometryPtr>::const_iterator it;
		for (it = vec.begin(); it != vec.end(); ++it)
		{
			delete (*it);
		}
	}

	// Test of createMultiPoint() const
	template<>
	template<>
	void object::test<24>()
	{
		MultiPointPtr mp = factory_->createMultiPoint();

		ensure( "createMultiPoint() returned null pointer.", mp != nullptr );
		ensure( "createMultiPoint() returned non-empty point.", mp->isEmpty() );
		ensure( mp->isSimple() );
		ensure( mp->isValid() );
		ensure( mp->getCentroid() == nullptr );

		GeometryPtr geo = nullptr;

		// TODO - mloskot - waiting for resolution
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/002011.html
		//geo = poly->getEnvelope();
		//ensure( geo != 0 );
		//ensure( geo->isEmpty() );
		//factory_->destroyGeometry(geo);

		geo = mp->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = mp->convexHull();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( mp->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT );
		ensure_equals( mp->getDimension(), geos::geom::Dimension::P );
		ensure_equals( mp->getBoundaryDimension(), geos::geom::Dimension::False );
		ensure_equals( mp->getNumPoints(), 0u );
		ensure_equals( mp->getLength(), 0.0 );
		ensure_equals( mp->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(mp);
	}

	// Test of createMultiPoint(std::vector<Geometry*>* newPoints) const
	template<>
	template<>
	void object::test<25>()
	{
		const std::size_t size = 3;
		geos::geom::Coordinate coord(x_, y_, z_);

		std::vector<GeometryPtr>* vec = new std::vector<GeometryPtr>();

		GeometryPtr geo = nullptr;
		geo = factory_->createPoint(coord);
		ensure( geo != nullptr );
		vec->push_back(geo);

		coord.x *= 2;
		coord.y *= 2;
		coord.z *= 2;
		geo = factory_->createPoint(coord);
		ensure( geo != nullptr );
		vec->push_back(geo);

		coord.x *= 3;
		coord.y *= 3;
		coord.z *= 3;
		geo = factory_->createPoint(coord);
		ensure( geo != nullptr );
		vec->push_back(geo);

		// Factory creates copy of the vec collection
		MultiPointPtr mp = factory_->createMultiPoint(vec);
		ensure( mp != nullptr );
		ensure( mp->isValid() );
		ensure( mp->isSimple() );
		ensure_equals( mp->getNumGeometries(), size );
		ensure_equals( mp->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT );

		// FREE MEMORY
		factory_->destroyGeometry(mp);
	}

	// Test of createMultiPoint(const std::vector<Geometry*>& fromPoints) const
	template<>
	template<>
	void object::test<26>()
	{
		const std::size_t size = 3;
		geos::geom::Coordinate coord(x_, y_, z_);

		std::vector<GeometryPtr> vec;

		GeometryPtr geo = nullptr;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 2;
		coord.y *= 2;
		coord.z *= 2;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 3;
		coord.y *= 3;
		coord.z *= 3;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		// Factory creates copy of the vec collection
		MultiPointPtr mp = factory_->createMultiPoint(vec);
		ensure( mp != nullptr );
		ensure( mp->isValid() );
		ensure( mp->isSimple() );
		ensure_equals( mp->getNumGeometries(), size );
		ensure_equals( mp->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT );

		// FREE MEMORY
		factory_->destroyGeometry(mp);
		std::vector<GeometryPtr>::const_iterator it;
		for (it = vec.begin(); it != vec.end(); ++it)
		{
			delete (*it);
		}
	}

	// Test of createMultiPoint(const CoordinateSequence& fromCoords) const
	template<>
	template<>
	void object::test<27>()
	{
		using geos::geom::Coordinate;
		const std::size_t size = 3;

		// Add collection of coordinates
		geos::geom::CoordinateArraySequence coords(size);
		coords.setAt(Coordinate(0, 0), 0);
		coords.setAt(Coordinate(5, 5), 1);
		coords.setAt(Coordinate(10, 5), 2);
		ensure_equals( coords.getSize(), size );

		MultiPointPtr mp = factory_->createMultiPoint(coords);
		ensure( mp != nullptr );
		ensure( mp->isValid() );
		ensure( mp->isSimple() );
		ensure_equals( mp->getNumGeometries(), size );
		ensure_equals( mp->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT );

		// FREE MEMORY
		factory_->destroyGeometry(mp);
	}

	// Test of createMultiLineString() const
	template<>
	template<>
	void object::test<28>()
	{
		MultiLineStringPtr mls = factory_->createMultiLineString();

		ensure( "createMultiLineString() returned null pointer.", mls != nullptr );
		ensure( "createMultiLineString() returned non-empty point.", mls->isEmpty() );
		ensure( mls->isSimple() );
		ensure( mls->isValid() );
		ensure( mls->getCentroid() == nullptr );

		GeometryPtr geo = nullptr;

		// TODO - mloskot - waiting for resolution
		// http://geos.osgeo.org/pipermail/geos-devel/2006-March/002011.html
		//geo = poly->getEnvelope();
		//ensure( geo != 0 );
		//ensure( geo->isEmpty() );
		//factory_->destroyGeometry(geo);

		geo = mls->getBoundary();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		geo = mls->convexHull();
		ensure( geo != nullptr );
		ensure( geo->isEmpty() );
		factory_->destroyGeometry(geo);

		ensure_equals( mls->getGeometryTypeId(), geos::geom::GEOS_MULTILINESTRING );
		ensure_equals( mls->getDimension(), geos::geom::Dimension::L );
		ensure_equals( mls->getBoundaryDimension(), geos::geom::Dimension::P );
		ensure_equals( mls->getNumPoints(), 0u );
		ensure_equals( mls->getLength(), 0.0 );
		ensure_equals( mls->getArea(), 0.0 );

		// FREE MEMORY
		factory_->destroyGeometry(mls);
	}

	// Test of createMultiLineString(std::vector<Geometry*>* newLines) const
	template<>
	template<>
	void object::test<29>()
	{
		using geos::geom::Coordinate;

		const std::size_t size = 5;
		const std::size_t lineSize = 2;

		std::vector<GeometryPtr>* lines = new std::vector<GeometryPtr>();

		for (std::size_t i = 0; i < size; ++i)
		{
			const double factor = static_cast<double>(i * i);
			CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(lineSize);
			ensure( coords != nullptr );
			coords->setAt(Coordinate(0. + factor, 0. + factor), 0);
			coords->setAt(Coordinate(5. + factor, 5. + factor), 1);
			ensure_equals( coords->getSize(), lineSize );

			LineStringPtr line = factory_->createLineString(coords);
			ensure( "createLineString() returned empty point.", !line->isEmpty() );
			ensure_equals( line->getNumPoints(), lineSize );
			ensure( line->isSimple() );
			ensure( line->getCoordinate() != nullptr );
			ensure_equals( line->getGeometryTypeId(), geos::geom::GEOS_LINESTRING );

			lines->push_back(line);
		}

		MultiLineStringPtr mls = factory_->createMultiLineString(lines);
		ensure( mls != nullptr );
		// TODO - mloskot - why isValid() returns false?
		//ensure( mls->isValid() );
		ensure_equals( mls->getNumGeometries(), size );
		ensure_equals( mls->getGeometryTypeId(), geos::geom::GEOS_MULTILINESTRING );

		// FREE MEMORY
		factory_->destroyGeometry(mls);
	}

	// Test of createMultiLineString(const std::vector<Geometry*>& fromLines) const
	template<>
	template<>
	void object::test<30>()
	{
		using geos::geom::Coordinate;

		const std::size_t size = 5;
		const std::size_t lineSize = 2;

		std::vector<GeometryPtr> lines;

		for (std::size_t i = 0; i < size; ++i)
		{
			const double factor = static_cast<double>(i * i);
			CoordArrayPtr coords = new geos::geom::CoordinateArraySequence(lineSize);
			ensure( coords != nullptr );
			coords->setAt(Coordinate(0. + factor, 0. + factor), 0);
			coords->setAt(Coordinate(5. + factor, 5. + factor), 1);
			ensure_equals( coords->getSize(), lineSize );

			LineStringPtr line = factory_->createLineString(coords);
			ensure( "createLineString() returned empty point.", !line->isEmpty() );
			ensure_equals( line->getNumPoints(), lineSize );
			ensure( line->isSimple() );
			ensure( line->getCoordinate() != nullptr );
			ensure_equals( line->getGeometryTypeId(), geos::geom::GEOS_LINESTRING );

			lines.push_back(line);
		}

		MultiLineStringPtr mls = factory_->createMultiLineString(lines);
		ensure( mls != nullptr );
		// TODO - mloskot - why isValid() returns false?
		//ensure( mls->isValid() );
		ensure_equals( mls->getNumGeometries(), size );
		ensure_equals( mls->getGeometryTypeId(), geos::geom::GEOS_MULTILINESTRING );

		// FREE MEMORY
		factory_->destroyGeometry(mls);
		std::vector<GeometryPtr>::const_iterator it;
		for (it = lines.begin(); it != lines.end(); ++it)
		{
			delete (*it);
		}
	}

	// Test of createMultiPolygon() const
	template<>
	template<>
	void object::test<31>()
	{
		// TODO - mloskot
		//inform("Test not implemented!");
	}

	// Test of createMultiPolygon(std::vector<Geometry>* newPolys) const
	template<>
	template<>
	void object::test<32>()
	{
		// TODO - mloskot
		//inform("Test not implemented!");
	}

	// Test of createMultiPolygon(const std::vector<Geometry*>& fromPolys) const
	template<>
	template<>
	void object::test<33>()
	{
		// TODO - mloskot
		//inform("Test not implemented!");
	}

	// Test of buildGeometry(std::vector<Geometry*>* geoms) const
	template<>
	template<>
	void object::test<34>()
	{
		// TODO - mloskot
		//inform("Test not implemented!");
	}

	// Test of buildGeometry(const std::vector<Geometry*>& geoms)
	template<>
	template<>
	void object::test<35>()
	{
		// TODO - mloskot
		//inform("Test not implemented!");
	}

	// Test of
  // buildGeometry(from, to, takeOwnership) const
	template<>
	template<>
	void object::test<36>()
	{
    typedef std::unique_ptr<geos::geom::Geometry> GeometryAutoPtr;
		typedef std::vector<PointPtr> PointVect;

		const std::size_t size = 3;
		geos::geom::Coordinate coord(x_, y_, z_);

		PointVect vec;

		PointPtr geo = nullptr;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 2;
		coord.y *= 2;
		coord.z *= 2;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		coord.x *= 3;
		coord.y *= 3;
		coord.z *= 3;
		geo = factory_->createPoint(coord);
		vec.push_back(geo);

		// Factory creates copy of the vec collection
		GeometryAutoPtr g = factory_->buildGeometry(vec.begin(), vec.end());
		ensure( g.get() != nullptr );
		ensure_equals( g->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT );
		ensure_equals( g->getNumGeometries(), size );

		// FREE MEMORY
		PointVect::const_iterator it;
		for (it = vec.begin(); it != vec.end(); ++it)
		{
			delete (*it);
		}
	}

} // namespace tut
