package fr.insa.clavardator.server;

import fr.insa.clavardator.lib.message.RecipientInfo;
import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.lib.network.TcpListener;
import fr.insa.clavardator.lib.users.UserInformation;
import fr.insa.clavardator.lib.util.Log;

import java.io.EOFException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

public class Proxy {
	private final HashMap<String, TcpConnection> users = new HashMap<>();
	private final TcpListener proxyListener;

	public Proxy(int port) {
		proxyListener = new TcpListener();
		proxyListener.acceptConnection(
				port,
				clientSocket -> {

					Log.v(getClass().getSimpleName(), "Accepting a new user");
					TcpConnection connection = new TcpConnection(clientSocket);
					connection.receive(msg -> {
						if (msg instanceof UserInformation) {
							// Send UserInformation to all other connected users
							Log.v(getClass().getSimpleName(), "Received UserInformation: " + msg);
							users.put(((UserInformation) msg).id, connection);
							for (String userId : users.keySet()) {
								UserInformation userInfo = ((UserInformation) msg);
								if (!userId.equals(userInfo.id)) {
									transmitMessage((Serializable) msg, userId);
								}
							}

						} else if (msg instanceof RecipientInfo) {
							// Send other messages only to the recipient
							transmitMessage((Serializable) msg, ((RecipientInfo) msg).getRecipientId());
						} else {
							Log.e(getClass().getSimpleName(), "Unexpected message received: " + msg.toString());
						}

					}, e -> {
						if (!(e instanceof EOFException)) {
							Log.e(getClass().getSimpleName(), "Error while receiving message to transmit", e);
						}
						for (Map.Entry<String, TcpConnection> user : users.entrySet()) {
							if (user.getValue().equals(connection)) {
								Log.v(getClass().getSimpleName(), "Disconnecting user " + user.getKey());
								users.remove(user.getKey());
								break;
							}
						}
					});
				},
				e -> Log.e(getClass().getSimpleName(), "Error while accepting a user", e));
	}


	void transmitMessage(Serializable msg, String recipientId) {
		TcpConnection user = users.get(recipientId);
		if (user == null) {
			Log.e(getClass().getSimpleName(), "Cannot find the recipient in the connected users");
		} else {
			Log.v(getClass().getSimpleName(), "Transmitting message: " + msg);
			user.send(msg, null, e -> Log.e(getClass().getSimpleName(), "Error while sending message", e));
		}
	}

	public void stop() {
		proxyListener.stopAccepting();
		for (TcpConnection connection : users.values()) {
			connection.close();
		}
	}
}
