package fr.insa.clavardator.client.server.proxy;

import fr.insa.clavardator.client.users.CurrentUser;
import fr.insa.clavardator.lib.message.SenderInfo;
import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.lib.users.UserInformation;
import fr.insa.clavardator.lib.util.ErrorCallback;
import fr.insa.clavardator.lib.util.Log;
import fr.insa.clavardator.lib.util.SimpleCallback;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

public class InsaProxy implements Proxy {
	private final String path;
	private final int proxyPort;
	TcpConnection proxyConnection;
	Map<String, TcpConnection.MessageReceivedCallback> callbackMap = new HashMap<>();
	private boolean receiving = false;

	public InsaProxy(String path, int proxyPort) {
		this.path = path;
		this.proxyPort = proxyPort;
	}

	public void connect(SimpleCallback callback, ErrorCallback errorCallback) {
		try {
			proxyConnection = new TcpConnection(InetAddress.getByName(path), proxyPort,
					(newConnection) -> newConnection.send(new UserInformation(CurrentUser.getInstance()), () -> {
						if (callback != null) {
							callback.call();
						}
					}, errorCallback),
					errorCallback);
		} catch (UnknownHostException e) {
			Log.e(getClass().getSimpleName(), "Could not connect to the proxy", e);
		}
	}

	public void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
		proxyConnection.send(message, callback, errorCallback);
	}

	public void receive(String userId, TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
		Log.v(getClass().getSimpleName(), "Registering a new receive request from " + userId);
		callbackMap.put(userId, callback);
		if (!receiving) {
			proxyConnection.receive(this::onMessageReceived, errorCallback);
			receiving = true;
		}
	}

	private void onMessageReceived(Object msg) throws IOException {
		if (msg instanceof SenderInfo) {
			TcpConnection.MessageReceivedCallback callback = callbackMap.get(((SenderInfo) msg).getSenderId());
			if (callback != null) {
				callback.onMessageReceived(msg);
			} else {
				Log.w(getClass().getSimpleName(),
						"Nobody is waiting for messages from " + ((SenderInfo) msg).getSenderId());
			}
		} else {
			Log.e(getClass().getSimpleName(),
					"Proxy sent a message that does not contain sender information: " +
							msg.getClass().getSimpleName());
		}
	}

	public boolean isConnected() {
		return proxyConnection != null &&
				proxyConnection.isOpen();
	}

	public void close() {
		proxyConnection.close();
	}

	public void disconnectUser(String id) {
		callbackMap.remove(id);
	}
}
