/* BdfServer - Copyright (c) 2012-2024 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.impl;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.api.users.BdfUserConstants;
import fr.exemole.bdfserver.api.users.BdfUserPrefs;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import fr.exemole.bdfserver.tools.configuration.ConfigurationUtils;
import fr.exemole.bdfserver.tools.roles.PermissionSummaryBuilder;
import fr.exemole.bdfserver.tools.subsettree.TreeUtils;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import net.fichotheque.EditOrigin;
import net.fichotheque.corpus.Fiches;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.FichothequeQueries;
import net.fichotheque.selection.SelectionContext;
import net.fichotheque.selection.SelectionDef;
import net.fichotheque.sphere.LoginKey;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.tools.corpus.FichesBuilder;
import net.fichotheque.tools.permission.PermissionUtils;
import net.fichotheque.tools.selection.FicheQueryBuilder;
import net.fichotheque.tools.selection.UserConditionBuilder;
import net.fichotheque.utils.EditOriginUtils;
import net.fichotheque.utils.selection.FicheSelectorBuilder;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.localisation.LangPreference;
import net.mapeadores.util.localisation.LangPreferenceBuilder;
import net.mapeadores.util.text.TypoOptions;


/**
 *
 * @author Vincent Calame
 */
class BdfUserImpl implements BdfUser {

    private static long lastBdfSessionId = 0;
    private final Map<String, Object> parameterMap = new HashMap<String, Object>();
    private final String bdfServerName;
    private final LoginKey loginKey;
    private final Redacteur redacteur;
    private final BdfUserPrefsImpl prefs;
    private FicheQuery simpleFicheQuery;
    private Fiches selectedFiches = null;
    private Object selectionObject;
    private final String bdfSessionId;
    private Lang workingLang;
    private Locale formatLocale;
    private LangPreference langPreference;

    private BdfUserImpl(String bdfServerName, Redacteur redacteur) {
        this.bdfServerName = bdfServerName;
        this.loginKey = LoginKey.build(redacteur.getSubsetKey(), redacteur.getLogin());
        this.bdfSessionId = getNewBdfSessionId();
        this.redacteur = redacteur;
        this.prefs = new BdfUserPrefsImpl(this);
        this.simpleFicheQuery = FicheQueryBuilder.init()
                .setUserCondition(UserConditionBuilder.init().addId(redacteur).toUserCondition())
                .toFicheQuery();
        prefs.defaultFicheQuery = simpleFicheQuery;
        this.selectionObject = simpleFicheQuery;
    }

    void updateLocalisation(BdfServer bdfServer) {
        workingLang = prefs.getWorkingLang();
        if (workingLang == null) {
            workingLang = bdfServer.getDefaultWorkingLang();
        }
        formatLocale = prefs.getCustomFormatLocale();
        if (formatLocale == null) {
            formatLocale = workingLang.toLocale();
        }
        putParameter(BdfUserConstants.FORM_TYPOOPTIONS, TypoOptions.getTypoOptions(formatLocale));
        LangPreferenceBuilder langPreferenceBuilder = new LangPreferenceBuilder();
        LangPreference customLangPreference = prefs.getCustomLangPreference();
        if (customLangPreference != null) {
            langPreferenceBuilder.addLangs(customLangPreference);
        }
        langPreferenceBuilder.addLang(workingLang);
        ConfigurationUtils.checkLangPreference(bdfServer.getLangConfiguration(), langPreferenceBuilder);
        this.langPreference = langPreferenceBuilder.toLangPreference();
    }

    @Override
    public LoginKey getLoginKey() {
        return loginKey;
    }

    @Override
    public String getBdfServerName() {
        return bdfServerName;
    }

    @Override
    public Redacteur getRedacteur() {
        return redacteur;
    }

    @Override
    public BdfUserPrefs getPrefs() {
        return prefs;
    }

    @Override
    public Lang getWorkingLang() {
        return workingLang;
    }

    @Override
    public Locale getFormatLocale() {
        return formatLocale;
    }

    @Override
    public LangPreference getLangPreference() {
        return langPreference;
    }

    @Override
    public FicheQuery getFicheQuery() {
        return simpleFicheQuery;
    }

    @Override
    public Fiches getSelectedFiches() {
        return selectedFiches;
    }


    @Override
    public Object getParameterValue(String parameterName) {
        return parameterMap.get(parameterName);
    }

