// SpatialGridCs — 2D 空间哈希网格 C# 实现
// 职责：固定 Cell 网格 dirty-list 方案重建 + query_circle
// 权威：architecture_design.md §4.3, ADR-L1
//
// ADR-L1 规厙1：内层循环体内禁止 GodotObject.Call() / .Set()
using Godot;
using System;
using System.Collections.Generic;

public partial class SpatialGridCs : Node
{
    private const int CellSize   = 64;
    private const int GridWidth  = 64;  // 64 格×64 格 = 4096×4096 像素覆盖范围
    private const int GridHeight = 64;
    private const int GridTotal  = GridWidth * GridHeight;

    // 网格主体：一维数组模拟二维，直接索引 grid[x + y * GridWidth]
    private readonly List<int>[] _grid = new List<int>[GridTotal];

    // dirty-list：本帧有变化的格子索引
    private readonly HashSet<int> _dirty = new();

    // 实体位置缓存：entity_id → cell_index
    private readonly Dictionary<int, int> _entityCell = new();

    private GodotObject _gdSpatialGrid;

    public override void _Ready()
    {
        for (int i = 0; i < GridTotal; i++)
            _grid[i] = new List<int>(4);

        _gdSpatialGrid = Engine.GetSingleton("SpatialGrid");
        _gdSpatialGrid.Set("_cs_node", this);

        GD.Print($"[SpatialGridCs] 初始化完成，{GridWidth}×{GridHeight} 格，格子 {CellSize} px");
    }

    // 插入或更新实体位置（由 EnemyManager 每帧调用）
    public void Insert(int entityId, Vector2 pos)
    {
        int cx = Mathf.Clamp((int)(pos.X / CellSize), 0, GridWidth  - 1);
        int cy = Mathf.Clamp((int)(pos.Y / CellSize), 0, GridHeight - 1);
        int cellIdx = cx + cy * GridWidth;

        if (_entityCell.TryGetValue(entityId, out int oldCell))
        {
            if (oldCell == cellIdx) return;  // 未跨格
            _grid[oldCell].Remove(entityId);
            _dirty.Add(oldCell);
        }
        _grid[cellIdx].Add(entityId);
        _entityCell[entityId] = cellIdx;
        _dirty.Add(cellIdx);
    }

    // 重建（仅清空 dirty 集合）
    public void Rebuild() => _dirty.Clear();

    // 圆形查询：返回 center 半径 radius 内的所有 entity_id
    // ADR-L1 规厙1：内层 foreach 内禁止 Call()
    public int[] QueryCircle(Vector2 center, float radius)
    {
        int minCx = Mathf.Clamp((int)((center.X - radius) / CellSize), 0, GridWidth  - 1);
        int maxCx = Mathf.Clamp((int)((center.X + radius) / CellSize), 0, GridWidth  - 1);
        int minCy = Mathf.Clamp((int)((center.Y - radius) / CellSize), 0, GridHeight - 1);
        int maxCy = Mathf.Clamp((int)((center.Y + radius) / CellSize), 0, GridHeight - 1);

        var result = new List<int>(32);
        float radSq = radius * radius;

        for (int cy = minCy; cy <= maxCy; cy++)
        for (int cx = minCx; cx <= maxCx; cx++)
        {
            // ADR-L1 规厙1：内层循环约束—不得调用 GodotObject
            foreach (int id in _grid[cx + cy * GridWidth])
                result.Add(id);
        }
        return result.ToArray();
    }

    // 移除实体
    public void Remove(int entityId)
    {
        if (!_entityCell.TryGetValue(entityId, out int cell)) return;
        _grid[cell].Remove(entityId);
        _entityCell.Remove(entityId);
        _dirty.Add(cell);
    }

    // 清空（关卡重置）
    public void Clear()
    {
        foreach (var cell in _grid) cell.Clear();
        _entityCell.Clear();
        _dirty.Clear();
    }
}
