package eu.jonahbauer.wizard.server;

import eu.jonahbauer.wizard.common.messages.client.CreateSessionMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.messages.server.SessionCreatedMessage;
import eu.jonahbauer.wizard.common.messages.server.SessionListMessage;
import eu.jonahbauer.wizard.common.messages.server.SessionRemovedMessage;
import eu.jonahbauer.wizard.server.machine.Player;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Lobby {
    private static final Lobby INSTANCE = new Lobby();
    public static Lobby getInstance() {
        return INSTANCE;
    }

    private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
    private final List<Player> players = new ArrayList<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private Lobby() {}

    public Session createSession(CreateSessionMessage create) {
        lock.readLock().lock();
        try {
            Session session;
            do {
                session = new Session(
                        UUID.randomUUID(),
                        create.getSessionName(),
                        create.getTimeout(),
                        create.getConfiguration()
                );
            } while (sessions.putIfAbsent(session.getUuid(), session) != null);

            notifyPlayers(new SessionCreatedMessage(session.toData()));

            return session;
        } finally {
            lock.readLock().unlock();
        }
    }

    public Session getSession(UUID uuid) {
        return sessions.get(uuid);
    }

    public void removeSession(UUID uuid) {
        lock.readLock().lock();
        try {
            sessions.remove(uuid);
            notifyPlayers(new SessionRemovedMessage(uuid));
        } finally {
            lock.readLock().unlock();
        }
    }

    public void join(Player player) {
        lock.writeLock().lock();
        try {
            players.add(player);
            player.send(new SessionListMessage(sessions.values().stream().map(Session::toData).toList()));
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void leave(Player player) {
        lock.writeLock().lock();
        try {
            players.remove(player);
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void notifyPlayers(ServerMessage message) {
        lock.readLock().lock();
        try {
            for (Player player : players) {
                player.send(message);
            }
        } finally {
            lock.readLock().unlock();
        }
    }
}
