/**********************************************************************
 *
 * 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: simplify/LineSegmentIndex.java rev. 1.1 (JTS-1.7.1)
 *
 **********************************************************************/

#include <geos/simplify/LineSegmentIndex.h>
#include <geos/simplify/TaggedLineSegment.h>
#include <geos/simplify/TaggedLineString.h>
#include <geos/index/quadtree/Quadtree.h>
#include <geos/index/ItemVisitor.h>
#include <geos/geom/LineSegment.h>
#include <geos/geom/Envelope.h>

#include <vector>
#include <memory> // for unique_ptr
#include <cassert>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

#ifdef GEOS_DEBUG
#include <iostream>
#endif

using namespace std;
using namespace geos::geom;
using namespace geos::index::quadtree;

namespace geos {
namespace simplify { // geos::simplify

/**
 * ItemVisitor subclass to reduce volume of query results.
 */
class LineSegmentVisitor: public index::ItemVisitor {

// MD - only seems to make about a 10% difference in overall time.

private:

	const LineSegment* querySeg;

	unique_ptr< vector<LineSegment*> > items;

public:

	LineSegmentVisitor(const LineSegment* s)
		:
		ItemVisitor(),
		querySeg(s),
		items(new vector<LineSegment*>())
	{}

	~LineSegmentVisitor() override
	{
		// nothing to do, LineSegments are not owned by us
	}

	LineSegmentVisitor(const LineSegmentVisitor& o)
		:
		ItemVisitor(),
		querySeg(o.querySeg),
		items(new vector<LineSegment*>(*(o.items.get())))
	{
	}

	LineSegmentVisitor& operator=(const LineSegmentVisitor& o)
	{
		if ( this == &o ) return *this;
		querySeg = o.querySeg;
		items.reset(new vector<LineSegment*>(*(o.items.get())));
		return *this;
	}

	void visitItem(void* item) override
	{
		LineSegment* seg = (LineSegment*) item;
		if ( Envelope::intersects(seg->p0, seg->p1,
				querySeg->p0, querySeg->p1) )
		{
			items->push_back(seg);
		}
	}

	unique_ptr< vector<LineSegment*> > getItems()
	{
		// NOTE: Apparently, this is 'source' method giving up the object resource.
		return std::move(items);
	}


};

/*public*/
LineSegmentIndex::LineSegmentIndex()
	:
	index(new Quadtree())
{
}

/*public*/
LineSegmentIndex::~LineSegmentIndex()
{
	for (size_t i=0, n=newEnvelopes.size(); i<n; ++i)
	{
		delete newEnvelopes[i];
	}
}

/*public*/
void
LineSegmentIndex::add(const TaggedLineString& line)
{
	const vector<TaggedLineSegment*>& segs = line.getSegments();
	for (size_t i=0, n=segs.size(); i<n; ++i)
	{
		const LineSegment* seg = segs[i];
		add(seg);
	}
}

/*public*/
void
LineSegmentIndex::add(const LineSegment* seg)
{
	Envelope* env = new Envelope(seg->p0, seg->p1);
	newEnvelopes.push_back(env);

	// We need a cast because index wants a non-const,
	// altought it won't change the argument
	index->insert(env, (LineSegment*)seg);
}

/*public*/
void
LineSegmentIndex::remove(const LineSegment* seg)
{
	Envelope env(seg->p0, seg->p1);

	// We need a cast because index wants a non-const
	// altought it won't change the argument
	index->remove(&env, (LineSegment*)seg);
}

/*public*/
unique_ptr< vector<LineSegment*> >
LineSegmentIndex::query(const LineSegment* querySeg) const
{
	Envelope env(querySeg->p0, querySeg->p1);

	LineSegmentVisitor visitor(querySeg);
	index->query(&env, visitor);

	unique_ptr< vector<LineSegment*> > itemsFound = visitor.getItems();

	return itemsFound;
}

} // namespace geos::simplify
} // namespace geos
