#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>
#include <cstdint>

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;
		}

		bool containsConsideringBorder(float px, float py, float border) const {
			return px >= x+border && px <= x + w-border && py >= y+border && py <= y + h-border;
		}
	};

	enum class ButtonState {
		Normal,
		Hover,
		Pressed,
		Disabled
	};

	enum class LayoutType {
		Frame, // Позиционирование по X, Y
		Linear // Автоматическое позиционирование
	};

	enum class Orientation {
		Vertical,
		Horizontal
	};

	enum class HorizontalAlign {
		Left,
		Center,
		Right
	};

	enum class VerticalAlign {
		Top,
		Center,
		Bottom
	};

	enum class HorizontalGravity {
		Left,
		Center,
		Right
	};

	enum class VerticalGravity {
		Bottom, // Обычно в OpenGL Y растет вверх, так что низ - это 0
		Center,
		Top
	};


	// В структуру или класс, отвечающий за LinearLayout (вероятно, это свойства UiNode)
	struct LayoutSettings {
		HorizontalAlign hAlign = HorizontalAlign::Left;
		VerticalAlign vAlign = VerticalAlign::Top;

		HorizontalGravity hGravity = HorizontalGravity::Left;
		VerticalGravity vGravity = VerticalGravity::Top;
	};

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

		VertexRenderStruct mesh;

		std::function<void(const std::string&)> onClick;
		std::function<void(const std::string&)> onPress; // fires on touch/mouse down

		// 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 UiTextButton {
		std::string name;
		UiRect rect;
		float border = 0;

		// Textures are optional — button can be text-only
		std::shared_ptr<Texture> texNormal;
		std::shared_ptr<Texture> texHover;
		std::shared_ptr<Texture> texPressed;
		std::shared_ptr<Texture> texDisabled;

		ButtonState state = ButtonState::Normal;
		VertexRenderStruct mesh;

		// Text drawn on top of the button
		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 };
		bool textCentered = true;
		float textPaddingX = 12.0f;
		float textPaddingY = 0.0f;

		std::unique_ptr<TextRenderer> textRenderer;

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

		// 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 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;
		bool wrap = false;
		bool topAligned = true;
		float paddingX = 0.0f;
		float paddingY = 0.0f;
		int maxLines = 0; // 0 = no line limit

		std::unique_ptr<TextRenderer> textRenderer;

		void draw(Renderer& renderer) const;
	};

	struct UiTextField {
		std::string name;
		UiRect rect;
		std::string text = "";
		std::string placeholder = "";
		std::string fontPath = "resources/fonts/DroidSans.ttf";
		int fontSize = 32;
		std::array<float, 4> color = { 1.f, 1.f, 1.f, 1.f };
		std::array<float, 4> placeholderColor = { 0.5f, 0.5f, 0.5f, 1.f };
		std::array<float, 4> backgroundColor = { 0.2f, 0.2f, 0.2f, 1.f };
		std::array<float, 4> borderColor = { 0.5f, 0.5f, 0.5f, 1.f };
		int maxLength = 256;
		bool focused = false;

		std::unique_ptr<TextRenderer> textRenderer;
		std::function<void(const std::string&, const std::string&)> onTextChanged;

		void draw(Renderer& renderer) const;
	};

	struct UiStaticImage {
		std::string name;
		UiRect rect;
		std::shared_ptr<Texture> texture;

		VertexRenderStruct mesh;

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

	struct UiNode {
		std::string name;
		LayoutType layoutType = LayoutType::Frame;
		Orientation orientation = Orientation::Vertical;
		float spacing = 0.0f;

		LayoutSettings layoutSettings;

		// Внутренние вычисленные координаты для OpenGL
		// Именно их мы передаем в Vertex Buffer при buildMesh()
		UiRect screenRect;

		// Данные из JSON (желаемые размеры и смещения)
		float localX = 0;
		float localY = 0;
		float width = 0;
		float height = 0;

		float scaleX = 1.0f;
		float scaleY = 1.0f;

		bool visible = true;

		// Иерархия
		std::vector<std::shared_ptr<UiNode>> children;

		// Компоненты (только один из них обычно активен для ноды)
		std::shared_ptr<UiButton> button;
		std::shared_ptr<UiTextButton> textButton;
		std::shared_ptr<UiSlider> slider;
		std::shared_ptr<UiTextView> textView;
		std::shared_ptr<UiTextField> textField;
		std::shared_ptr<UiStaticImage> staticImage;

		// Анимации
		struct AnimStep {
			std::string type;
			float toX = 0.0f;
			float toY = 0.0f;
			float toScale = 1.0f; // Полезно добавить для UI
			float durationMs = 0.0f;
			std::string easing = "linear";
		};
		struct AnimSequence {
			std::vector<AnimStep> steps;
			bool repeat = false;
		};
		std::map<std::string, AnimSequence> animations;
	};

	std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
	std::shared_ptr<UiNode> loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");


	class UiManager {
	public:
		UiManager() = default;

		// Sentinel finger ID used for mouse events on desktop/web
		static constexpr int64_t MOUSE_FINGER_ID = -1LL;

		void replaceRoot(std::shared_ptr<UiNode> newRoot);
		void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
		void appendFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");

		void draw(Renderer& renderer);

		// Multi-touch methods (used directly for touch events with per-finger IDs)
		void onTouchDown(int64_t fingerId, int x, int y);
		void onTouchUp(int64_t fingerId, int x, int y);
		void onTouchMove(int64_t fingerId, int x, int y);

		// Mouse convenience wrappers (delegate to touch with MOUSE_FINGER_ID)
		void onMouseMove(int x, int y) { onTouchMove(MOUSE_FINGER_ID, x, y); }
		void onMouseDown(int x, int y) { onTouchDown(MOUSE_FINGER_ID, x, y); }
		void onMouseUp(int x, int y)   { onTouchUp(MOUSE_FINGER_ID, x, y); }

		void onKeyPress(unsigned char key);
		void onKeyBackspace();

		// Returns true if any finger is currently interacting with UI
		bool isUiInteraction() const {
			return !pressedButtons.empty() || !pressedTextButtons.empty() || !pressedSliders.empty() || focusedTextField != nullptr;
		}

		// Returns true if this specific finger is currently interacting with UI
		bool isUiInteractionForFinger(int64_t fingerId) const {
			return pressedButtons.count(fingerId) > 0 || pressedTextButtons.count(fingerId) > 0 || pressedSliders.count(fingerId) > 0 || focusedTextField != 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;
				}
			}
			for (auto& tb : textButtons) {
				if (tb) {
					tb->animOffsetX = 0.0f;
					tb->animOffsetY = 0.0f;
					tb->animScaleX = 1.0f;
					tb->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 setButtonPressCallback(const std::string& name, std::function<void(const std::string&)> cb);

		std::shared_ptr<UiTextButton> findTextButton(const std::string& name);
		bool setTextButtonCallback(const std::string& name, std::function<void(const std::string&)> cb);
		bool setTextButtonPressCallback(const std::string& name, std::function<void(const std::string&)> cb);
		bool setTextButtonText(const std::string& name, const std::string& newText);
		bool setTextButtonColor(const std::string& name, const std::array<float, 4>& color);

		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 setTextColor(const std::string& name, const std::array<float, 4>& color);

		std::shared_ptr<UiTextField> findTextField(const std::string& name);
		bool setTextFieldCallback(const std::string& name, std::function<void(const std::string&, const std::string&)> cb);
		std::string getTextFieldValue(const std::string& name);

		std::shared_ptr<UiStaticImage> findStaticImage(const std::string& name);

		bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
		bool pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot);
		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);
		void updateAllLayouts();

		std::shared_ptr<UiNode> findNode(const std::string& name);

		bool setNodeVisible(const std::string& nodeName, bool visible);
		bool getNodeVisible(const std::string& nodeName);

	private:
		void layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY, float parentW, float parentH, float finalLocalX, float finalLocalY);
		void syncComponentRects(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<UiTextButton>> textButtons;
		std::vector<std::shared_ptr<UiSlider>> sliders;
		std::vector<std::shared_ptr<UiTextView>> textViews;
		std::vector<std::shared_ptr<UiTextField>> textFields;
		std::vector<std::shared_ptr<UiStaticImage>> staticImages;

		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)

		// Per-finger tracking for multi-touch support
		std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
		std::map<int64_t, std::shared_ptr<UiTextButton>> pressedTextButtons;
		std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
		std::shared_ptr<UiTextField> focusedTextField;

		struct MenuState {
			std::shared_ptr<UiNode> root;
			std::vector<std::shared_ptr<UiButton>> buttons;
			std::vector<std::shared_ptr<UiTextButton>> textButtons;
			std::vector<std::shared_ptr<UiSlider>> sliders;
			std::vector<std::shared_ptr<UiTextView>> textViews;
			std::vector<std::shared_ptr<UiTextField>> textFields;
			std::vector<std::shared_ptr<UiStaticImage>> staticImages;
			std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
			std::map<int64_t, std::shared_ptr<UiTextButton>> pressedTextButtons;
			std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
			std::shared_ptr<UiTextField> focusedTextField;
			std::string path;
			std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks;
		};

		std::vector<MenuState> menuStack;

	};

} // namespace ZL