#pragma once

#include "render/Renderer.h"
#include "render/TextureManager.h"
#include "render/TextRenderer.h"
#include "Environment.h"
#include "external/nlohmann/json.hpp"
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <map>

namespace ZL {

	using json = nlohmann::json;

	struct UiRect {
		float x = 0;
		float y = 0;
		float w = 0;
		float h = 0;
		bool contains(float px, float py) const {
			return px >= x && px <= x + w && py >= y && py <= y + h;
		}
	};

	enum class ButtonState {
		Normal,
		Hover,
		Pressed
	};

	struct UiButton {
		std::string name;
		UiRect rect;
		std::shared_ptr<Texture> texNormal;
		std::shared_ptr<Texture> texHover;
		std::shared_ptr<Texture> texPressed;
		ButtonState state = ButtonState::Normal;

		VertexRenderStruct mesh;

		std::function<void(const std::string&)> onClick;

		// animation runtime
		float animOffsetX = 0.0f;
		float animOffsetY = 0.0f;
		float animScaleX = 1.0f;
		float animScaleY = 1.0f;

		void buildMesh();
		void draw(Renderer& renderer) const;
	};

	struct UiSlider {
		std::string name;
		UiRect rect;
		std::shared_ptr<Texture> texTrack;
		std::shared_ptr<Texture> texKnob;

		VertexRenderStruct trackMesh;
		VertexRenderStruct knobMesh;

		float value = 0.0f;
		bool vertical = true;

		std::function<void(const std::string&, float)> onValueChanged;

		void buildTrackMesh();
		void buildKnobMesh();
		void draw(Renderer& renderer) const;
	};

	struct UiTextView {
		std::string name;
		UiRect rect;
		std::string text = "";
		std::string fontPath = "resources/fonts/DroidSans.ttf";
		int fontSize = 32;
		std::array<float, 4> color = { 1.f, 1.f, 1.f, 1.f }; // rgba
		bool centered = true;

		std::unique_ptr<TextRenderer> textRenderer;

		void draw(Renderer& renderer) const {
			if (textRenderer) {
				textRenderer->drawText(text, rect.x + rect.w / 2, rect.y + rect.h / 2, 1.0f, centered, color);
			}
		}
	};

	struct UiNode {
		std::string type;
		UiRect rect;
		std::string name;
		std::vector<std::shared_ptr<UiNode>> children;
		std::shared_ptr<UiButton> button;
		std::shared_ptr<UiSlider> slider;
		std::shared_ptr<UiTextView> textView;
		std::string orientation = "vertical";
		float spacing = 0.0f;

		struct AnimStep {
			std::string type;
			float toX = 0.0f;
			float toY = 0.0f;
			float durationMs = 0.0f;
			std::string easing = "linear";
		};
		struct AnimSequence {
			std::vector<AnimStep> steps;
			bool repeat = false;
		};
		std::map<std::string, AnimSequence> animations;
	};

	class UiManager {
	public:
		UiManager() = default;

		void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");

		void draw(Renderer& renderer);

		void onMouseMove(int x, int y);
		void onMouseDown(int x, int y);
		void onMouseUp(int x, int y);

		bool isUiInteraction() const {
			return pressedButton != nullptr || pressedSlider != nullptr;
		}

		void stopAllAnimations() {
			nodeActiveAnims.clear();

			for (auto& b : buttons) {
				if (b) {
					b->animOffsetX = 0.0f;
					b->animOffsetY = 0.0f;
					b->animScaleX = 1.0f;
					b->animScaleY = 1.0f;
				}
			}
		}

		std::shared_ptr<UiButton> findButton(const std::string& name);

		bool setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb);

		bool addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
			const std::string& trackPath, const std::string& knobPath, float initialValue = 0.0f, bool vertical = true);

		std::shared_ptr<UiSlider> findSlider(const std::string& name);
		bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
		bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)

		std::shared_ptr<UiTextView> findTextView(const std::string& name);
		bool setText(const std::string& name, const std::string& newText);

		bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
		bool popMenu();
		void clearMenuStack();

		void update(float deltaMs);
		void startAnimation(const std::string& animName);
		bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
		bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
		bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);

	private:
		std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
		void layoutNode(const std::shared_ptr<UiNode>& node);
		void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);

		struct ActiveAnim {
			std::string name;
			const UiNode::AnimSequence* seq = nullptr;
			size_t stepIndex = 0;
			float elapsedMs = 0.0f;
			bool repeat = false;
			float startOffsetX = 0.0f;
			float startOffsetY = 0.0f;
			float endOffsetX = 0.0f;
			float endOffsetY = 0.0f;
			float startScaleX = 1.0f;
			float startScaleY = 1.0f;
			float endScaleX = 1.0f;
			float endScaleY = 1.0f;
			std::function<void()> onComplete;

			float origOffsetX = 0.0f;
			float origOffsetY = 0.0f;
			float origScaleX = 1.0f;
			float origScaleY = 1.0f;
			bool stepStarted = false;
		};

		std::shared_ptr<UiNode> root;
		std::vector<std::shared_ptr<UiButton>> buttons;
		std::vector<std::shared_ptr<UiSlider>> sliders;
		std::vector<std::shared_ptr<UiTextView>> textViews;

		std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
		std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)

		std::shared_ptr<UiButton> pressedButton;
		std::shared_ptr<UiSlider> pressedSlider;

		struct MenuState {
			std::shared_ptr<UiNode> root;
			std::vector<std::shared_ptr<UiButton>> buttons;
			std::vector<std::shared_ptr<UiSlider>> sliders;
			std::shared_ptr<UiButton> pressedButton;
			std::shared_ptr<UiSlider> pressedSlider;
			std::string path;
			std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks;
		};

		std::vector<MenuState> menuStack;

	};

} // namespace ZL