/* 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.BdfExtensionInitializer;
import fr.exemole.bdfserver.api.BdfExtensionReference;
import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.EditSessionListener;
import fr.exemole.bdfserver.api.managers.ExtensionManager;
import fr.exemole.bdfserver.api.storage.BdfExtensionStorage;
import fr.exemole.bdfserver.api.storage.ConfigurationStorage;
import fr.exemole.bdfserver.tools.BdfServerUtils;
import java.util.AbstractList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.TreeMap;
import net.fichotheque.Fichotheque;
import net.fichotheque.exportation.table.TableInclusionResolver;
import net.fichotheque.exportation.table.TableInclusionResolverProvider;
import net.mapeadores.util.instruction.InstructionResolver;
import net.mapeadores.util.instruction.InstructionResolverProvider;
import net.mapeadores.util.localisation.Message;


/**
 *
 * @author Vincent Calame
 */
class ExtensionManagerImpl implements ExtensionManager {

    private final BdfServer bdfServer;
    private final Map<String, InternalBdfExtensionReference> referenceMap = new TreeMap<String, InternalBdfExtensionReference>();
    private final BdfExtensionStorage bdfExtensionStorage;
    private final ConfigurationStorage configurationStorage;
    private final InternalInstructionResolverProvider instructionResolverProvider = new InternalInstructionResolverProvider();
    private final BdfExtensionReferenceList referenceList;

    ExtensionManagerImpl(BdfServer bdfServer, Fichotheque fichotheque, BdfExtensionStorage bdfExtensionStorage, List<BdfExtensionInitializer> extensionInitializerList, ConfigurationStorage configurationStorage) {
        this.bdfServer = bdfServer;
        this.configurationStorage = configurationStorage;
        for (BdfExtensionInitializer initializer : extensionInitializerList) {
            String registratioName = initializer.getRegistrationName();
            BdfExtensionInitializer.Factory factory = initializer.init(bdfExtensionStorage, fichotheque);
            InternalBdfExtensionReference reference = new InternalBdfExtensionReference(bdfServer, registratioName, factory);
            referenceMap.put(registratioName, reference);
        }
        this.bdfExtensionStorage = bdfExtensionStorage;
        referenceList = new BdfExtensionReferenceList(referenceMap.values().toArray(new InternalBdfExtensionReference[referenceMap.size()]));
    }

    void endInit() {
        for (InternalBdfExtensionReference reference : referenceMap.values()) {
            EditSessionListener editionSessionListener = (EditSessionListener) reference.factory.getImplementation(bdfServer, EditSessionListener.class);
            if (editionSessionListener != null) {
                bdfServer.addEditionSessionListener(editionSessionListener);
            }
        }
    }

    @Override
    public List<BdfExtensionReference> getBdfExtensionReferenceList() {
        return referenceList;
    }

    @Override
    public BdfExtensionReference getBdfExtensionReference(String registrationName) {
        return referenceMap.get(registrationName);
    }

    @Override
    public BdfExtensionStorage getBdfExtensionStorage() {
        return bdfExtensionStorage;
    }

    InstructionResolverProvider getInstructionResolverProvider() {
        return instructionResolverProvider;
    }

    TableInclusionResolver getExtensionTableInclusionResolver(String tableInclusionName) {
        return referenceList.getExtensionTableInclusionResolver(tableInclusionName);
    }


    void update(List<String> activeExtensionList) {
        referenceList.updateActive(activeExtensionList);
    }

    void save() {
        configurationStorage.saveActiveExtensionList(BdfServerUtils.toActiveExtensionList(referenceList));
    }


    private class InternalInstructionResolverProvider implements InstructionResolverProvider {

        private InternalInstructionResolverProvider() {

        }

        @Override
        public InstructionResolver getInstructionResolver(Class destinationClass, Object optionObject) {
            return referenceList.getInstructionResolver(destinationClass, optionObject);
        }

    }


    private static class InternalBdfExtensionReference implements BdfExtensionReference {

        private final BdfServer bdfServer;
        private final String registrationName;
        private final BdfExtensionInitializer.Factory factory;
        private final Map<Class, Object> implementationMap = new HashMap<Class, Object>();
        private boolean active = true;

        private InternalBdfExtensionReference(BdfServer bdfServer, String registrationName, BdfExtensionInitializer.Factory factory) {
            this.bdfServer = bdfServer;
            this.registrationName = registrationName;
            this.factory = factory;
        }

        @Override
        public String getRegistrationName() {
            return registrationName;
        }

        @Override
        public boolean isActive() {
            return active;
        }

        @Override
        public Message getTitleMessage() {
            return (Message) factory.getImplementation(bdfServer, Message.class);
        }

        @Override
        public Object getImplementation(Class interfaceClass) {
            if (active) {
                if (implementationMap.containsKey(interfaceClass)) {
                    return implementationMap.get(interfaceClass);
                }
                Object implementation = factory.getImplementation(bdfServer, interfaceClass);
                implementationMap.put(interfaceClass, implementation);
                return implementation;
            } else {
                return null;
            }
        }

        private void setActive(boolean active) {
            this.active = active;
        }

    }


    private static class BdfExtensionReferenceList extends AbstractList<BdfExtensionReference> implements RandomAccess {

        private final InternalBdfExtensionReference[] array;

        private BdfExtensionReferenceList(InternalBdfExtensionReference[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public BdfExtensionReference get(int index) {
            return array[index];
        }

        private void updateActive(List<String> activeExtensionList) {
            for (InternalBdfExtensionReference reference : array) {
                boolean active = false;
                if (activeExtensionList.contains(reference.getRegistrationName())) {
                    active = true;
                }
                reference.setActive(active);
            }
        }

        private InstructionResolver getInstructionResolver(Class destinationClass, Object optionObject) {
            for (InternalBdfExtensionReference reference : array) {
                InstructionResolverProvider provider = (InstructionResolverProvider) reference.getImplementation(InstructionResolverProvider.class);
                if (provider != null) {
                    InstructionResolver resolver = provider.getInstructionResolver(destinationClass, optionObject);
                    if (resolver != null) {
                        return resolver;
                    }
                }
            }
            return null;
        }

        private TableInclusionResolver getExtensionTableInclusionResolver(String nameSpace) {
            for (InternalBdfExtensionReference reference : array) {
                TableInclusionResolverProvider provider = (TableInclusionResolverProvider) reference.getImplementation(TableInclusionResolverProvider.class);
                if (provider != null) {
                    TableInclusionResolver resolver = provider.getTableInclusionResolver(nameSpace);
                    if (resolver != null) {
                        return resolver;
                    }
                }
            }
            return null;
        }

    }


}
