#ifndef EDITSCENE_TERRAIN_COMMANDS_HPP
#define EDITSCENE_TERRAIN_COMMANDS_HPP
#pragma once

#include <Ogre.h>
#include <vector>
#include <string>
#include <functional>

/**
 * @file TerrainCommands.hpp
 * @brief Command-pattern terrain editing API.
 *
 * Terrain editing operations (raise, lower, smooth, flatten, splat paint)
 * are encapsulated as command objects that can be queued, executed
 * synchronously, and scripted from Lua.
 *
 * Usage (C++):
 * @code
 *   TerrainSystem *ts = TerrainSystem::getInstance();
 *   TerrainCommandQueue &q = ts->getCommandQueue();
 *   q.push(TerrainCommand::raise(worldPos, radius, strength));
 *   q.push(TerrainCommand::splat(worldPos, radius, strength, layerIdx, weight));
 *   q.executeAll();  // blocks until all done, results in q.results()
 *   // On next update(), rebuildDirtyPages() picks up the changes.
 * @endcode
 *
 * Usage (Lua):
 * @code
 *   local q = terrain.create_command_queue()
 *   terrain.queue_raise(q, x, y, z, radius, strength)
 *   terrain.queue_splat(q, x, y, z, radius, strength, layer, weight)
 *   local results = terrain.execute_queue(q)
 * @endcode
 */

/* ------------------------------------------------------------------ */
/* TerrainCommand — one brush operation                               */
/* ------------------------------------------------------------------ */

enum class TerrainCommandType {
	Raise,
	Lower,
	Smooth,
	Flatten,
	SplatPaint
};

struct TerrainCommand {
	TerrainCommandType type = TerrainCommandType::Raise;

	/* Brush center in world-space coordinates. */
	Ogre::Vector3 worldPos = Ogre::Vector3::ZERO;

	/* Brush radius in world units. */
	float radius = 5.0f;

	/* Brush strength 0..1 (opacity of the effect). */
	float strength = 0.5f;

	/* Layer index for SplatPaint (0 = first blend layer). */
	int layerIndex = 0;

	/* Blend weight for SplatPaint (0..1, 0 = erase, 1 = full paint). */
	float splatWeight = 1.0f;

	/* --- Results (populated by executeAll) --- */
	bool executed = false;
	bool success = false;
	int pagesAffected = 0;
	std::string errorMsg;

	/* Factory helpers */
	static TerrainCommand raise(const Ogre::Vector3 &pos, float radius,
				    float strength)
	{
		TerrainCommand c;
		c.type = TerrainCommandType::Raise;
		c.worldPos = pos;
		c.radius = radius;
		c.strength = strength;
		return c;
	}

	static TerrainCommand lower(const Ogre::Vector3 &pos, float radius,
				    float strength)
	{
		TerrainCommand c;
		c.type = TerrainCommandType::Lower;
		c.worldPos = pos;
		c.radius = radius;
		c.strength = strength;
		return c;
	}

	static TerrainCommand smooth(const Ogre::Vector3 &pos, float radius,
				     float strength)
	{
		TerrainCommand c;
		c.type = TerrainCommandType::Smooth;
		c.worldPos = pos;
		c.radius = radius;
		c.strength = strength;
		return c;
	}

	static TerrainCommand flatten(const Ogre::Vector3 &pos, float radius,
				      float strength)
	{
		TerrainCommand c;
		c.type = TerrainCommandType::Flatten;
		c.worldPos = pos;
		c.radius = radius;
		c.strength = strength;
		return c;
	}

	static TerrainCommand splat(const Ogre::Vector3 &pos, float radius,
				    float strength, int layerIndex,
				    float splatWeight)
	{
		TerrainCommand c;
		c.type = TerrainCommandType::SplatPaint;
		c.worldPos = pos;
		c.radius = radius;
		c.strength = strength;
		c.layerIndex = layerIndex;
		c.splatWeight = splatWeight;
		return c;
	}
};

/* ------------------------------------------------------------------ */
/* TerrainCommandQueue — FIFO queue with synchronous execution        */
/* ------------------------------------------------------------------ */

class TerrainSystem;

class TerrainCommandQueue {
public:
	TerrainCommandQueue() = default;

	/** Push a command to the back of the queue. */
	void push(const TerrainCommand &cmd) { m_queue.push_back(cmd); }
	void push(TerrainCommand &&cmd)
	{
		m_queue.push_back(std::move(cmd));
	}

	/**
	 * Execute all queued commands synchronously.
	 *
	 * Each command is applied to the heightmap data immediately.  Affected
	 * terrain pages are marked dirty and will be rebuilt on the next
	 * TerrainSystem::update() call.  Results (success, pagesAffected,
	 * errorMsg) are stored in each command and also appended to m_results.
	 *
	 * This call blocks until every command in the queue has been processed.
	 * The queue is drained after execution.
	 */
	void executeAll();

	/** Discard all queued commands without executing them. */
	void clear()
	{
		m_queue.clear();
		m_results.clear();
	}

	/** Number of commands currently queued. */
	size_t size() const { return m_queue.size(); }

	/** Results from the last executeAll() call. */
	const std::vector<TerrainCommand> &results() const
	{
		return m_results;
	}

	/** Set the owning TerrainSystem (called by TerrainSystem ctor). */
	void setTerrainSystem(TerrainSystem *ts) { m_terrainSystem = ts; }

private:
	void executeCommandInternal(const TerrainCommand &cmd);
	std::vector<TerrainCommand> m_queue;
	std::vector<TerrainCommand> m_results;
	TerrainSystem *m_terrainSystem = nullptr;
};

#endif /* EDITSCENE_TERRAIN_COMMANDS_HPP */
