package com.insa.app.model;

import com.insa.app.Resources;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class Users {

    private User me;
    private ObservableList<User> others;
    private List<User> connectedUsers;

    public Users(String userName, UUID id) {
        connectedUsers = new ArrayList<>();
        try {
            String ip = Resources.getMyIp();
            me = new User(id, userName, ip, true);
            others = FXCollections.synchronizedObservableList(FXCollections.observableArrayList());
        }
        catch (Exception e) {
            Logger.getAnonymousLogger().log(Level.SEVERE, "Failed to retrieve IP address : " + e);
            Platform.exit();
        }
    }

    //todo find alternative?
    public User getMe() {
        return me;
    }

    public UUID getMyId() {
        return me.getId();
    }

    public String getMyUserName() {
        return me.getName();
    }

    public String getMyIp() {
        return me.getIpAddress();
    }

    public void setUserName(String newName) {
        me.setName(newName);
    }

    public void setMyIp(String ip) {
        me.setIpAdress(ip);
    }

    public ObservableList<User> getOthers() {
        return others;
    }

    public ObservableList<String> getConnectedUsersName() {
        List l = others.stream().map(Object::toString).collect(Collectors.toList());
        return FXCollections.observableList(l);
    }

    public void replaceOrInsert(User user) {
        try {
            if (user.equals(me)) {
                return;
            }
            User u = getUserById(user.getId());
            Logger.getAnonymousLogger().log(Level.INFO, u + " is now " + user);
            Runnable runnable = () -> {
                synchronized (others) {
                    others.removeAll(u);
                    others.add(user);
                    others.notifyAll();
                }
            };
            Platform.runLater(runnable);
        }
        catch (RuntimeException e) {

            others.add(user);
        }
    }

    public void setUserConnected(User user) {
        user.setConnected();
        replaceOrInsert(user);
        connectedUsers.add(user);
    }

    public void setUserDisconnected(User user) {
        user.setDisconnected();
        replaceOrInsert(user);
        connectedUsers.remove(user);
    }

    public User getUserById(UUID id) throws RuntimeException {
        if (id.equals(getMyId())) {
            return me;
        }
        else {
            return others.stream().filter(user -> user.getId().equals(id)).findAny().orElseThrow(RuntimeException::new);
        }
    }

    public User getUserByIp(String ip) throws RuntimeException {
        return others.stream().filter(user -> user.getIpAddress().equals(ip)).findAny().orElseThrow(RuntimeException::new);
    }

    public void deleteUser(User user) {
        others.removeAll(user);
    }

    public boolean isConnected(User user) {
        return connectedUsers.contains(user);
    }
}
