package com.openosrs.client.core;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;

/**
 * WorldEntities - Manages all entities in the game world.
 * This includes NPCs, players, objects, and ground items.
 */
public class WorldEntities {
    private final ConcurrentMap<Integer, GameNPC> npcs = new ConcurrentHashMap<>();
    private final ConcurrentMap<Integer, OtherPlayer> players = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, GameObject> objects = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, GroundItem> groundItems = new ConcurrentHashMap<>();
    
    private final EventSystem eventSystem;
    
    public WorldEntities(EventSystem eventSystem) {
        this.eventSystem = eventSystem;
    }
    
    // ========== NPC MANAGEMENT ==========
    
    public void addNPC(GameNPC npc) {
        if (npc != null) {
            npcs.put(npc.getIndex(), npc);
            eventSystem.publishEvent(new WorldState.NPCSpawnedEvent(npc));
        }
    }
    
    public void removeNPC(int index) {
        GameNPC removed = npcs.remove(index);
        if (removed != null) {
            eventSystem.publishEvent(new WorldState.NPCDespawnedEvent(removed));
        }
    }
    
    public GameNPC getNPC(int index) {
        return npcs.get(index);
    }
    
    public Collection<GameNPC> getAllNPCs() {
        return npcs.values();
    }
    
    public List<GameNPC> getNPCsInRadius(int centerX, int centerY, int radius) {
        List<GameNPC> result = new ArrayList<>();
        for (GameNPC npc : npcs.values()) {
            int distance = Math.abs(npc.getX() - centerX) + Math.abs(npc.getY() - centerY);
            if (distance <= radius) {
                result.add(npc);
            }
        }
        return result;
    }
    
    // ========== PLAYER MANAGEMENT ==========
    
    public void addPlayer(OtherPlayer player) {
        if (player != null) {
            players.put(player.getIndex(), player);
        }
    }
    
    public void removePlayer(int index) {
        players.remove(index);
    }
    
    public OtherPlayer getPlayer(int index) {
        return players.get(index);
    }
    
    public Collection<OtherPlayer> getAllPlayers() {
        return players.values();
    }
    
    public List<OtherPlayer> getPlayersInRadius(int centerX, int centerY, int radius) {
        List<OtherPlayer> result = new ArrayList<>();
        for (OtherPlayer player : players.values()) {
            int distance = Math.abs(player.getX() - centerX) + Math.abs(player.getY() - centerY);
            if (distance <= radius) {
                result.add(player);
            }
        }
        return result;
    }
    
    // ========== OBJECT MANAGEMENT ==========
    
    public void addObject(GameObject object) {
        if (object != null) {
            String key = objectKey(object.getX(), object.getY(), object.getPlane());
            objects.put(key, object);
            eventSystem.publishEvent(new WorldState.ObjectSpawnedEvent(object));
        }
    }
    
    public void removeObject(int x, int y, int plane) {
        String key = objectKey(x, y, plane);
        GameObject removed = objects.remove(key);
        if (removed != null) {
            eventSystem.publishEvent(new WorldState.ObjectDespawnedEvent(removed));
        }
    }
    
    public GameObject getObject(int x, int y, int plane) {
        String key = objectKey(x, y, plane);
        return objects.get(key);
    }
    
    public Collection<GameObject> getAllObjects() {
        return objects.values();
    }
    
    // ========== GROUND ITEM MANAGEMENT ==========
    
    public void addGroundItem(GroundItem item) {
        if (item != null) {
            String key = groundItemKey(item.getX(), item.getY(), item.getPlane(), item.getId());
            groundItems.put(key, item);
            eventSystem.publishEvent(new WorldState.ItemSpawnedEvent(item));
        }
    }
    
    public void removeGroundItem(int x, int y, int plane, int itemId) {
        String key = groundItemKey(x, y, plane, itemId);
        GroundItem removed = groundItems.remove(key);
        if (removed != null) {
            eventSystem.publishEvent(new WorldState.ItemDespawnedEvent(removed));
        }
    }
    
    public GroundItem getGroundItem(int x, int y, int plane, int itemId) {
        String key = groundItemKey(x, y, plane, itemId);
        return groundItems.get(key);
    }
    
    public Collection<GroundItem> getAllGroundItems() {
        return groundItems.values();
    }
    
    public List<GroundItem> getGroundItemsAt(int x, int y, int plane) {
        List<GroundItem> result = new ArrayList<>();
        for (GroundItem item : groundItems.values()) {
            if (item.getX() == x && item.getY() == y && item.getPlane() == plane) {
                result.add(item);
            }
        }
        return result;
    }
    
    // ========== UTILITY METHODS ==========
    
    public void tick() {
        // Update all entities
        for (GameNPC npc : npcs.values()) {
            npc.tick();
        }
        
        for (OtherPlayer player : players.values()) {
            player.tick();
        }
        
        // Remove expired ground items
        groundItems.entrySet().removeIf(entry -> {
            GroundItem item = entry.getValue();
            if (item.isExpired()) {
                eventSystem.publishEvent(new WorldState.ItemDespawnedEvent(item));
                return true;
            }
            return false;
        });
    }
    
    public void clear() {
        npcs.clear();
        players.clear();
        objects.clear();
        groundItems.clear();
    }
    
    public int getNPCCount() { return npcs.size(); }
    public int getPlayerCount() { return players.size(); }
    public int getObjectCount() { return objects.size(); }
    public int getGroundItemCount() { return groundItems.size(); }
    
    private String objectKey(int x, int y, int plane) {
        return x + "," + y + "," + plane;
    }
    
    private String groundItemKey(int x, int y, int plane, int itemId) {
        return x + "," + y + "," + plane + "," + itemId;
    }
}