<commit_before>﻿using System;
using System.Linq;
using OQF.AnalysisAndProgress.ProgressUtils;
using OQF.Bot.Contracts.GameElements;
using OQF.Net.LanMessaging.AddressTypes;
using OQF.Net.LanMessaging.NetworkMessageBase;
using OQF.Net.LanMessaging.NetworkMessages.Notifications;
using OQF.Net.LanMessaging.NetworkMessages.RequestsAndResponses;
using OQF.Net.LanMessaging.Types;
using OQF.Net.LanServer.Contracts;
using OQF.Net.LanServer.NetworkGameLogic.Messaging;
using OQF.Utils.Enum;

namespace OQF.Net.LanServer.NetworkGameLogic.GameServer
{
	public class NetworkGameServer : INetworkGameServer
	{
		public event Action<string> NewOutputAvailable;

		private IServerMessaging messagingService;

		private readonly IClientRepository clientRepository;
		private readonly IGameRepository gameRepository;

		public NetworkGameServer(IClientRepository clientRepository,
								 IGameRepository gameRepository)
		{
			this.clientRepository = clientRepository;
			this.gameRepository = gameRepository;

			gameRepository.RepositoryChanged += OnRepositoryChanged;
		}

		private void OnRepositoryChanged()
		{
			SendGameListUpdateToAllClients();
		}


		public void Activate(Address serverAddress)
		{
			if (messagingService != null)
			{
				Deactivate();	
			}

			clientRepository.ClearRepository();
			gameRepository.ClearRepository();

			messagingService = new ServerMessaging(serverAddress);

			messagingService.NewIncomingMessage += OnNewIncomingMessage;

			NewOutputAvailable?.Invoke("ActivatedServer");
		}

