/* BdfServer - Copyright (c) 2006-2025 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.BdfExtensionInitializer;
import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.BdfServerEditor;
import fr.exemole.bdfserver.api.EditSession;
import fr.exemole.bdfserver.api.EditSessionListener;
import fr.exemole.bdfserver.api.configuration.LangConfiguration;
import fr.exemole.bdfserver.api.managers.AccessManager;
import fr.exemole.bdfserver.api.managers.BalayageManager;
import fr.exemole.bdfserver.api.managers.ExtensionManager;
import fr.exemole.bdfserver.api.managers.ExternalScriptManager;
import fr.exemole.bdfserver.api.managers.GroupManager;
import fr.exemole.bdfserver.api.managers.JsLibManager;
import fr.exemole.bdfserver.api.managers.L10nManager;
import fr.exemole.bdfserver.api.managers.PasswordManager;
import fr.exemole.bdfserver.api.managers.PermissionManager;
import fr.exemole.bdfserver.api.managers.PolicyManager;
import fr.exemole.bdfserver.api.managers.ScrutariExportManager;
import fr.exemole.bdfserver.api.managers.SelectionManager;
import fr.exemole.bdfserver.api.managers.SqlExportManager;
import fr.exemole.bdfserver.api.managers.TableExportManager;
import fr.exemole.bdfserver.api.managers.TransformationManager;
import fr.exemole.bdfserver.api.managers.TreeManager;
import fr.exemole.bdfserver.api.managers.UiManager;
import fr.exemole.bdfserver.api.storage.BdfUserStorage;
import fr.exemole.bdfserver.api.storage.MigrationEngine;
import fr.exemole.bdfserver.api.storage.StorageCheck;
import fr.exemole.bdfserver.api.storage.StorageRoot;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.conf.BdfServerDirs;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import fr.exemole.bdfserver.tools.externalsource.CoreExternalSourceProvider;
import fr.exemole.bdfserver.tools.storage.ResourceJsAnalyser;
import fr.exemole.bdfserver.tools.storage.Storages;
import java.util.List;
import java.util.Map;
import net.fichotheque.EditOrigin;
import net.fichotheque.Fichotheque;
import net.fichotheque.FichothequeEditor;
import net.fichotheque.FichothequeEditorProvider;
import net.fichotheque.exportation.table.TableExportContext;
import net.fichotheque.externalsource.ExternalSourceProvider;
import net.fichotheque.importation.ImportationContext;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.thesaurus.metadata.ThesaurusLangChecker;
import net.fichotheque.tools.importation.ImportationUtils;
import net.mapeadores.util.buildinfo.BuildInfo;
import net.mapeadores.util.css.SacParserInit;
import net.mapeadores.util.io.ResourceStorages;
import net.mapeadores.util.jslib.JsAnalyser;
import net.mapeadores.util.misc.ArrayUtils;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.logging.SourceLogs;


/**
 *
 * @author Vincent Calame
 */
public class BdfServerImpl implements BdfServer {

    static {
        SacParserInit.init();
    }

    private final String name;
    private final Map<String, Object> contextObjectMap;
    private final BdfServerDirs bdfServerDirs;
    private final Fichotheque fichotheque;
    private final FichothequeEditorProvider fichothequeEditorProvider;
    private final PermissionManagerImpl permissionManager;
    private final GroupManagerImpl groupManager;
    private final TreeManagerImpl treeManager;
    private final PasswordManagerImpl passwordManager;
    private final UiManagerImpl uiManager;
    private final PolicyManagerImpl policyManager;
    private final BdfServerFichothequeListener fichothequeListener;
    private final BalayageManagerImpl balayageManager;
    private final ScrutariExportManagerImpl scrutariExportManager;
    private final SqlExportManagerImpl sqlExportManager;
    private final SelectionManagerImpl selectionManager;
    private final LangConfigurationImpl langConfiguration;
    private final L10nManagerImpl l10nManager;
    private final ExtensionManagerImpl extensionManager;
    private final TransformationManagerImpl transformationManager;
    private final FormatContextImpl formatContext;
    private final TableExportContextImpl tableExportContext;
    private final TableExportManagerImpl tableExportManager;
    private final ExternalScriptManagerImpl externalScriptManager;
    private final ThesaurusLangCheckerImpl thesaurusLangChecker;
    private final AccessManagerImpl accessManager;
    private final JsLibManagerImpl jsLibManager;
    private final JsAnalyser jsAnalyser;
    private final SourceLogs initLogs;
    private final BuildInfo buildInfo;
    private final ResourceStorages resourceStorages;
    private final StorageRoot outputStorage;
    private final StorageRoot cacheStorage;
    private final BdfUserStorage bdfUserStorage;
    private final ExternalSourceProvider externalSourceProvider;
    private final ImportationContext importationContext;
    private EditSessionListener[] editSessionListenerArray = new EditSessionListener[0];