    @Override
    public void putParameter(String parameterName, Object parameterValue) {
        parameterMap.put(parameterName, parameterValue);
    }

    @Override
    public EditOrigin newEditOrigin(String source) {
        String globalId;
        Object alternateGlobalId = getParameterValue(BdfUserConstants.EDIT_ALTERNATEGLOBALID);
        if (alternateGlobalId != null) {
            globalId = alternateGlobalId.toString();
        } else {
            globalId = redacteur.getGlobalId();
        }
        return EditOriginUtils.newEditOrigin(source, bdfSessionId, globalId);
    }

    void setFicheQuery(FicheQuery ficheQuery) {
        if (ficheQuery == null) {
            throw new IllegalArgumentException("ficheQuery is null");
        }
        this.simpleFicheQuery = ficheQuery;
        this.selectionObject = ficheQuery;
    }

    void setFicheSelection(BdfServer bdfServer, Object selectionObject, String sortType) {
        if (selectionObject == null) {
            throw new IllegalArgumentException("selectionObject is null");
        }
        this.selectionObject = selectionObject;
        if (selectionObject instanceof FicheQuery) {
            this.simpleFicheQuery = (FicheQuery) selectionObject;
        }
        updateFicheSelection(bdfServer, sortType);
    }

    void setSelectedFiches(Fiches fiches) {
        this.selectedFiches = fiches;
        this.selectionObject = null;
    }

    boolean updateFicheSelection(BdfServer bdfServer, String sortType) {
        if (selectionObject == null) {
            return false;
        }
        FicheSelectorBuilder ficheSelectorBuilder = FicheSelectorBuilder.init(getSelectionContext(bdfServer));
        if (selectionObject instanceof FicheQuery) {
            ficheSelectorBuilder.add((FicheQuery) selectionObject);
        } else if (selectionObject instanceof FichothequeQueries) {
            for (FicheQuery ficheQuery : ((FichothequeQueries) selectionObject).getFicheQueryList()) {
                ficheSelectorBuilder.add(ficheQuery);
            }
        } else if (selectionObject instanceof String) {
            SelectionDef selectionDef = bdfServer.getSelectionManager().getSelectionDef((String) selectionObject);
            if (selectionDef != null) {
                for (FicheQuery ficheQuery : selectionDef.getFichothequeQueries().getFicheQueryList()) {
                    ficheSelectorBuilder.add(ficheQuery);
                }
                putParameter(BdfUserConstants.SELECTION_DEFNAME, selectionObject);
            } else {
                return false;
            }
        }
        this.selectedFiches = FichesBuilder.build(sortType, getWorkingLang())
                .initSubsetKeyOrder(TreeUtils.getCorpusKeyList(bdfServer))
                .populate(ficheSelectorBuilder.toFicheSelector())
                .toFiches();
        return true;
    }

    private SelectionContext getSelectionContext(BdfServer bdfServer) {
        PermissionSummary permissionSummary = PermissionSummaryBuilder.build(bdfServer, this);
        return BdfServerUtils.initSelectionContextBuilder(bdfServer, getWorkingLang())
                .setSubsetAccessPredicate(permissionSummary.getSubsetAccessPredicate())
                .setFichePredicate(PermissionUtils.getFichePredicate(permissionSummary))
                .toSelectionContext();
    }

    private void initBdfUserPrefs(BdfUserPrefs bdfUserPrefs) {
        prefs.init(bdfUserPrefs);
        FicheQuery defaultFicheQuery = bdfUserPrefs.getDefaultFicheQuery();
        if (defaultFicheQuery != null) {
            this.simpleFicheQuery = defaultFicheQuery;
            this.selectionObject = defaultFicheQuery;
        }
    }

    private synchronized static String getNewBdfSessionId() {
        long l = System.currentTimeMillis();
        if (l <= lastBdfSessionId) {
            l = lastBdfSessionId + 1;
        }
        lastBdfSessionId = l;
        return String.valueOf(l);
    }

    static BdfUserImpl build(BdfServer bdfServer, Redacteur redacteur) {
        BdfUserImpl bdfUser = new BdfUserImpl(bdfServer.getName(), redacteur);
        BdfUserPrefs storagePrefs = bdfServer.getBdfUserStorage().getBdfUserPrefs(redacteur);
        if (storagePrefs != null) {
            bdfUser.initBdfUserPrefs(storagePrefs);
        }
        bdfUser.updateLocalisation(bdfServer);
        return bdfUser;
    }

}
