<commit_before>package com.cardshifter.server.model;

import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.cardshifter.ai.FakeAIClientTCG;
import com.cardshifter.api.CardshifterConstants;
import com.cardshifter.api.ClientIO;
import com.cardshifter.api.both.ChatMessage;
import com.cardshifter.api.incoming.LoginMessage;
import com.cardshifter.api.incoming.StartGameRequest;
import com.cardshifter.core.game.ModCollection;
import com.cardshifter.core.game.ServerGame;
import com.cardshifter.core.game.TCGGame;
import com.cardshifter.server.commands.AICommand;
import com.cardshifter.server.commands.AICommand.AICommandParameters;
import com.cardshifter.server.commands.CommandContext;
import com.cardshifter.server.commands.EntityCommand;
import com.cardshifter.server.commands.EntityCommand.EntityInspectParameters;
import com.cardshifter.server.commands.HelpCommand;
import com.cardshifter.server.commands.HelpCommand.HelpParameters;
import com.cardshifter.server.commands.ReplayAllCommand;
import com.cardshifter.server.commands.ReplayAllCommand.ReplayAllParameters;
import com.cardshifter.server.commands.ReplayCommand;
import com.cardshifter.server.commands.ReplayCommand.ReplayParameters;
import com.cardshifter.server.utils.export.DataExporter;

/**
 *Starts the Server object, sets up the AIs and GameFactories, and controls other functions of Server
 * 
 * @author <NAME>
 */
public class MainServer {
	
	private static final Logger logger = LogManager.getLogger(MainServer.class);
	
	/**
	 * Server handles incoming messages and passes them to appropriate methods
	 */
	private final Server server = new Server();
	private final ModCollection mods = new ModCollection();

	private Thread consoleThread;
	
	/**
	 * Adds connections, AIs, and GameFactories to the Server, and starts the ServerConsole.
	 * CommandHandler is a reference to the CommandHandler in Server
	 * 
	 * @return The configured Server object
	 */
	public Server start() {
		
		try {
			logger.info("Starting Server...");
			
			server.addConnections(new ServerSock(server, 4242));
			server.addConnections(new ServerWeb(server, 4243));
			
			logger.info("Starting Console...");
			CommandHandler commandHandler = server.getCommandHandler();
			initializeCommands(commandHandler);
			ServerConsole console = new ServerConsole(server, commandHandler);
			consoleThread = new Thread(console, "Console-Thread");
			consoleThread.start();
			
			mods.getAIs().entrySet().forEach(entry -> {
				ClientIO tcgAI = new FakeAIClientTCG(server, entry.getValue());
				server.newClient(tcgAI);
				server.getIncomingHandler().perform(new LoginMessage("AI " + entry.getKey()), tcgAI);
			});
			final Supplier<ScheduledExecutorService> aiExecutor = () -> server.getScheduler();
			
			mods.getMods().forEach((name, mod) -> server.addGameFactory(name, (serv, id) -> new TCGGame(aiExecutor, id, mod)));
			
			logger.info("Started");
		}
		catch (Exception e) {
			logger.error("Initializing Error", e);
		}
		return server;
	}
	
	/**
	 * Adds specific commands to the CommandHandler such as "exit" and "chat" and attaches them to various methods
	 * 
	 * @param commandHandler The command handler that commands will be added to
	 */
	private void initializeCommands(CommandHandler commandHandler) {
		commandHandler.addHandler("exit", () -> new Object(), this::shutdown);
		commandHandler.addHandler("help", () -> new HelpParameters(), new HelpCommand(commandHandler));
		commandHandler.addHandler("export", this::export);
		commandHandler.addHandler("users", this::users);
		commandHandler.addHandler("play", this::play);
		commandHandler.addHandler("say", this::say);
		commandHandler.addHandler("chat", this::chatInfo);
		commandHandler.addHandler("games", this::showGames);
		commandHandler.addHandler("invites", this::showInvites);
		commandHandler.addHandler("ai", () -> new AICommandParameters(), new AICommand());
		commandHandler.addHandler("ent", () -> new EntityInspectParameters(), new EntityCommand());
		commandHandler.addHandler("threads", cmd -> showAllStackTraces(server, System.out::println));
		commandHandler.addHandler("replay", () -> new ReplayParameters(), new ReplayCommand());
		commandHandler.addHandler("allreplays", () -> new ReplayAllParameters(), new ReplayAllCommand());
	}
	
