/* BdfServer - Copyright (c) 2022-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.tools.exportation.transformation;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.configuration.PathConfiguration;
import fr.exemole.bdfserver.api.exportation.transformation.SimpleTemplateFactory;
import fr.exemole.bdfserver.api.exportation.transformation.StreamTemplateFactory;
import fr.exemole.bdfserver.api.storage.TemplateStorage;
import fr.exemole.bdfserver.tools.configuration.PathConfigurationBuilder;
import fr.exemole.bdfserver.tools.exportation.transformation.compilers.CompilationException;
import fr.exemole.bdfserver.tools.exportation.transformation.compilers.SimpleTemplateCompiler;
import fr.exemole.bdfserver.tools.exportation.transformation.compilers.TemplateCompilerUtils;
import fr.exemole.bdfserver.tools.exportation.transformation.compilers.XsltOdStreamTemplateCompiler;
import fr.exemole.bdfserver.tools.exportation.transformation.defaulttemplates.DefaultOptions;
import fr.exemole.bdfserver.tools.exportation.transformation.defaulttemplates.TemplateStorageUnitFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.fichotheque.exportation.transformation.NoDefaultTemplateException;
import net.fichotheque.exportation.transformation.SimpleTemplate;
import net.fichotheque.exportation.transformation.StreamTemplate;
import net.fichotheque.exportation.transformation.TemplateKey;
import net.fichotheque.exportation.transformation.TransformationKey;
import net.mapeadores.util.attr.Attributes;
import net.mapeadores.util.exceptions.ShouldNotOccurException;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.logging.LogUtils;
import net.mapeadores.util.text.ValidExtension;


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

    private final static Set<String> ODT_DEFAULT_SET = new HashSet<String>();
    private final static Set<String> UNMODIFIABLE_ODT_DEFAULT_SET = Collections.unmodifiableSet(ODT_DEFAULT_SET);
    private final static Set<String> EMPTY_SET = Collections.emptySet();
    private final BdfServer bdfServer;
    private final Map<TemplateKey, SimpleTemplateFactory> distSimpleTemplateFactoryMap = new HashMap<TemplateKey, SimpleTemplateFactory>();
    private final Map<TemplateKey, StreamTemplateFactory> distStreamTemplateFactoryMap = new HashMap<TemplateKey, StreamTemplateFactory>();

    static {
        ODT_DEFAULT_SET.add("odt");
    }

    public DistTemplateManager(BdfServer bdfServer) {
        this.bdfServer = bdfServer;
    }

    public void clear() {
        distSimpleTemplateFactoryMap.clear();
        distStreamTemplateFactoryMap.clear();
    }

    public boolean containsTemplate(TemplateKey templateKey) {
        if (!templateKey.isDist()) {
            throw new IllegalArgumentException("!templateKey.isDist()");
        }
        if (templateKey.isSimpleTemplate()) {
            return isDistSimpleTemplateAvailable(templateKey);
        } else {
            if (templateKey.getName().equals(TemplateKey.DEFAULT_NAME)) {
                return isDefaultStreamTemplateAvailable(templateKey.getTransformationKey(), templateKey.getExtension());
            } else {
                return false;
            }
        }
    }

    public boolean isDefaultStreamTemplateAvailable(TransformationKey transformationKey, String extension) {
        return getDefaultStreamTemplateAvailableExtensionSet(transformationKey).contains(extension);
    }

    public Set<String> getDefaultStreamTemplateAvailableExtensionSet(TransformationKey transformationKey) {
        if (transformationKey.isCorpusTransformationKey()) {
            return UNMODIFIABLE_ODT_DEFAULT_SET;
        }
        switch (transformationKey.getKeyString()) {
            case TransformationKey.COMPILATION:
                return UNMODIFIABLE_ODT_DEFAULT_SET;
            default:
                return EMPTY_SET;
        }
    }

    public SimpleTemplate getSimpleTemplate(TemplateKey templateKey, boolean useDefault) {
        if (!templateKey.isDist()) {
            throw new IllegalArgumentException("!templateKey.isDist()");
        }
        if (!templateKey.isSimpleTemplate()) {
            throw new IllegalArgumentException("not a simple template key");
        }
        SimpleTemplateFactory simpleTemplateFactory = distSimpleTemplateFactoryMap.get(templateKey);
        if (simpleTemplateFactory == null) {
            simpleTemplateFactory = createDistSimpleTemplateFactory(templateKey);
        }
        if (simpleTemplateFactory != null) {
            return simpleTemplateFactory.newInstance();
        } else if (useDefault) {
            return getDefaultSimpleTemplate(templateKey.getTransformationKey());
        } else {
            return null;
        }
    }

    public StreamTemplate getStreamTemplate(TemplateKey templateKey, boolean useDefault) throws NoDefaultTemplateException {
        if (!templateKey.isDist()) {
            throw new IllegalArgumentException("!templateKey.isDist()");
        }
        if (!templateKey.isStreamTemplate()) {
            throw new IllegalArgumentException("not a stream template key");
        }
        if ((useDefault) || (templateKey.getName().equals(TemplateKey.DEFAULT_NAME))) {
            return DistTemplateManager.this.getDefaultStreamTemplate(templateKey.getTransformationKey(), templateKey.getValidExtension());
        } else {
            return null;
        }
    }

    public SimpleTemplate getDefaultSimpleTemplate(TransformationKey transformationKey) {
        TemplateKey defaultTemplateKey = TemplateKey.toDefault(transformationKey);
        SimpleTemplateFactory defaultSimpleTemplateFactory = distSimpleTemplateFactoryMap.get(defaultTemplateKey);
        if (defaultSimpleTemplateFactory == null) {
            defaultSimpleTemplateFactory = createDistSimpleTemplateFactory(defaultTemplateKey);
        }
        return defaultSimpleTemplateFactory.newInstance();
    }

    public StreamTemplate getDefaultStreamTemplate(TransformationKey transformationKey, ValidExtension validExtension) throws NoDefaultTemplateException {
        TemplateKey defaultTemplateKey = TemplateKey.toDefault(transformationKey, validExtension);
        StreamTemplateFactory streamTemplateFactory = distStreamTemplateFactoryMap.get(defaultTemplateKey);
        if (streamTemplateFactory == null) {
            streamTemplateFactory = createDefaultStreamTemplateFactory(defaultTemplateKey);
            distStreamTemplateFactoryMap.put(defaultTemplateKey, streamTemplateFactory);
        }
        return streamTemplateFactory.newInstance();
    }

    private boolean isDistSimpleTemplateAvailable(TemplateKey distTemplateKey) {
        switch (distTemplateKey.getName()) {
            case TemplateKey.DEFAULT_NAME:
            case TemplateKey.CLEAN_NAME:
                return true;
            case TemplateKey.FRAGMENT_NAME:
                return TransformationAvailabilities.hasDefaultFragment(distTemplateKey.getTransformationKey());
            default:
                return false;
        }
    }

    private SimpleTemplateFactory createDistSimpleTemplateFactory(TemplateKey distTemplateKey) {
        SimpleTemplateFactory simpleTemplateFactory = null;
        switch (distTemplateKey.getName()) {
            case TemplateKey.DEFAULT_NAME:
                simpleTemplateFactory = createSimpleTemplateFactory(distTemplateKey, BdfTransformationUtils.HTML_XSLT_DEFAULT_TEMPLATE_ATTRIBUTES);
                break;
            case TemplateKey.CLEAN_NAME:
                simpleTemplateFactory = createSimpleTemplateFactory(distTemplateKey, BdfTransformationUtils.HTML_XSLT_CLEAN_TEMPLATE_ATTRIBUTES);
                break;
            case TemplateKey.FRAGMENT_NAME:
                if (TransformationAvailabilities.hasDefaultFragment(distTemplateKey.getTransformationKey())) {
                    simpleTemplateFactory = createFragmentSimpleTemplateFactory(distTemplateKey);
                }
                break;
        }
        if (simpleTemplateFactory == null) {
            return null;
        } else {
            distSimpleTemplateFactoryMap.put(distTemplateKey, simpleTemplateFactory);
            return simpleTemplateFactory;
        }

    }

    private SimpleTemplateFactory createSimpleTemplateFactory(TemplateKey distTemplateKey, Attributes attributes) {
        PathConfiguration pathConfiguration = getPathConfiguration();
        DefaultOptions options = DefaultOptions.init()
                .withExtractionDef(false);
        TemplateStorage.Unit templateStorageUnit = TemplateStorageUnitFactory.newXsltHtml(bdfServer, distTemplateKey, options, attributes);
        SimpleTemplateCompiler simpleTemplateCompiler = TemplateCompilerUtils.getSimpleTemplateCompiler("xslt", bdfServer, pathConfiguration, distTemplateKey, new LogUtils.ShouldNotOccurMessageHandler());
        try {
            return simpleTemplateCompiler.compile(templateStorageUnit);
        } catch (CompilationException ce) {
            throw new ShouldNotOccurException(BdfTransformationUtils.getShouldNotOccurMessage(simpleTemplateCompiler));
        }
    }

    private SimpleTemplateFactory createFragmentSimpleTemplateFactory(TemplateKey fragmentTemplateKey) {
        PathConfiguration pathConfiguration = getPathConfiguration();
        TemplateStorage.Unit templateStorageUnit = TemplateStorageUnitFactory.newXsltFragment(bdfServer, fragmentTemplateKey, BdfTransformationUtils.ODT_XSLT_DEFAULT_TEMPLATE_ATTRIBUTES);
        SimpleTemplateCompiler simpleTemplateCompiler = TemplateCompilerUtils.getSimpleTemplateCompiler("xslt", bdfServer, pathConfiguration, fragmentTemplateKey, new LogUtils.ShouldNotOccurMessageHandler());
        try {
            return simpleTemplateCompiler.compile(templateStorageUnit);
        } catch (CompilationException ce) {
            throw new ShouldNotOccurException(BdfTransformationUtils.getShouldNotOccurMessage(simpleTemplateCompiler));
        }
    }

    private StreamTemplateFactory createDefaultStreamTemplateFactory(TemplateKey defaultTemplateKey) throws NoDefaultTemplateException {
        TransformationKey transformationKey = defaultTemplateKey.getTransformationKey();
        String extension = defaultTemplateKey.getExtension();
        TemplateStorage.Unit templateStorageUnit = BdfTransformationUtils.getStreamDefaultUnit(bdfServer, transformationKey, extension);
        if (templateStorageUnit == null) {
            throw new NoDefaultTemplateException(transformationKey, extension);
        }
        if (extension.equals("odt")) {
            XsltOdStreamTemplateCompiler compiler = new XsltOdStreamTemplateCompiler(bdfServer, getPathConfiguration(), defaultTemplateKey, new LogUtils.ShouldNotOccurMessageHandler());
            try {
                return compiler.compile(templateStorageUnit);
            } catch (CompilationException ce) {
                throw new ShouldNotOccurException(BdfTransformationUtils.getShouldNotOccurMessage(compiler));
            }
        } else {
            throw new SwitchException("extension = " + extension);
        }
    }

    private PathConfiguration getPathConfiguration() {
        return PathConfigurationBuilder.build(bdfServer);
    }

}
