/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: noding/ScaledNoder.java rev. 1.3 (JTS-1.7.1)
 *
 **********************************************************************/

#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateSequence.h> // for apply and delete
#include <geos/geom/CoordinateFilter.h> // for inheritance
#include <geos/noding/ScaledNoder.h>
#include <geos/noding/SegmentString.h>
#include <geos/util/math.h>
#include <geos/util.h>

#include <functional>
#include <vector>
#include <cassert>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

#ifdef GEOS_DEBUG
#include <iostream>
#include <string>
#endif

using namespace geos::geom;



namespace geos {
namespace noding { // geos.noding

namespace {

#if GEOS_DEBUG > 1
void
sqlPrint(const std::string& table, std::vector<SegmentString*>& ssv)
{
	std::cerr << "CREATE TABLE \"" << table
		<< "\" (id integer, geom geometry);" << std::endl;

	std::cerr << "COPY \"" << table
		<< "\" FROM stdin;" << std::endl;

	for (size_t i=0, n=ssv.size(); i<n; i++)
	{
		SegmentString* ss=ssv[i];
		geom::CoordinateSequence* cs = ss->getCoordinates();
		assert(cs);

		std::cerr << i << '\t' << "LINESTRING"
			<< *cs
			<< std::endl;
	}
	std::cerr << "\\." << std::endl;
}
#endif // GEOS_DEBUG > 1

} // anonym namespace

class ScaledNoder::Scaler : public geom::CoordinateFilter {
public:
	const ScaledNoder& sn;
	Scaler(const ScaledNoder&n): sn(n)
	{
#if GEOS_DEBUG
		std::cerr << "Scaler: offsetX,Y: " << sn.offsetX << ","
			<< sn.offsetY << " scaleFactor: " << sn.scaleFactor
			<< std::endl;
#endif
	}

	//void filter_ro(const geom::Coordinate* c) { assert(0); }

	void filter_rw(geom::Coordinate* c) const override {
		c->x = util::round( ( c->x - sn.offsetX ) * sn.scaleFactor );
		c->y = util::round( ( c->y - sn.offsetY ) * sn.scaleFactor );
	}

private:
    // Declare type as noncopyable
    Scaler(const Scaler& other) = delete;
    Scaler& operator=(const Scaler& rhs) = delete;
};

class ScaledNoder::ReScaler: public geom::CoordinateFilter {
public:
	const ScaledNoder& sn;
	ReScaler(const ScaledNoder&n): sn(n)
	{
#if GEOS_DEBUG
		std::cerr << "ReScaler: offsetX,Y: " << sn.offsetX << ","
			<< sn.offsetY << " scaleFactor: " << sn.scaleFactor
			<< std::endl;
#endif
	}

	void filter_ro(const geom::Coordinate* c) override
    {
        ::geos::ignore_unused_variable_warning(c);
        assert(0);
    }

	void filter_rw(geom::Coordinate* c) const override {
		c->x = c->x / sn.scaleFactor + sn.offsetX;
		c->y = c->y / sn.scaleFactor + sn.offsetY;
	}

private:
    // Declare type as noncopyable
    ReScaler(const ReScaler& other);
    ReScaler& operator=(const ReScaler& rhs);
};

/*private*/
void
ScaledNoder::rescale(SegmentString::NonConstVect& segStrings) const
{
	ReScaler rescaler(*this);
	for (SegmentString::NonConstVect::const_iterator
		i0=segStrings.begin(), i0End=segStrings.end();
			i0!=i0End; ++i0)
	{

		SegmentString* ss=*i0;

		ss->getCoordinates()->apply_rw(&rescaler);

	}
}


/*private*/
void
ScaledNoder::scale(SegmentString::NonConstVect& segStrings) const
{
	Scaler scaler(*this);
	for (SegmentString::NonConstVect::const_iterator
		i0=segStrings.begin(), i0End=segStrings.end();
			i0!=i0End; ++i0)
	{
		SegmentString* ss=*i0;

		CoordinateSequence* cs=ss->getCoordinates();

#ifndef NDEBUG
		size_t npts = cs->size();
#endif
		cs->apply_rw(&scaler);
		assert(cs->size() == npts);

		// Actually, we should be creating *new*
		// SegmentStrings here, but who's going
		// to delete them then ? And is it worth
		// the memory cost ?
		cs->removeRepeatedPoints();

	}
}

ScaledNoder::~ScaledNoder()
{
	for (std::vector<geom::CoordinateSequence*>::const_iterator
		it=newCoordSeq.begin(), end=newCoordSeq.end();
		it != end;
		++it)
	{
		delete *it;
	}
}


/*public*/
SegmentString::NonConstVect*
ScaledNoder::getNodedSubstrings() const
{
	SegmentString::NonConstVect* splitSS = noder.getNodedSubstrings();

#if GEOS_DEBUG > 1
	sqlPrint("nodedSegStr", *splitSS);
#endif

	if ( isScaled ) rescale(*splitSS);

#if GEOS_DEBUG > 1
	sqlPrint("scaledNodedSegStr", *splitSS);
#endif

	return splitSS;

}

/*public*/
void
ScaledNoder::computeNodes(SegmentString::NonConstVect* inputSegStr)
{

#if GEOS_DEBUG > 1
	sqlPrint("inputSegStr", *inputSegStr);
#endif

	if (isScaled) scale(*inputSegStr);

#if GEOS_DEBUG > 1
	sqlPrint("scaledInputSegStr", *inputSegStr);
#endif

	noder.computeNodes(inputSegStr);
}





} // namespace geos.noding
} // namespace geos