	/**
	 * Prints out the game invites of the Server
	 * 
	 * @param command The command object
	 */
	private void showInvites(Command command) {
		CommandContext context = new CommandContext(server, command, command.getSender());
		for (Entry<Integer, GameInvite> ee : server.getInvites().all().entrySet()) {
			context.sendChatResponse(ee.getKey() + " = " + ee.getValue());
		}
	}
	
	/**
	 * Prints out the current games of the Server
	 * 
	 * @param command The command object
	 */
	private void showGames(Command command) {
		CommandContext context = new CommandContext(server, command, command.getSender());
		for (Entry<Integer, ServerGame> ee : server.getGames().entrySet()) {
			context.sendChatResponse(ee.getKey() + " = " + ee.getValue());
		}
	}
	
	/**
	 * Sends a chat message to the master chat of the Server
	 * 
	 * @param command The command object
	 */
	private void say(Command command) {
		ChatArea chat = server.getMainChat();
		chat.broadcast(new ChatMessage(chat.getId(), "Server", command.getFullCommand(1)));
	}
	
	/**
	 * Either prints out all of the chats, or the users currently in the master chat
	 * 
	 * @param command The command object
	 */
	private void chatInfo(Command command) {
		int chatId = command.getParameterInt(1);
		CommandContext context = new CommandContext(server, command, command.getSender());
		if (chatId == 0) {
			context.sendChatResponse(server.getChats().keySet().toString());
		}
		else {
			ChatArea chat = server.getChats().get(chatId);
			context.sendChatResponse(chat.getUsers().toString());
		}
	}
	
	/**
	 * Stops the Server and consoleThread, and shows all stack traces for Server
	 * 
	 * @param command A command object with the Server and ClientIO also (unused)
	 * @param parameters Unused parameter
	 */
	private void shutdown(CommandContext command, Object parameters) {
		server.stop();
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			System.out.println("Interrupted when shutting down");
		}
		
		showAllStackTraces(server, System.out::println);
		consoleThread.interrupt();
	}
	
	/**
	 * Print out the clients<commit_msg>Extracted shutdown method on `MainServer` and made it public<commit_after>package com.cardshifter.server.model;

import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.cardshifter.ai.FakeAIClientTCG;
import com.cardshifter.api.CardshifterConstants;
import com.cardshifter.api.ClientIO;
import com.cardshifter.api.both.ChatMessage;
import com.cardshifter.api.incoming.LoginMessage;
import com.cardshifter.api.incoming.StartGameRequest;
import com.cardshifter.core.game.ModCollection;
import com.cardshifter.core.game.ServerGame;
import com.cardshifter.core.game.TCGGame;
import com.cardshifter.server.commands.AICommand;
import com.cardshifter.server.commands.AICommand.AICommandParameters;
import com.cardshifter.server.commands.CommandContext;
import com.cardshifter.server.commands.EntityCommand;
import com.cardshifter.server.commands.EntityCommand.EntityInspectParameters;
import com.cardshifter.server.commands.HelpCommand;
import com.cardshifter.server.commands.HelpCommand.HelpParameters;
import com.cardshifter.server.commands.ReplayAllCommand;
import com.cardshifter.server.commands.ReplayAllCommand.ReplayAllParameters;
import com.cardshifter.server.commands.ReplayCommand;
import com.cardshifter.server.commands.ReplayCommand.ReplayParameters;
import com.cardshifter.server.utils.export.DataExporter;

/**
 *Starts the Server object, sets up the AIs and GameFactories, and controls other functions of Server
 * 
 * @author <NAME>
 */
public class MainServer {
	
	private static final Logger logger = LogManager.getLogger(MainServer.class);
	
	/**
	 * Server handles incoming messages and passes them to appropriate methods
	 */
	private final Server server = new Server();
	private final ModCollection mods = new ModCollection();

	private Thread consoleThread;
	
