package com.zombie.game.model;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zombie.game.ecs.components.BlocksMovement;
import com.zombie.game.ecs.components.MovementCost;
import lombok.Getter;

import java.io.File;
import java.util.*;

/** Game map: spatial index for wall entities, flow field, and spawn points / 游戏地图：墙体实体空间索引、流场和出生点 */
@Getter
public class GameMap {
    private final int[][] cells;
    private final int width;
    private final int height;
    /** Spatial index: "x,y" -> wall entity ID / 空间索引："x,y" -> 墙体实体ID */
    private final Map<String, Integer> wallGrid = new HashMap<>();
    private final List<int[]> spawnPoints;
    /** Raw static wall positions loaded from JSON / 从JSON加载的静态墙位置 */
    private final List<int[]> staticWallData = new ArrayList<>();
    /** Raw nut wall positions loaded from JSON / 从JSON加载的坚果墙位置 */
    private final List<int[]> nutWallData = new ArrayList<>();
    /** Flow field for zombie pathfinding / 僵尸寻路流场 */
    private final FlowField flowField;

    // References to ECS component maps, set by ECSWorld after construction
    private Map<Integer, BlocksMovement> blocksMovements;
    private Map<Integer, MovementCost> movementCosts;

    public GameMap(String mapFilePath) {
        JsonNode root = loadMapFile(mapFilePath);
        this.width = root.get("width").asInt();
        this.height = root.get("height").asInt();
        this.cells = new int[height][width];
        this.spawnPoints = new ArrayList<>();
        this.flowField = new FlowField(width, height);
        loadFromJson(root);
    }

    /** Set ECS component map references (called by ECSWorld) */
    public void setComponentMaps(Map<Integer, BlocksMovement> bm, Map<Integer, MovementCost> mc) {
        this.blocksMovements = bm;
        this.movementCosts = mc;
    }

    /** Register a wall entity in the spatial index */
    public void registerWall(int gx, int gy, int entityId) {
        wallGrid.put(key(gx, gy), entityId);
    }

    /** Unregister a wall entity and invalidate flow field */
    public void unregisterWall(int gx, int gy) {
        wallGrid.remove(key(gx, gy));
        flowField.invalidate();
    }

    /** Check if grid cell blocks movement / 检查网格格子是否阻挡移动 */
    public boolean isWall(int gx, int gy) {
        if (gx < 0 || gx >= width || gy < 0 || gy >= height) return true;
        Integer entityId = wallGrid.get(key(gx, gy));
        if (entityId == null) return false;
        return blocksMovements != null && blocksMovements.containsKey(entityId);
    }

    /** Check if world-space area is walkable / 检查世界坐标区域是否可行走 */
    public boolean isWalkable(float wx, float wy, float size) {
        float half = size / 2;
        float[][] corners = {
            {wx - half, wy - half}, {wx + half, wy - half},
            {wx - half, wy + half}, {wx + half, wy + half}
        };
        for (float[] c : corners) {
            int gx = (int) Math.floor(c[0]);
            int gy = (int) Math.floor(c[1]);
            if (isWall(gx, gy)) return false;
        }
        return true;
    }

    /** Update flow field with current player positions */
    public void updateFlowField(List<float[]> playerPositions) {
        flowField.update(wallGrid, blocksMovements, movementCosts, playerPositions);
    }

    /** Get flow direction at world position */
    public float[] getFlowDirection(float wx, float wy) {
        return flowField.getDirection(wx, wy);
    }

    /** Check if flow field is valid */
    public boolean isFlowFieldValid() {
        return flowField.isValid();
    }

    /** Get cell grid with wall info (for sync) / 获取包含墙壁信息的格子网格 */
    public int[][] getCells() {
        int[][] result = new int[height][width];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                result[y][x] = cells[y][x];
            }
        }
        // Mark wall cells from spatial index
        for (Map.Entry<String, Integer> entry : wallGrid.entrySet()) {
            String[] parts = entry.getKey().split(",");
            int x = Integer.parseInt(parts[0]);
            int y = Integer.parseInt(parts[1]);
            if (x >= 0 && x < width && y >= 0 && y < height) {
                Integer entityId = entry.getValue();
                if (blocksMovements != null && blocksMovements.containsKey(entityId)) {
                    // Check if it's a nut wall (has MovementCost) or static wall
                    if (movementCosts != null && movementCosts.containsKey(entityId)) {
                        result[y][x] = 4; // nut wall
                    } else {
                        result[y][x] = 1; // static wall
                    }
                }
            }
        }
        return result;
    }

    /** Get player spawn points */
    public List<int[]> getSpawnPoints() {
        return spawnPoints;
    }

    // --- Private ---

    private JsonNode loadMapFile(String mapFilePath) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.readTree(new File(mapFilePath));
        } catch (Exception e) {
            throw new RuntimeException("Failed to load map file: " + mapFilePath, e);
        }
    }

    private void loadFromJson(JsonNode root) {
        JsonNode wallsNode = root.get("walls");
        if (wallsNode != null && wallsNode.isArray()) {
            for (JsonNode wallNode : wallsNode) {
                int x = (int) wallNode.get("x").asDouble();
                int y = (int) wallNode.get("y").asDouble();
                String type = wallNode.get("type").asText();
                if (x < 0 || x >= width || y < 0 || y >= height) continue;
                if ("static".equals(type)) {
                    staticWallData.add(new int[]{x, y});
                } else if ("nut".equals(type)) {
                    nutWallData.add(new int[]{x, y});
                }
            }
        }

        JsonNode playerSpawns = root.get("playerSpawns");
        if (playerSpawns != null && playerSpawns.isArray()) {
            for (JsonNode spawn : playerSpawns) {
                int x = spawn.get("x").asInt();
                int y = spawn.get("y").asInt();
                if (x >= 0 && x < width && y >= 0 && y < height) {
                    cells[y][x] = 2;
                    spawnPoints.add(new int[]{x, y});
                }
            }
        }

        JsonNode zombieSpawns = root.get("zombieSpawns");
        if (zombieSpawns != null && zombieSpawns.isArray()) {
            for (JsonNode spawn : zombieSpawns) {
                int x = spawn.get("x").asInt();
                int y = spawn.get("y").asInt();
                if (x >= 0 && x < width && y >= 0 && y < height) {
                    cells[y][x] = 3;
                }
            }
        }
    }

    /** Get zombie spawn points from cells / 从格子中获取僵尸出生点 */
    public List<int[]> getZombieSpawnPoints() {
        List<int[]> points = new ArrayList<>();
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                if (cells[y][x] == 3) {
                    points.add(new int[]{x, y});
                }
            }
        }
        return points;
    }

    private static String key(int x, int y) {
        return x + "," + y;
    }
}
