#ifndef EDITSCENE_TILECACHE_NAVMESH_HPP
#define EDITSCENE_TILECACHE_NAVMESH_HPP
#pragma once

#include <Ogre.h>
#include <vector>
#include <memory>

// Forward declarations
struct rcContext;
struct dtNavMesh;
struct dtNavMeshQuery;
struct dtQueryFilter;
struct dtTileCache;
struct dtTileCacheAlloc;
struct dtTileCacheCompressor;
struct dtTileCacheMeshProcess;

class PartitionedMesh;

/**
 * Self-contained DetourTileCache navmesh builder and query engine.
 *
 * Builds a tiled navmesh from Ogre::Entity geometry. Supports partial
 * tile rebuilds when geometry in a specific area changes.
 */
class TileCacheNavMesh {
public:
	struct BuildParams {
		float cellSize = 0.3f;
		float cellHeight = 0.2f;
		float agentHeight = 2.5f;
		float agentRadius = 0.5f;
		float agentMaxClimb = 1.0f;
		float agentMaxSlope = 20.0f;
		float edgeMaxLen = 12.0f;
		float edgeMaxError = 1.3f;
		float regionMinSize = 20.0f;
		float regionMergeSize = 20.0f;
		int vertsPerPoly = 6;
		float detailSampleDist = 6.0f;
		float detailSampleMaxError = 1.0f;
		int tileSize = 48; // voxels per tile
	};

	TileCacheNavMesh(Ogre::SceneManager *sceneMgr,
			 const BuildParams &params);
	~TileCacheNavMesh();

	TileCacheNavMesh(const TileCacheNavMesh &) = delete;
	TileCacheNavMesh &operator=(const TileCacheNavMesh &) = delete;

	// --- Build ---
	bool build(const std::vector<Ogre::Entity *> &entities);
	bool isBuilt() const { return m_navMesh != nullptr; }

	// --- Partial rebuild ---
	void rebuildTilesInArea(const Ogre::AxisAlignedBox &area);

	// --- Queries ---
	bool findPath(const Ogre::Vector3 &start,
		      const Ogre::Vector3 &end,
		      std::vector<Ogre::Vector3> &path);
	bool findNearestPoint(const Ogre::Vector3 &pos,
			      Ogre::Vector3 &out);
	Ogre::Vector3 getRandomPoint();

	// --- Debug ---
	void drawNavMesh();
	void clearDebugDraw();

private:
	Ogre::SceneManager *m_sceneMgr;
	BuildParams m_params;

	// Input geometry
	std::vector<float> m_verts;
	std::vector<int> m_tris;
	float m_bmin[3];
	float m_bmax[3];
	std::unique_ptr<PartitionedMesh> m_partitionedMesh;

	// Tile cache
	dtTileCacheAlloc *m_talloc;
	dtTileCacheCompressor *m_tcomp;
	dtTileCacheMeshProcess *m_tmproc;
	dtTileCache *m_tileCache;

	// Detour output
	dtNavMesh *m_navMesh;
	dtNavMeshQuery *m_navQuery;
	dtQueryFilter *m_filter;

	// Tile grid dimensions
	int m_tw = 0;
	int m_th = 0;
	float m_cellSize = 0;

	// Recast context
	rcContext *m_ctx;

	// Debug draw
	Ogre::ManualObject *m_debugMO;
	Ogre::SceneNode *m_debugNode;

	// Extents for nearest-poly searches
	float m_extents[3];

	// Internal helpers
	bool extractMeshData(const std::vector<Ogre::Entity *> &entities);
	bool initTileCache();
	int rasterizeTileLayers(int tx, int ty,
				struct TileCacheData *tiles,
				int maxTiles);
	bool buildTile(int tx, int ty);
	bool removeTile(int tx, int ty);
	void getTileCoords(const float *pos, int &tx, int &ty);
};

#endif // EDITSCENE_TILECACHE_NAVMESH_HPP