    public BdfServerImpl(String name, Storages storages, BuildInfo buildInfo, List<BdfExtensionInitializer> extensionInitializerList, Map<String, Object> contextObjectMap) {
        this.name = name;
        this.contextObjectMap = contextObjectMap;
        this.bdfServerDirs = storages.getBdfServerDirs();
        this.buildInfo = buildInfo;
        this.initLogs = storages.getInitLogs();
        StorageCheck storageCheck = storages.getStorageCheck();
        this.bdfUserStorage = storages.getBdfUserStorage();
        this.resourceStorages = storages.getResourceStorages();
        this.outputStorage = storages.getOutputStorage();
        this.cacheStorage = storages.getCacheStorage();
        this.fichothequeEditorProvider = storages.getFichothequeEditorProvider();
        this.fichotheque = fichothequeEditorProvider.getFichotheque();
        this.langConfiguration = new LangConfigurationImpl(storages.getConfigurationStorage());
        langConfiguration.update(storageCheck.getLangConfiguration());
        this.permissionManager = PermissionManagerImpl.build(fichotheque, storages.getRoleStorage(), storages.getRedacteurList(), storageCheck);
        this.groupManager = GroupManagerImpl.build(storages.getGroupStorage(), storageCheck.getGroupDefList());
        this.treeManager = TreeManagerImpl.build(fichotheque, storages.getTreeStorage(), storageCheck.getSubsetTreeMap());
        this.passwordManager = new PasswordManagerImpl(fichotheque, storages.getPasswordStorage());
        this.policyManager = PolicyManagerImpl.build(fichotheque, storages.getPolicyStorage(), storageCheck);
        this.selectionManager = new SelectionManagerImpl(this, storages.getSelectionStorage());
        selectionManager.check(storageCheck.getSelectionDefList());
        this.scrutariExportManager = new ScrutariExportManagerImpl(storages.getScrutariExportStorage());
        scrutariExportManager.check(storageCheck.getScrutariExportDefList());
        this.sqlExportManager = new SqlExportManagerImpl(storages.getSqlExportStorage());
        sqlExportManager.check(storageCheck.getSqlExportDefList());
        this.l10nManager = new L10nManagerImpl(resourceStorages);
        this.jsAnalyser = new ResourceJsAnalyser(resourceStorages);
        this.jsLibManager = new JsLibManagerImpl(resourceStorages);
        this.externalScriptManager = new ExternalScriptManagerImpl(this);
        this.extensionManager = new ExtensionManagerImpl(this, fichotheque, storages.getBdfExtensionStorage(), extensionInitializerList, storages.getConfigurationStorage());
        extensionManager.update(storageCheck.getActiveExtensionList());
        this.thesaurusLangChecker = new ThesaurusLangCheckerImpl();
        this.balayageManager = new BalayageManagerImpl(storages.getBalayageStorage());
        this.uiManager = UiManagerImpl.build(this, storages.getUiStorage(), storageCheck.getUiCheckMap());
        this.transformationManager = new TransformationManagerImpl(this, storages.getTemplateStorage());
        this.formatContext = new FormatContextImpl(this, extensionManager.getInstructionResolverProvider(), transformationManager, l10nManager, uiManager);
        this.tableExportContext = new TableExportContextImpl(fichotheque, extensionManager, formatContext, langConfiguration, l10nManager, uiManager);
        this.tableExportManager = new TableExportManagerImpl(fichotheque, storages.getTableExportStorage(), tableExportContext);
        this.accessManager = new AccessManagerImpl(this, storages.getAccessStorage());
        accessManager.check(storageCheck.getAccessDefList());
        this.externalSourceProvider = new CoreExternalSourceProvider(this);
        this.importationContext = ImportationUtils.toImportationContext(fichothequeEditorProvider.getHtmlCleaner(), BdfServerUtils.DEFAULT_HTMLCONVERTER);
        this.fichothequeListener = new BdfServerFichothequeListener(this);

        fichotheque.addFichothequeListener(fichothequeListener);
        fichotheque.addFichothequeListener(treeManager);
        fichotheque.addFichothequeListener(uiManager);
        extensionManager.endInit();
        MigrationEngine migrationEngine = storages.getMigrationEngine();
        if (migrationEngine != null) {
            migrationEngine.afterInitRun(this);
        }
        this.transformationManager.update();
        this.tableExportManager.update();
        this.balayageManager.update();
        this.jsLibManager.update();
    }