	/**
	 * Adds connections, AIs, and GameFactories to the Server, and starts the ServerConsole.
	 * CommandHandler is a reference to the CommandHandler in Server
	 * 
	 * @return The configured Server object
	 */
	public Server start() {
		
		try {
			logger.info("Starting Server...");
			
			server.addConnections(new ServerSock(server, 4242));
			server.addConnections(new ServerWeb(server, 4243));
			
			logger.info("Starting Console...");
			CommandHandler commandHandler = server.getCommandHandler();
			initializeCommands(commandHandler);
			ServerConsole console = new ServerConsole(server, commandHandler);
			consoleThread = new Thread(console, "Console-Thread");
			consoleThread.start();
			
			mods.getAIs().entrySet().forEach(entry -> {
				ClientIO tcgAI = new FakeAIClientTCG(server, entry.getValue());
				server.newClient(tcgAI);
				server.getIncomingHandler().perform(new LoginMessage("AI " + entry.getKey()), tcgAI);
			});
			final Supplier<ScheduledExecutorService> aiExecutor = () -> server.getScheduler();
			
			mods.getMods().forEach((name, mod) -> server.addGameFactory(name, (serv, id) -> new TCGGame(aiExecutor, id, mod)));
			
			logger.info("Started");
		}
		catch (Exception e) {
			logger.error("Initializing Error", e);
		}
		return server;
	}
	
	/**
	 * Adds specific commands to the CommandHandler such as "exit" and "chat" and attaches them to various methods
	 * 
	 * @param commandHandler The command handler that commands will be added to
	 */
	private void initializeCommands(CommandHandler commandHandler) {
		commandHandler.addHandler("exit", () -> new Object(), this::shutdown);
		commandHandler.addHandler("help", () -> new HelpParameters(), new HelpCommand(commandHandler));
		commandHandler.addHandler("export", this::export);
		commandHandler.addHandler("users", this::users);
		commandHandler.addHandler("play", this::play);
		commandHandler.addHandler("say", this::say);
		commandHandler.addHandler("chat", this::chatInfo);
		commandHandler.addHandler("games", this::showGames);
		commandHandler.addHandler("invites", this::showInvites);
		commandHandler.addHandler("ai", () -> new AICommandParameters(), new AICommand());
		commandHandler.addHandler("ent", () -> new EntityInspectParameters(), new EntityCommand());
		commandHandler.addHandler("threads", cmd -> showAllStackTraces(server, System.out::println));
		commandHandler.addHandler("replay", () -> new ReplayParameters(), new ReplayCommand());
		commandHandler.addHandler("allreplays", () -> new ReplayAllParameters(), new ReplayAllCommand());
	}
	
	/**
	 * Prints out the game invites of the Server
	 * 
	 * @param command The command object
	 */
	private void showInvites(Command command) {
		CommandContext context = new CommandContext(server, command, command.getSender());
		for (Entry<Integer, GameInvite> ee : server.getInvites().all().entrySet()) {
			context.sendChatResponse(ee.getKey() + " = " + ee.getValue());
		}
	}
	
	/**
	 * Prints out the current games of the Server
	 * 
	 * @param command The command object
	 */
	private void showGames(Command command) {
		CommandContext context = new CommandContext(server, command, command.getSender());
		for (Entry<Integer, ServerGame> ee : server.getGames().entrySet()) {
			context.sendChatResponse(ee.getKey() + " = " + ee.getValue());
		}
	}
	
	/**
	 * Sends a chat message to the master chat of the Server
	 * 
	 * @param command The command object
	 */
	private void say(Command command) {
		ChatArea chat = server.getMainChat();
		chat.broadcast(new ChatMessage(chat.getId(), "Server", command.getFullCommand(1)));
	}
	
	/**
	 * Either prints out all of the chats, or the users currently in the master chat
	 * 
	 * @param command The command object
	 */
	private void chatInfo(Command command) {
		int chatId = command.getParameterInt(1);
		CommandContext context = new CommandContext(server, command, command.getSender());
		if (chatId == 0) {
			context.sendChatResponse(server.getChats().keySet().toString());
		}
		else {
			ChatArea chat = server.getChats().get(chatId);
			context.sendChatResponse(chat.getUsers().toString());
		}
	}
	
	/**
	 * Stops the Server and consoleThread, and shows all stack traces for Server
	 * 
	 * @param command A command object with the Server and ClientIO also (unused)
	 * @param parameters Unused parameter
	 */
	private void shutdown(CommandContext command, Object parameters) {
		shutdown();
	}
	
	/**
	 * Shutdown everything
	 */
	public void shutdown() {
		server.stop();
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			System.out.println("Interrupted when shutting down");
		}
		
		showAllStackTraces(server, System.out::println);
		consoleThread.interrupt();
	}
	
	/**
	 * Print out the clients
{'fraction_non_alphanumeric': 0.12160132191677933, 'fraction_numerical': 0.0025537028691602825, 'mean_word_length': 4.883782589482987, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6274955', 'n_tokens_mistral': 4181, 'n_tokens_neox': 3781, 'n_words': 925}