/* BDF - Copyright (c) 2015-2023 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package fr.exemole.bdfserver.servlets;

import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.multi.api.central.CentralUser;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpSession;
import net.fichotheque.sphere.LoginKey;


/**
 *
 * @author Vincent Calame
 */
public class BDFSession {

    private final Map<String, FichothequeInfo> fichothequeInfoMap = new LinkedHashMap<String, FichothequeInfo>();
    private final Map<LoginKey, LoginInfo> loginKeyMap = new LinkedHashMap<LoginKey, LoginInfo>();
    private final Set<LoginKey> unmodifiableLoginKeySet = Collections.unmodifiableSet(loginKeyMap.keySet());
    private final HttpSession session;
    private boolean multiAdmin = false;
    private CentralUser centralUser = null;

    public BDFSession(HttpSession session) {
        this.session = session;
    }

    public HttpSession getHttpSession() {
        return session;
    }

    public boolean isEmpty() {
        return ((centralUser == null) && (!multiAdmin) && (fichothequeInfoMap.isEmpty()));
    }

    public boolean isMultiAdmin() {
        return multiAdmin;
    }

    public synchronized void setMultiAdmin(boolean multiAdmin) {
        this.multiAdmin = multiAdmin;
    }

    public boolean hasCentralUser() {
        return (centralUser != null);
    }

    public CentralUser getCentralUser() {
        return centralUser;
    }

    public Set<LoginKey> getLoginKeySet() {
        return unmodifiableLoginKeySet;
    }

    public synchronized BdfUser getBdfUser(String fichothequeName) {
        FichothequeInfo fichothequeInfo = fichothequeInfoMap.get(fichothequeName);
        if (fichothequeInfo != null) {
            return fichothequeInfo.getBdfUser();
        } else {
            return null;
        }
    }

    public synchronized void centralLogin(CentralUser centralUser) {
        if (this.centralUser != null) {
            return;
        }
        this.centralUser = centralUser;
        LoginKey loginKey = centralUser.getLoginKey();
        LoginInfo loginInfo = loginKeyMap.get(loginKey);
        if (loginInfo == null) {
            loginInfo = new LoginInfo();
            loginKeyMap.put(loginKey, loginInfo);
        }
        loginInfo.setCentralUser(true);
    }

    public synchronized void login(String fichothequeName, BdfUser bdfUser) {
        LoginKey loginKey = bdfUser.getLoginKey();
        LoginInfo loginInfo = loginKeyMap.get(loginKey);
        if (loginInfo == null) {
            loginInfo = new LoginInfo();
            loginKeyMap.put(loginKey, loginInfo);
        }
        loginInfo.addFichotheque(fichothequeName);
        fichothequeInfoMap.put(fichothequeName, new FichothequeInfo(loginKey, bdfUser));
    }

    public synchronized LoginKey logout(String fichothequeName) {
        FichothequeInfo fichothequeInfo = fichothequeInfoMap.remove(fichothequeName);
        LoginKey loginKey = null;
        if (fichothequeInfo != null) {
            loginKey = fichothequeInfo.getLoginKey();
            LoginInfo loginInfo = loginKeyMap.get(loginKey);
            loginInfo.removeFichotheque(fichothequeName);
            if (loginInfo.isEmpty()) {
                loginKeyMap.remove(loginKey);
            }
        }
        return loginKey;
    }

    public synchronized void logoutAll(LoginKey loginKey) {
        LoginInfo loginInfo = loginKeyMap.get(loginKey);
        if (loginInfo == null) {
            return;
        }
        for (String fichothequeName : loginInfo.fichothequeNameSet) {
            fichothequeInfoMap.remove(fichothequeName);
        }
        if ((centralUser != null) && (centralUser.getLoginKey().equals(loginKey))) {
            this.centralUser = null;
        }
        loginInfo.clear();
        loginKeyMap.remove(loginKey);
    }

    public boolean containsBdfUser(String fichothequeName, BdfUser bdfUser) {
        FichothequeInfo fichothequeInfo = fichothequeInfoMap.get(fichothequeName);
        if (fichothequeInfo != null) {
            return fichothequeInfo.getBdfUser().equals(bdfUser);
        } else {
            return false;
        }
    }


    private static class LoginInfo {

        private Set<String> fichothequeNameSet = new HashSet<String>();
        private boolean centralUser;

        private LoginInfo() {

        }

        private void setCentralUser(boolean centralUser) {
            this.centralUser = centralUser;
        }

        private void addFichotheque(String fichothequeName) {
            fichothequeNameSet.add(fichothequeName);
        }

        private void removeFichotheque(String fichothequeName) {
            fichothequeNameSet.remove(fichothequeName);
        }

        private void clear() {
            centralUser = false;
            fichothequeNameSet.clear();
        }

        private boolean isEmpty() {
            return ((!centralUser) && (fichothequeNameSet.isEmpty()));
        }

    }


    private static class FichothequeInfo {

        private final LoginKey loginKey;
        private final BdfUser bdfUser;

        private FichothequeInfo(LoginKey loginKey, BdfUser bdfUser) {
            this.loginKey = loginKey;
            this.bdfUser = bdfUser;
        }

        private LoginKey getLoginKey() {
            return loginKey;
        }

        private BdfUser getBdfUser() {
            return bdfUser;
        }

    }

}
