using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Синглтон. Собирает все BlockDefinition, строит Texture2DArray,
/// создаёт один shared Material для всех чанков.
/// Вызвать Initialise() до создания любого чанка.
/// </summary>
public class BlockRegistry : MonoBehaviour
{
    public static BlockRegistry Instance { get; private set; }

    [Header("All block definitions in the game")]
    public List<BlockDefinition> definitions = new();

    [Header("Shader")]
    public Shader voxelShader; // назначить Blocks/VoxelLit через Inspector

    // Доступно после Initialise()
    public Material ChunkMaterial  { get; private set; }
    public Texture2DArray TexArray { get; private set; }

    private readonly Dictionary<string, BlockDefinition> _byId     = new();
    private readonly Dictionary<string, BlockDefinition> _byItemId = new();

    void Awake()
    {
        if (Instance != null) { Destroy(gameObject); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);
        Initialise();
    }

    public void Initialise()
    {
        _byId.Clear();
        _byItemId.Clear();

        if (definitions == null || definitions.Count == 0)
        {
            Debug.LogWarning("[BlockRegistry] No BlockDefinitions assigned.");
            return;
        }

        // Собираем уникальные текстуры (top/bottom/side каждого блока)
        // Порядок: для блока i → top=i*3, bottom=i*3+1, side=i*3+2
        int layerCount = definitions.Count * 3;
        int res        = 128; // разрешение должно совпадать с текстурами блоков

        TexArray = new Texture2DArray(res, res, layerCount,
                                      TextureFormat.RGBA32, true, false);
        TexArray.filterMode = FilterMode.Point; // пиксельный стиль
        TexArray.wrapMode   = TextureWrapMode.Repeat;

        for (int i = 0; i < definitions.Count; i++)
        {
            var def = definitions[i];

            def.topIndex    = i * 3;
            def.bottomIndex = i * 3 + 1;
            def.sideIndex   = i * 3 + 2;

            CopyTexture(def.topTexture,    TexArray, def.topIndex,    res);
            CopyTexture(def.bottomTexture, TexArray, def.bottomIndex, res);
            CopyTexture(def.sideTexture,   TexArray, def.sideIndex,   res);

            _byId[def.blockId] = def;

            if (def.linkedItem != null)
                _byItemId[def.linkedItem.id] = def;
        }

        TexArray.Apply(true, true); // загрузить на GPU, сделать non-readable

        // Создаём один материал для всех чанков
        if (voxelShader == null)
            voxelShader = Shader.Find("Blocks/VoxelLit");

        ChunkMaterial = new Material(voxelShader);
        ChunkMaterial.SetTexture("_MainTexArray", TexArray);

        Debug.Log($"[BlockRegistry] Initialised {definitions.Count} blocks, {layerCount} texture layers.");
    }

    // ── Lookups ───────────────────────────────────────────────────────────────

    public BlockDefinition GetById(string blockId)
    {
        _byId.TryGetValue(blockId, out var def);
        return def;
    }

    /// <summary>Находит BlockDefinition по id связанного ItemData (для постановки блока).</summary>
    public BlockDefinition GetByItemId(string itemDataId)
    {
        _byItemId.TryGetValue(itemDataId, out var def);
        return def;
    }

    // ── Утилиты ───────────────────────────────────────────────────────────────

    /// <summary>
    /// Копирует пиксели из src в Texture2DArray на слой layerIndex.
    /// Если src null или не того размера — заполняет пурпурным (ошибка-текстура).
    /// </summary>
    static void CopyTexture(Texture2D src, Texture2DArray dst, int layerIndex, int res)
    {
        Color[] pixels;

        if (src == null)
        {
            // Пурпурный = "текстура не назначена"
            pixels = new Color[res * res];
            for (int p = 0; p < pixels.Length; p++)
                pixels[p] = (p / res + p) % 2 == 0 ? Color.magenta : Color.black;
        }
        else if (src.width != res || src.height != res)
        {
            Debug.LogWarning($"[BlockRegistry] {src.name} is {src.width}×{src.height}, expected {res}×{res}. Scaling.");
            // Простой fallback — масштабировать через RenderTexture
            var rt  = RenderTexture.GetTemporary(res, res);
            Graphics.Blit(src, rt);
            var prev = RenderTexture.active;
            RenderTexture.active = rt;
            var resized = new Texture2D(res, res, TextureFormat.RGBA32, false);
            resized.ReadPixels(new Rect(0, 0, res, res), 0, 0);
            resized.Apply();
            RenderTexture.active = prev;
            RenderTexture.ReleaseTemporary(rt);
            pixels = resized.GetPixels();
        }
        else
        {
            pixels = src.GetPixels();
        }

        dst.SetPixels(pixels, layerIndex);
    }
}
