// 2009 © Václav Šmilauer <eudoxos@arcig.cz>
#include <pkg/common/Collider.hpp>

namespace yade { // Cannot have #include directive inside.

class NewtonIntegrator;
class FlatGridCollider : public Collider {
	struct Grid {
		typedef std::vector<Body::id_t> idVector;
		Vector3i                        size;
		Vector3r                        mn, mx;
		Real                            step;
		// convert point into its integral coordinates (can be outside grid, use fitGrid to coords inside)
		Vector3i pt2int(const Vector3r& pt)
		{
			Vector3i ret;
			for (int i = 0; i < 3; i++)
				ret[i] = int(math::floor((pt[i] - mn[1]) / step));
			return ret;
		}
		std::vector<idVector> data;
		// force integral coordinate inside (0,sz-1)
		int      fit(int i, int sz) const { return math::max(0, math::min(i, sz - 1)); }
		Vector3i fitGrid(const Vector3i& v) { return Vector3i(fit(v[0], size[0]), fit(v[1], size[1]), fit(v[2], size[2])); }
		// linearize x,y,z → n in data vector
		size_t lin(int x, int y, int z) const { return fit(x, size[0]) + size[0] * fit(y, size[1]) + size[0] * size[1] * fit(z, size[2]); }
		// return vector of ids at (x,y,z)
		idVector&       operator()(int x, int y, int z) { return data[lin(x, y, z)]; }
		idVector&       operator()(const Vector3i& v) { return data[lin(v[0], v[1], v[2])]; }
		const idVector& operator()(int x, int y, int z) const { return data[lin(x, y, z)]; }
	};
	Grid grid;
	int  sphereIdx, facetIdx, wallIdx, boxIdx;
	// needed for maxVelSq
	shared_ptr<NewtonIntegrator> newton;
	// track maximum distance of fastest body, at every step in isActivated
	Real fastestBodyMaxDist;
	void initIndices();
	void updateGrid();
	void updateBodyCells(const shared_ptr<Body>& b);
	void updateCollisions();
	void action() override;
	bool isActivated() override;
	DECLARE_LOGGER;

	// clang-format off
	YADE_CLASS_BASE_DOC_ATTRS_CTOR(FlatGridCollider,Collider,"Non-optimized grid collider, storing grid as dense flat array. Each body is assigned to (possibly multiple) cells, which are arranged in regular grid between *aabbMin* and *aabbMax*, with cell size *step* (same in all directions). Bodies outsize (*aabbMin*, *aabbMax*) are handled gracefully, assigned to closest cells (this will create spurious potential interactions). *verletDist* determines how much is each body enlarged to avoid collision detection at every step.\n\n.. note::\n\tThis collider keeps all cells in linear memory array, therefore will be memory-inefficient for sparse simulations.\n\n.. warning::\n\tobjects :yref:`Body::bound` are not used, :yref:`BoundFunctors<BoundFunctor>` are not used either: assigning cells to bodies is hard-coded internally. Currently handles :yref:`Shapes<Shape>` are: :yref:`Sphere`.\n\n.. note::\n\tPeriodic boundary is not handled (yet).\n\n",
		((Real,verletDist,0,,"Length by which enlarge space occupied by each particle; avoids running collision detection at every step."))
		((Vector3r,aabbMin,Vector3r::Zero(),,"Lower corner of grid."))
		((Vector3r,aabbMax,Vector3r::Zero(),,"Upper corner of grid (approximate, might be rouded up to *minStep*."))
		((Real,step,0,,"Step in the grid (cell size)")),
		initIndices();
	);
	// clang-format on
};
REGISTER_SERIALIZABLE(FlatGridCollider);

} // namespace yade