		private void OnNewIncomingMessage(NetworkMessageBase newIncommingMsg)
		{
			switch (newIncommingMsg.Type)
			{
				case NetworkMessageType.ConnectToServerRequest:
				{
					var msg = (ConnectToServerRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< ConnectToServer from ({msg.PlayerName}|{msg.ClientId})");

					if (!clientRepository.IsClientIdRegistered(msg.ClientId))
					{
						clientRepository.AddClient(msg.ClientId, msg.PlayerName);

						NewOutputAvailable?.Invoke(">>> ConnectToServerResponse");

						messagingService.SendMessage(new ConnectToServerResponse(msg.ClientId));
						SendGameListUpdate(msg.ClientId);
					}
					else
					{
						NewOutputAvailable?.Invoke(">>> ConnectToServerResponse ERROR");
						// ERROR !!!!
					}

					break;
				}	
				case NetworkMessageType.CreateGameRequest:
				{					
					var msg = (CreateGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< CreateGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					gameRepository.CreateGame(msg.GameId, 
											  clientRepository.GetClientById(msg.ClientId), 
											  msg.GameName);					

					break;
				}
				case NetworkMessageType.JoinGameRequest:
				{
					var msg = (JoinGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< JoinGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || game.IsGameActive)
					{
						NewOutputAvailable?.Invoke($">>> JoinGameResponse (negative) to {clientRepository.GetClientById(msg.ClientId).PlayerName}");
						messagingService.SendMessage(new JoinGameResponse(msg.ClientId, msg.GameId, false, ""));
					}
					else
					{
						NewOutputAvailable?.Invoke($">>> JoinGameResponse (positive) to {clientRepository.GetClientById(msg.ClientId).PlayerName}");

						messagingService.SendMessage(new JoinGameResponse(msg.ClientId, 
																		  game.GameId, 
																		  true, 
																		  game.GameInitiator.PlayerName));

						NewOutputAvailable?.Invoke($">>> Opend Game is starting for {game.GameInitiator.PlayerName}");

						messagingService.SendMessage(new OpendGameIsStarting(game.GameInitiator.ClientId, 
																			 game.GameId, 
																			 clientRepository.GetClientById(msg.ClientId).PlayerName));

						game.NewBoardStateAvailable += OnNewBoardStateAvailable;
						game.WinnerAvailable        += OnWinnerAvailable;

						game.StartGame(clientRepository.GetClientById(msg.ClientId));
					}

					break;
				}
				case NetworkMessageType.NextMoveSubmission:
				{
					var msg = (NextMoveSubmission) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< NextMoveSubmission from {clientRepository.GetClientById(msg.ClientId).PlayerName} [{msg.NextMove}]");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || !game.IsGameActive)
					{
						NewOutputAvailable?.Invoke(">>> NextMoveSubmission ERROR!");
					}
					else
					{
						game.ReportMove(clientRepository.GetClientById(msg.ClientId), msg.NextMove);
					}

					break;
				}
				case NetworkMessageType.LeaveGameRequest:
				{
					var msg = (LeaveGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< LeaveGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || !game.IsGameActive)
					{
						NewOutputAvailable?.Invoke(">>> NextMoveSubmission ERROR!");
					}
					else
					{
						LeaveGame(msg.ClientId, game);
					}

					break;
				}
				case NetworkMessageType.CancelCreatedGameRequest:
				{
					var msg = (CancelCreatedGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< CancelGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game.IsGameActive)
					{
						messagingService.SendMessage(new CancelCreatedGameResponse(msg.ClientId, false));
					}
					else
					{
						gameRepository.DeleteGame(msg.GameId);
						messagingService.SendMessage(new CancelCreatedGameResponse(msg.ClientId, true));
					}

					break;
				}
				case NetworkMessageType.ClientDisconnect:
				{
					NewOutputAvailable?.Invoke($"<<< Disconnect from {clientRepository.GetClientById(newIncommingMsg.ClientId)?.PlayerName}");

					DisconnectClient(newIncommingMsg.ClientId);

					break;
				}
			}			
		}

		private void DisconnectClient(ClientId clientId)
		{
			var game = gameRepository.GetGameByPlayer(clientId);

			if (game != null)
			{
				if (game.IsGameActive)
					LeaveGame(clientId, game);
				else
					gameRepository.DeleteGame(game.GameId);
			}

			clientRepository.RemoveClient(clientId);
		}

		private void LeaveGame(ClientId clientId, INetworkGame game)
		{
			game.NewBoardStateAvailable -= OnNewBoardStateAvailable;
			game.WinnerAvailable        -= OnWinnerAvailable;

			NewOutputAvailable?.Invoke($">>> GameOver notification for ({game.GameName})");

			messagingService.SendMessage(new GameOverNotification(clientId == game.GameInitiator.ClientId
																					? game.Opponend.ClientId
																					: game.GameInitiator.ClientId,
																  true,
																  WinningReason.Capitulation));


			// TODO store game

			gameRepository.DeleteGame(game.GameId);
		}

		private void OnWinnerAvailable(INetworkGame networkGame, ClientInfo clientInfo, WinningReason winningReason)
		{
			networkGame.NewBoardStateAvailable -= OnNewBoardStateAvailable;
			networkGame.WinnerAvailable        -= OnWinnerAvailable;

			NewOutputAvailable?.Invoke($">>> GameOver notification for ({networkGame.GameName})");


			messagingService.SendMessage(new GameOverNotification(networkGame.GameInitiator.ClientId, <commit_msg>Network.LanServer: slight improvement<commit_after>﻿using System;
using System.Linq;
using OQF.AnalysisAndProgress.ProgressUtils;
using OQF.Bot.Contracts.GameElements;
using OQF.Net.LanMessaging.AddressTypes;
using OQF.Net.LanMessaging.NetworkMessageBase;
using OQF.Net.LanMessaging.NetworkMessages.Notifications;
using OQF.Net.LanMessaging.NetworkMessages.RequestsAndResponses;
using OQF.Net.LanMessaging.Types;
using OQF.Net.LanServer.Contracts;
using OQF.Net.LanServer.NetworkGameLogic.Messaging;
using OQF.Utils.Enum;

namespace OQF.Net.LanServer.NetworkGameLogic.GameServer
{
	public class NetworkGameServer : INetworkGameServer
	{
		public event Action<string> NewOutputAvailable;

		private IServerMessaging messagingService;

		private readonly IClientRepository clientRepository;
		private readonly IGameRepository gameRepository;

		public NetworkGameServer(IClientRepository clientRepository,
								 IGameRepository gameRepository)
		{
			this.clientRepository = clientRepository;
			this.gameRepository = gameRepository;

			gameRepository.RepositoryChanged += OnRepositoryChanged;
		}

		private void OnRepositoryChanged()
		{
			SendGameListUpdateToAllClients();
		}


		public void Activate(Address serverAddress)
		{
			if (messagingService != null)
			{
				Deactivate();	
			}

			clientRepository.ClearRepository();
			gameRepository.ClearRepository();

			messagingService = new ServerMessaging(serverAddress);

			messagingService.NewIncomingMessage += OnNewIncomingMessage;

			NewOutputAvailable?.Invoke("ActivatedServer");
		}

		private void OnNewIncomingMessage(NetworkMessageBase newIncommingMsg)
		{
			switch (newIncommingMsg.Type)
			{
				case NetworkMessageType.ConnectToServerRequest:
				{
					var msg = (ConnectToServerRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< ConnectToServer from ({msg.PlayerName}|{msg.ClientId})");

					if (!clientRepository.IsClientIdRegistered(msg.ClientId))
					{
						clientRepository.AddClient(msg.ClientId, msg.PlayerName);

						NewOutputAvailable?.Invoke(">>> ConnectToServerResponse");

						messagingService.SendMessage(new ConnectToServerResponse(msg.ClientId));
						SendGameListUpdate(msg.ClientId);
					}
					else
					{
						NewOutputAvailable?.Invoke(">>> ConnectToServerResponse ERROR");
						// ERROR !!!!
					}

					break;
				}	
				case NetworkMessageType.CreateGameRequest:
				{					
					var msg = (CreateGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< CreateGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					gameRepository.CreateGame(msg.GameId, 
											  clientRepository.GetClientById(msg.ClientId), 
											  msg.GameName);					

					break;
				}
				case NetworkMessageType.JoinGameRequest:
				{
					var msg = (JoinGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< JoinGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || game.IsGameActive)
					{
						NewOutputAvailable?.Invoke($">>> JoinGameResponse (negative) to {clientRepository.GetClientById(msg.ClientId).PlayerName}");
						messagingService.SendMessage(new JoinGameResponse(msg.ClientId, msg.GameId, false, ""));
					}
					else
					{
						NewOutputAvailable?.Invoke($">>> JoinGameResponse (positive) to {clientRepository.GetClientById(msg.ClientId).PlayerName}");

						messagingService.SendMessage(new JoinGameResponse(msg.ClientId, 
																		  game.GameId, 
																		  true, 
																		  game.GameInitiator.PlayerName));

						NewOutputAvailable?.Invoke($">>> Opend Game is starting for {game.GameInitiator.PlayerName}");

						messagingService.SendMessage(new OpendGameIsStarting(game.GameInitiator.ClientId, 
																			 game.GameId, 
																			 clientRepository.GetClientById(msg.ClientId).PlayerName));

						game.NewBoardStateAvailable += OnNewBoardStateAvailable;
						game.WinnerAvailable        += OnWinnerAvailable;

						game.StartGame(clientRepository.GetClientById(msg.ClientId));
					}

					break;
				}
				case NetworkMessageType.NextMoveSubmission:
				{
					var msg = (NextMoveSubmission) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< NextMoveSubmission from {clientRepository.GetClientById(msg.ClientId).PlayerName} [{msg.NextMove}]");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || !game.IsGameActive)
					{
						NewOutputAvailable?.Invoke(">>> NextMoveSubmission ERROR!");
					}
					else
					{
						game.ReportMove(clientRepository.GetClientById(msg.ClientId), msg.NextMove);
					}

					break;
				}
				case NetworkMessageType.LeaveGameRequest:
				{
					var msg = (LeaveGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< LeaveGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game == null || !game.IsGameActive)
					{
						NewOutputAvailable?.Invoke(">>> NextMoveSubmission ERROR!");
					}
					else
					{
						LeaveGame(msg.ClientId, game);
					}

					break;
				}
				case NetworkMessageType.CancelCreatedGameRequest:
				{
					var msg = (CancelCreatedGameRequest) newIncommingMsg;

					NewOutputAvailable?.Invoke($"<<< CancelGameRequest from {clientRepository.GetClientById(msg.ClientId).PlayerName}");

					var game = gameRepository.GetGameById(msg.GameId);

					if (game.IsGameActive)
					{
						messagingService.SendMessage(new CancelCreatedGameResponse(msg.ClientId, false));
					}
					else
					{
						gameRepository.DeleteGame(msg.GameId);
						messagingService.SendMessage(new CancelCreatedGameResponse(msg.ClientId, true));
					}

					break;
				}
				case NetworkMessageType.ClientDisconnect:
				{
					NewOutputAvailable?.Invoke($"<<< Disconnect from {clientRepository.GetClientById(newIncommingMsg.ClientId)?.PlayerName}");

					DisconnectClient(newIncommingMsg.ClientId);

					break;
				}
			}			
		}

		private void DisconnectClient(ClientId clientId)
		{
			var game = gameRepository.GetGameByPlayer(clientId);
			clientRepository.RemoveClient(clientId);

			if (game != null)
			{
				if (game.IsGameActive)
					LeaveGame(clientId, game);
				else
					gameRepository.DeleteGame(game.GameId);
			}			
		}

		private void LeaveGame(ClientId clientId, INetworkGame game)
		{
			game.NewBoardStateAvailable -= OnNewBoardStateAvailable;
			game.WinnerAvailable        -= OnWinnerAvailable;

			NewOutputAvailable?.Invoke($">>> GameOver notification for ({game.GameName})");

			messagingService.SendMessage(new GameOverNotification(clientId == game.GameInitiator.ClientId
																					? game.Opponend.ClientId
																					: game.GameInitiator.ClientId,
																  true,
																  WinningReason.Capitulation));


			// TODO store game

			gameRepository.DeleteGame(game.GameId);
		}

		private void OnWinnerAvailable(INetworkGame networkGame, ClientInfo clientInfo, WinningReason winningReason)
		{
			networkGame.NewBoardStateAvailable -= OnNewBoardStateAvailable;
			networkGame.WinnerAvailable        -= OnWinnerAvailable;

			NewOutputAvailable?.Invoke($">>> GameOver notification for ({networkGame.GameName})");


			messagingService.SendMessage(new GameOverNotification(networkGame.GameInitiator.ClientId, 
{'fraction_non_alphanumeric': 0.10158257811443257, 'fraction_numerical': 0.0, 'mean_word_length': 3.709235668789809, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 59, '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': '5400301', 'n_tokens_mistral': 5637, 'n_tokens_neox': 4207, 'n_words': 638}