using System.Collections.Generic;
using Godot;

// Браузер ВСЕХ персонажей в нативном виде (без Spine): сканирует characters/*/*_native.tscn,
// инстанцирует выбранную сцену и играет её анимации.
//   Q / E — персонаж,  W / S — состояние (скелет),  A / D — анимация
public partial class NativeBrowser : Node2D
{
	private struct State { public string Label; public string Path; }
	private struct Char { public string Name; public State[] States; }

	private Char[] _chars = System.Array.Empty<Char>();
	private int _ci, _si, _ai;
	private Node2D _inst;
	private AnimationPlayer _player;
	private string[] _anims = System.Array.Empty<string>();
	private Label _label;

	private ShaderMaterial _skinMat, _hairMat, _eyeMat;

	public override void _Ready()
	{
		var bg = new ColorRect { Color = new Color(0.12f, 0.13f, 0.15f), AnchorRight = 1, AnchorBottom = 1, MouseFilter = Control.MouseFilterEnum.Ignore };
		AddChild(bg);
		_label = new Label { Position = new Vector2(16, 12) };
		_label.AddThemeFontSizeOverride("font_size", 18);
		_label.AddThemeConstantOverride("outline_size", 4);
		_label.AddThemeColorOverride("font_outline_color", Colors.Black);
		AddChild(_label);

		BuildCustomization();

		Scan();
		if (_chars.Length == 0) { _label.Text = "Нет *_native.tscn в characters/"; return; }
		LoadState();
	}

	private void Scan()
	{
		var chars = new List<Char>();
		var root = DirAccess.Open("res://characters");
		if (root == null) return;
		var names = new List<string>(root.GetDirectories());
		names.Sort();
		foreach (var name in names)
		{
			var d = DirAccess.Open("res://characters/" + name);
			if (d == null) continue;
			var states = new List<State>();
			foreach (var file in d.GetFiles())
			{
				if (!file.EndsWith("_native.tscn")) continue;
				states.Add(new State { Label = file.Replace("_native.tscn", ""), Path = $"res://characters/{name}/{file}" });
			}
			if (states.Count > 0)
			{
				states.Sort((a, b) => string.CompareOrdinal(a.Label, b.Label));
				chars.Add(new Char { Name = name, States = states.ToArray() });
			}
		}
		_chars = chars.ToArray();
		_ci = System.Array.FindIndex(_chars, c => c.Name == "elf1");
		if (_ci < 0) _ci = 0;
	}

	private void LoadState()
	{
		_inst?.QueueFree();
		_inst = null;
		_player = null;
		_anims = System.Array.Empty<string>();
		_ai = 0;

		var st = _chars[_ci].States[_si];
		var packed = ResourceLoader.Load<PackedScene>(st.Path);
		if (packed == null) { UpdateLabel(); return; }
		_inst = packed.Instantiate<Node2D>();
		AddChild(_inst);
		Fit(_inst);
		ApplyMaterials(_inst);
		_player = _inst.GetNodeOrNull<AnimationPlayer>("AnimationPlayer");
		if (_player != null)
		{
			_anims = _player.GetAnimationList();
			PlayAnim();
		}
		else UpdateLabel();
	}

	private void Fit(Node2D inst)
	{
		Vector2 vp = GetViewportRect().Size, baseVp = new Vector2(1280, 720);
		float k = Mathf.Min(vp.X / baseVp.X, vp.Y / baseVp.Y);
		inst.Scale = new Vector2(k, k);
		inst.Position = (vp - baseVp * k) * 0.5f;
	}

	private void PlayAnim()
	{
		if (_player != null && _anims.Length > 0)
			_player.Play(_anims[_ai]);
		UpdateLabel();
	}

	private void UpdateLabel()
	{
		var c = _chars[_ci];
		var st = c.States[_si];
		string anim = _anims.Length > 0 ? _anims[_ai] : "—";
		_label.Text =
			$"НАТИВНО (без Spine)   персонаж: {c.Name} ({_ci + 1}/{_chars.Length})   [Q/E]\n" +
			$"состояние: {st.Label} ({_si + 1}/{c.States.Length})   [W/S]\n" +
			$"анимация: {anim} ({(_anims.Length > 0 ? _ai + 1 : 0)}/{_anims.Length})   [A/D]   ·   Esc — меню";
	}