    @Override
    public SourceLogs getInitLogs() {
        return initLogs;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Object getContextObject(String objectName) {
        if (objectName.equals(BdfServerConstants.BDFSERVER_CONTEXTOBJECT)) {
            return this;
        } else {
            return contextObjectMap.get(objectName);
        }
    }

    @Override
    public BdfServerDirs getBdfServerDirs() {
        return bdfServerDirs;
    }

    @Override
    public Fichotheque getFichotheque() {
        return fichotheque;
    }

    @Override
    public PermissionManager getPermissionManager() {
        return permissionManager;
    }

    @Override
    public PasswordManager getPasswordManager() {
        return passwordManager;
    }

    @Override
    public TransformationManager getTransformationManager() {
        return transformationManager;
    }

    @Override
    public UiManager getUiManager() {
        return uiManager;
    }

    @Override
    public PolicyManager getPolicyManager() {
        return policyManager;
    }

    @Override
    public TableExportManager getTableExportManager() {
        return tableExportManager;
    }

    @Override
    public SelectionManager getSelectionManager() {
        return selectionManager;
    }

    @Override
    public ScrutariExportManager getScrutariExportManager() {
        return scrutariExportManager;
    }

    @Override
    public SqlExportManager getSqlExportManager() {
        return sqlExportManager;
    }

    @Override
    public ExtensionManager getExtensionManager() {
        return extensionManager;
    }

    @Override
    public BalayageManager getBalayageManager() {
        return balayageManager;
    }

    @Override
    public ExternalScriptManager getExternalScriptManager() {
        return externalScriptManager;
    }

    @Override
    public AccessManager getAccessManager() {
        return accessManager;
    }

    @Override
    public BdfUserStorage getBdfUserStorage() {
        return bdfUserStorage;
    }

    @Override
    public GroupManager getGroupManager() {
        return groupManager;
    }

    @Override
    public TreeManager getTreeManager() {
        return treeManager;
    }

    @Override
    public L10nManager getL10nManager() {
        return l10nManager;
    }

    @Override
    public JsLibManager getJsLibManager() {
        return jsLibManager;
    }

    @Override
    public BdfUser createBdfUser(Redacteur redacteur) {
        return BdfUserImpl.build(this, redacteur);
    }

    @Override
    public LangConfiguration getLangConfiguration() {
        return langConfiguration;
    }

    @Override
    public void addEditionSessionListener(EditSessionListener editSessionListener) {
        int length = editSessionListenerArray.length;
        editSessionListenerArray = (EditSessionListener[]) ArrayUtils.addUnique(editSessionListenerArray, editSessionListener, new EditSessionListener[length + 1]);
    }

    @Override
    public void removeEditionSessionListener(EditSessionListener editSessionListener) {
        int length = editSessionListenerArray.length;
        if (length > 0) {
            editSessionListenerArray = (EditSessionListener[]) ArrayUtils.removeUnique(editSessionListenerArray, editSessionListener, new EditSessionListener[length - 1]);
        }
    }

    @Override
    public EditSession initEditSession(EditOrigin editOrigin) {
        return new InternalEditSession(this, editOrigin);
    }

    @Override
    public BuildInfo getBuildInfo() {
        return buildInfo;
    }

    @Override
    public ResourceStorages getResourceStorages() {
        return resourceStorages;
    }

    @Override
    public StorageRoot getOutputStorage() {
        return outputStorage;
    }

    @Override
    public StorageRoot getCacheStorage() {
        return cacheStorage;
    }

    @Override
    public JsAnalyser getJsAnalyser() {
        return jsAnalyser;
    }

    @Override
    public ExternalSourceProvider getExternalSourceProvider() {
        return externalSourceProvider;
    }

    @Override
    public HtmlCleaner getHtmlCleaner() {
        return fichothequeEditorProvider.getHtmlCleaner();
    }

    @Override
    public ThesaurusLangChecker getThesaurusLangChecker() {
        return thesaurusLangChecker;
    }

    @Override
    public TableExportContext getTableExportContext() {
        return tableExportContext;
    }

    @Override
    public ImportationContext getImportationContext() {
        return importationContext;
    }


    private void fireUseOfFichothequeEditor() {
        EditSessionListener[] array = editSessionListenerArray;
        int length = array.length;
        for (int i = 0; i < length; i++) {
            array[i].useOfFichothequeEditor();
        }
    }


    private static class InternalEditSession implements EditSession {

        private final BdfServerImpl bdfServer;
        private final EditOrigin editOrigin;
        private FichothequeEditor fichothequeEditor;
        private BdfServerEditorImpl bdfServerEditor;

        private InternalEditSession(BdfServerImpl bdfServer, EditOrigin editOrigin) {
            this.bdfServer = bdfServer;
            this.editOrigin = editOrigin;
            bdfServer.fichothequeListener.buffer();
        }

        @Override
        public BdfServer getBdfServer() {
            return bdfServer;
        }

        @Override
        public FichothequeEditor getFichothequeEditor() {
            if (fichothequeEditor == null) {
                fichothequeEditor = bdfServer.fichothequeEditorProvider.newFichothequeEditor(editOrigin);
                bdfServer.fireUseOfFichothequeEditor();
            }
            return fichothequeEditor;
        }

        @Override
        public BdfServerEditor getBdfServerEditor() {
            if (bdfServerEditor == null) {
                bdfServerEditor = new BdfServerEditorImpl(bdfServer);
            }
            return bdfServerEditor;
        }

        @Override
        public void close() {
            bdfServer.fichothequeListener.flush();
            if (fichothequeEditor != null) {
                fichothequeEditor.saveChanges();
            }
            if (bdfServerEditor != null) {
                bdfServerEditor.saveChanges();
            }
        }

    }

}
