package com.example.minestom;

import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.player.PlayerBlockInteractEvent;
import net.minestom.server.event.player.PlayerLoginEvent;
import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.Block;
import net.minestom.server.timer.TaskSchedule;
import java.util.HashSet;
import java.util.Set;

public class GameOfLifeServer {
    
    private static final int GRID_SIZE = 50;
    private static final Block ALIVE_BLOCK = Block.WHITE_WOOL;
    private static final Block DEAD_BLOCK = Block.AIR;
    private static final Pos GRID_START = new Pos(0, 40, 0);
    
    private final boolean[][] grid = new boolean[GRID_SIZE][GRID_SIZE];
    private final Set<Player> players = new HashSet<>();
    private InstanceContainer instance;
    private boolean isRunning = false;
    
    public static void main(String[] args) {
        MinecraftServer minecraftServer = MinecraftServer.init();
        GameOfLifeServer server = new GameOfLifeServer();
        
        GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
        globalEventHandler.addListener(PlayerLoginEvent.class, server::onPlayerLogin);
        globalEventHandler.addListener(PlayerSpawnEvent.class, server::onPlayerSpawn);
        globalEventHandler.addListener(PlayerBlockInteractEvent.class, server::onBlockInteract);
        
        minecraftServer.start("0.0.0.0", 25565);
    }
    
    private void onPlayerLogin(PlayerLoginEvent event) {
        Player player = event.getPlayer();
        players.add(player);
        player.sendMessage("§aWelcome to Conway's Game of Life!");
        player.sendMessage("§eRight-click blocks to toggle cells. Type /start to begin simulation.");
    }
    
    private void onPlayerSpawn(PlayerSpawnEvent event) {
        if (instance == null) {
            InstanceManager instanceManager = MinecraftServer.getInstanceManager();
            instance = instanceManager.createInstanceContainer();
            instance.setGenerator(unit -> unit.modifier().fillHeight(0, 40, Block.GRASS_BLOCK));
            initializeGrid();
        }
        event.getPlayer().setInstance(instance, GRID_START.add(0, 5, GRID_SIZE/2));
    }
    
    private void onBlockInteract(PlayerBlockInteractEvent event) {
        if (isRunning) {
            event.getPlayer().sendMessage("§cCannot modify grid while simulation is running!");
            return;
        }
        
        Pos blockPos = event.getBlockPosition();
        int x = blockPos.blockX() - GRID_START.blockX();
        int z = blockPos.blockZ() - GRID_START.blockZ();
        
        if (x >= 0 && x < GRID_SIZE && z >= 0 && z < GRID_SIZE) {
            grid[x][z] = !grid[x][z];
            updateBlock(x, z);
        }
    }
    
    private void initializeGrid() {
        // Initialize with some gliders for demonstration
        // Glider 1
        grid[1][2] = true;
        grid[2][3] = true;
        grid[3][1] = true;
        grid[3][2] = true;
        grid[3][3] = true;
        
        // Glider 2
        grid[10][12] = true;
        grid[11][13] = true;
        grid[12][11] = true;
        grid[12][12] = true;
        grid[12][13] = true;
        
        // Update all blocks
        for (int x = 0; x < GRID_SIZE; x++) {
            for (int z = 0; z < GRID_SIZE; z++) {
                updateBlock(x, z);
            }
        }
    }
    
    private void updateBlock(int x, int z) {
        Pos pos = GRID_START.add(x, 0, z);
        instance.setBlock(pos, grid[x][z] ? ALIVE_BLOCK : DEAD_BLOCK);
    }
    
    public void startSimulation() {
        if (isRunning) return;
        isRunning = true;
        
        MinecraftServer.getSchedulerManager().scheduleTask(() -> {
            if (!isRunning) return;
            
            boolean[][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];
            
            for (int x = 0; x < GRID_SIZE; x++) {
                for (int z = 0; z < GRID_SIZE; z++) {
                    int neighbors = countNeighbors(x, z);
                    
                    if (grid[x][z]) {
                        // Any live cell with fewer than two live neighbors dies (underpopulation)
                        // Any live cell with more than three live neighbors dies (overpopulation)
                        newGrid[x][z] = neighbors == 2 || neighbors == 3;
                    } else {
                        // Any dead cell with exactly three live neighbors becomes a live cell (reproduction)
                        newGrid[x][z] = neighbors == 3;
                    }
                }
            }
            
            // Update grid and blocks
            for (int x = 0; x < GRID_SIZE; x++) {
                for (int z = 0; z < GRID_SIZE; z++) {
                    if (grid[x][z] != newGrid[x][z]) {
                        grid[x][z] = newGrid[x][z];
                        updateBlock(x, z);
                    }
                }
            }
            
            // Broadcast generation count
            players.forEach(p -> p.sendMessage("§7Generation completed"));
        }, TaskSchedule.tick(10), TaskSchedule.tick(10));
    }
    
    public void stopSimulation() {
        isRunning = false;
        players.forEach(p -> p.sendMessage("§aSimulation stopped. You can now modify the grid."));
    }
    
    private int countNeighbors(int x, int z) {
        int count = 0;
        for (int dx = -1; dx <= 1; dx++) {
            for (int dz = -1; dz <= 1; dz++) {
                if (dx == 0 && dz == 0) continue;
                
                int nx = x + dx;
                int nz = z + dz;
                
                if (nx >= 0 && nx < GRID_SIZE && nz >= 0 && nz < GRID_SIZE && grid[nx][nz]) {
                    count++;
                }
            }
        }
        return count;
    }
}