	// ---------- кастомизация цвета ----------
	private void BuildCustomization()
	{
		var shader = ResourceLoader.Load<Shader>("res://assets/recolor.gdshader");
		_skinMat = MakeMat(shader);
		_hairMat = MakeMat(shader);
		_eyeMat = MakeMat(shader);

		var bar = new HBoxContainer { Position = new Vector2(16, GetViewportRect().Size.Y - 46) };
		bar.AddThemeConstantOverride("separation", 8);
		AddChild(bar);

		AddPicker(bar, "Кожа", _skinMat, new Color(0.95f, 0.78f, 0.66f));
		AddPicker(bar, "Волосы", _hairMat, new Color(0.55f, 0.4f, 0.95f));
		AddPicker(bar, "Глаза", _eyeMat, new Color(0.35f, 0.75f, 0.95f));

		var reset = new Button { Text = "Сброс" };
		reset.Pressed += () =>
		{
			_skinMat.SetShaderParameter("enabled", false);
			_hairMat.SetShaderParameter("enabled", false);
			_eyeMat.SetShaderParameter("enabled", false);
		};
		bar.AddChild(reset);
	}

	private static ShaderMaterial MakeMat(Shader shader)
	{
		var m = new ShaderMaterial { Shader = shader };
		m.SetShaderParameter("enabled", false);
		return m;
	}

	private void AddPicker(Node parent, string label, ShaderMaterial mat, Color def)
	{
		var l = new Label { Text = label };
		l.AddThemeColorOverride("font_color", Colors.White);
		l.AddThemeConstantOverride("outline_size", 3);
		l.AddThemeColorOverride("font_outline_color", Colors.Black);
		parent.AddChild(l);

		var p = new ColorPickerButton { Color = def, CustomMinimumSize = new Vector2(48, 28), EditAlpha = false };
		p.ColorChanged += c =>
		{
			mat.SetShaderParameter("target_color", c);
			mat.SetShaderParameter("enabled", true);
		};
		parent.AddChild(p);
	}

	private void ApplyMaterials(Node2D inst)
	{
		var skel = inst.GetNodeOrNull<Skeleton2D>("SpineRoot/Skeleton2D");
		if (skel == null) return;
		foreach (var child in skel.GetChildren())
		{
			if (child is not Polygon2D poly) continue;
			poly.Material = Category(poly.Name.ToString().ToLower());
		}
	}

	private ShaderMaterial Category(string n)
	{
		if (n.Contains("eye")) return _eyeMat;
		if (n.Contains("hair")) return _hairMat;
		bool cloth = n.Contains("cloth") || n.Contains("chain") || n.Contains("armor")
			|| n.Contains("armour") || n.Contains("greave") || n.Contains("dress")
			|| n.Contains("skirt") || n.Contains("boot") || n.Contains("shoe")
			|| n.Contains("sock") || n.Contains("stock") || n.Contains("pant");
		bool skin = n.Contains("body") || n.Contains("tits") || n.Contains("nipple")
			|| n.Contains("face") || n.Contains("head") || n.Contains("milk")
			|| n.Contains("pregnant") || n.Contains("arm") || n.Contains("leg");
		return (skin && !cloth) ? _skinMat : null;
	}

	private static int Wrap(int i, int n) => n <= 0 ? 0 : ((i % n) + n) % n;

	public override void _UnhandledInput(InputEvent @event)
	{
		if (@event is InputEventKey esc && esc.Pressed && esc.Keycode == Key.Escape)
		{
			GetTree().ChangeSceneToFile("res://scenes/MainMenu.tscn");
			return;
		}
		if (_chars.Length == 0 || @event is not InputEventKey k || !k.Pressed || k.Echo) return;
		switch (k.Keycode)
		{
			case Key.E: _ci = Wrap(_ci + 1, _chars.Length); _si = 0; LoadState(); break;
			case Key.Q: _ci = Wrap(_ci - 1, _chars.Length); _si = 0; LoadState(); break;
			case Key.S: case Key.Down: _si = Wrap(_si + 1, _chars[_ci].States.Length); LoadState(); break;
			case Key.W: case Key.Up: _si = Wrap(_si - 1, _chars[_ci].States.Length); LoadState(); break;
			case Key.D: case Key.Right: if (_anims.Length > 0) { _ai = Wrap(_ai + 1, _anims.Length); PlayAnim(); } break;
			case Key.A: case Key.Left: if (_anims.Length > 0) { _ai = Wrap(_ai - 1, _anims.Length); PlayAnim(); } break;
		}
	}
}
