/* BdfServer_Get - Copyright (c) 2009-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.get.instructions;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.balayage.BalayageTemplate;
import fr.exemole.bdfserver.api.balayage.TemplateProvider;
import fr.exemole.bdfserver.api.configuration.PathConfiguration;
import fr.exemole.bdfserver.api.instruction.BdfInstruction;
import fr.exemole.bdfserver.api.instruction.BdfInstructionConstants;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.get.streamproducers.ExtractionXmlProducer;
import fr.exemole.bdfserver.tools.balayage.BdfBalayageUtils;
import fr.exemole.bdfserver.tools.balayage.TemplateProviderBuilder;
import fr.exemole.bdfserver.tools.balayage.engine.BalayageParameters;
import fr.exemole.bdfserver.tools.balayage.engine.EngineUtils;
import fr.exemole.bdfserver.tools.configuration.PathConfigurationBuilder;
import fr.exemole.bdfserver.tools.instruction.DefaultBdfParameters;
import java.io.IOException;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.List;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.exportation.balayage.BalayageConstants;
import net.fichotheque.exportation.balayage.BalayageDef;
import net.fichotheque.exportation.balayage.BalayageDescription;
import net.fichotheque.exportation.balayage.BalayageOutput;
import net.fichotheque.exportation.balayage.BalayageUnit;
import net.fichotheque.extraction.ExtractParameters;
import net.fichotheque.extraction.ExtractionSource;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.tools.extraction.ExtractionEngine;
import net.fichotheque.tools.extraction.ExtractionEngineUtils;
import net.mapeadores.util.exceptions.NestedSAXException;
import net.mapeadores.util.io.BufferErrorListener;
import net.mapeadores.util.io.StreamProducer;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.servlets.ResponseHandler;
import net.mapeadores.util.servlets.exceptions.InternalErrorException;
import net.mapeadores.util.servlets.handlers.StreamResponseHandler;
import net.mapeadores.util.servlets.handlers.XmlResponseHandler;
import net.mapeadores.util.text.ValueResolver;
import net.mapeadores.util.xml.XmlProducer;
import org.xml.sax.SAXException;


/**
 *
 * @author Vincent Calame
 */
public class BalayagesInstruction implements BdfInstruction {

    private final BdfServer bdfServer;
    private final String filePath;

    public BalayagesInstruction(BdfServer bdfServer, String filePath) {
        this.bdfServer = bdfServer;
        this.filePath = filePath;
    }

    @Override
    public short getBdfUserNeed() {
        return BdfInstructionConstants.MANDATORY_BDFUSER;
    }

    @Override
    public ResponseHandler runInstruction(BdfUser bdfUser) {
        BdfParameters bdfParameters = new DefaultBdfParameters(bdfServer, bdfUser);
        BalayageUnitInitializer balayageUnitInitializer = new BalayageUnitInitializer(bdfServer);
        boolean done = balayageUnitInitializer.initFilePath(filePath);
        if (!done) {
            return null;
        }
        try {
            balayageUnitInitializer.initExtraction();
        } catch (SAXException saxe) {
            throw new NestedSAXException(saxe);
        }
        BalayageParameters balayageParameters = balayageUnitInitializer.getBalayageParameters();
        ExtractParameters extractParameters = balayageParameters.getExtractParameters();
        ExtractionDef extractionDef = balayageUnitInitializer.getExtractionDef();
        Object dynamicObject = balayageUnitInitializer.getDynamicObject();
        ExtractionSource extractionSource = ExtractionEngineUtils.getExtractionSource(dynamicObject, extractParameters.getExtractionContext(), extractionDef, balayageParameters.getSelectedFiches());
        if (balayageUnitInitializer.isXmlVersion()) {
            XmlProducer xmlProducer = new ExtractionXmlProducer(extractParameters, extractionDef, extractionSource, null);
            return XmlResponseHandler.init(xmlProducer);
        } else {
            if (balayageUnitInitializer.hasError()) {
                throw new InternalErrorException("_ error.exception.internalerror", balayageUnitInitializer.getErrorKey());
            }
            TemplateProvider templateProvider = balayageUnitInitializer.getTemplateProvider();
            String extractionString = ExtractionEngine.init(extractParameters, extractionDef).run(extractionSource);
            return StreamResponseHandler.init(new BalayageTemplateStreamProducer(null, templateProvider.getBalayageTemplate(dynamicObject), extractionString));
        }
    }


    private static class BalayageTemplateStreamProducer implements StreamProducer {

        private final String fileName;
        private final BalayageTemplate balayageTemplate;
        private final String extractionString;

        private BalayageTemplateStreamProducer(String fileName, BalayageTemplate balayageTemplate, String extractionString) {
            this.fileName = fileName;
            this.balayageTemplate = balayageTemplate;
            this.extractionString = extractionString;
        }

        @Override
        public String getMimeType() {
            return balayageTemplate.getMimeType();
        }

        @Override
        public String getCharset() {
            return balayageTemplate.getCharset();
        }

        @Override
        public String getFileName() {
            return fileName;
        }

        @Override
        public void writeStream(OutputStream outputStream) throws IOException {
            balayageTemplate.transform(extractionString, outputStream);
        }

    }


    private static class BalayageUnitInitializer {

        private final BdfServer bdfServer;
        private final PathConfiguration pathConfiguration;
        private BalayageDescription balayageDescription;
        private BalayageDef balayageDef;
        private BalayageUnit balayageUnit;
        private SubsetKey subsetKey;
        private Subset subset;
        private SubsetItem subsetItem;
        private Lang lang;
        private boolean xmlVersion;
        private BalayageParameters balayageParameters;
        private ExtractionDef extractionDef;
        private Object dynamicObject;
        private String errorKey = null;
        private TemplateProvider templateProvider;
        private BalayageOutput output;

        private BalayageUnitInitializer(BdfServer bdfServer) {
            this.bdfServer = bdfServer;
            this.pathConfiguration = PathConfigurationBuilder.build(bdfServer);
        }

        public boolean isXmlVersion() {
            return xmlVersion;
        }

        public BalayageParameters getBalayageParameters() {
            return balayageParameters;
        }

        public ExtractionDef getExtractionDef() {
            return extractionDef;
        }

        public Object getDynamicObject() {
            return dynamicObject;
        }

        public TemplateProvider getTemplateProvider() {
            return templateProvider;
        }

        public boolean hasError() {
            return (errorKey != null);
        }

        public String getErrorKey() {
            return errorKey;
        }

        public boolean initFilePath(String filePath) {
            int ln = filePath.length();
            int idx1 = filePath.indexOf('/');
            if (idx1 == -1) {
                return false;
            }
            int idx2 = filePath.indexOf('/', idx1 + 1);
            if ((idx2 == -1) || (idx2 > (ln - 5))) {
                return false;
            }
            String balayageName = filePath.substring(0, idx1);
            boolean test = initBalayage(balayageName);
            if (!test) {
                return false;
            }
            String balayageUnitName = filePath.substring(idx1 + 1, idx2);
            test = initBalayageUnit(balayageUnitName);
            if (!test) {
                return false;
            }
            String baseName = filePath.substring(idx2 + 1);
            test = initSubset(baseName);
            return test;
        }

        private void initExtraction() throws SAXException {
            balayageParameters = new BalayageParameters(balayageDescription, bdfServer, PathConfigurationBuilder.build(bdfServer));
            ExtractParameters extractParameters = balayageParameters.getExtractParameters();
            if (lang == null) {
                lang = extractParameters.getExtractionContext().getLangContext().getDefaultLang();
            }
            String balayageType = balayageUnit.getType();
            String extractionPath = balayageUnit.getExtractionPath();
            if (!extractionPath.isEmpty()) {
                extractionDef = BdfBalayageUtils.getExtractionDef(bdfServer, balayageDescription, extractionPath);
            } else {
                extractionDef = BdfBalayageUtils.getDefaultExtractionDef(balayageType);
            }
            if (dynamicObject == null) {
                dynamicObject = subset;
            }
            if (!xmlVersion) {
                if (balayageDef.ignoreTransformation()) {
                    xmlVersion = true;
                } else {
                    BalayageOutput checkedOutput = checkOutput();
                    if (checkedOutput == null) {
                        xmlVersion = true;
                    } else {
                        initTemplateProvider(checkedOutput, lang.toString());
                    }
                }
            }
        }

        private void initTemplateProvider(BalayageOutput output, String lang) {
            InternalTemplateProviderBuilderErrorHandler transformerBuilderErrorHandler = new InternalTemplateProviderBuilderErrorHandler();
            TemplateProviderBuilder templateProviderBuilder = new TemplateProviderBuilder(bdfServer, pathConfiguration, balayageDef, transformerBuilderErrorHandler);
            templateProvider = templateProviderBuilder.build(output);
            if (templateProvider != null) {
                ValueResolver patternValueSet = EngineUtils.getValueResolver(dynamicObject);
                String path = EngineUtils.getPath(output, patternValueSet);
                String fileName = EngineUtils.getFileName(output, patternValueSet, balayageUnit.getType(), true);
                templateProvider.setCurrentFileName(fileName);
                templateProvider.setCurrentPath(path);
                templateProvider.setCurrentLang(lang);
            }
        }

        private boolean initBalayage(String balayageName) {
            int idxtiret = balayageName.indexOf('-');
            if (idxtiret != -1) {
                try {
                    lang = Lang.parse(balayageName.substring(idxtiret + 1));
                } catch (ParseException pe) {
                    return false;
                }
                balayageName = balayageName.substring(0, idxtiret);
            }
            balayageDescription = bdfServer.getBalayageManager().getBalayage(balayageName);
            if (balayageDescription != null) {
                balayageDef = balayageDescription.getBalayageDef();
            }
            return (balayageDescription != null);
        }

        private boolean initBalayageUnit(String balayageUnitName) {
            int idxtiret = balayageUnitName.indexOf('-');
            String outputName = null;
            if (idxtiret != -1) {
                if (idxtiret < 2) {
                    return false;
                }
                outputName = balayageUnitName.substring(idxtiret + 1);
                balayageUnitName = balayageUnitName.substring(0, idxtiret);
            }
            balayageUnit = balayageDef.getBalayageUnit(balayageUnitName);
            if (balayageUnit == null) {
                return false;
            }
            if (outputName != null) {
                output = balayageUnit.getOutput(outputName);
                if (output == null) {
                    return false;
                }
            }
            return true;
        }

        private boolean initSubset(String baseName) {
            if (baseName.endsWith(".xml")) {
                xmlVersion = true;
                baseName = baseName.substring(0, baseName.length() - 4);
            }
            int idxtiret = baseName.indexOf('-');
            boolean needId = false;
            try {
                switch (balayageUnit.getType()) {
                    case BalayageConstants.UNIQUE_TYPE:
                        return baseName.equals("unique");
                    case BalayageConstants.CORPUS_TYPE:
                        if (idxtiret != -1) {
                            return false;
                        }
                        subsetKey = SubsetKey.parse(SubsetKey.CATEGORY_CORPUS, baseName);
                        break;
                    case BalayageConstants.THESAURUS_TYPE:
                        if (idxtiret != -1) {
                            return false;
                        }
                        subsetKey = SubsetKey.parse(SubsetKey.CATEGORY_THESAURUS, baseName);
                        break;
                    case BalayageConstants.FICHE_TYPE:
                        if (idxtiret < 2) {
                            return false;
                        }
                        subsetKey = SubsetKey.parse(SubsetKey.CATEGORY_CORPUS, baseName.substring(0, idxtiret));
                        needId = true;
                        break;
                    case BalayageConstants.MOTCLE_TYPE:
                        if (idxtiret < 2) {
                            return false;
                        }
                        subsetKey = SubsetKey.parse(SubsetKey.CATEGORY_THESAURUS, baseName.substring(0, idxtiret));
                        needId = true;
                        break;
                    default:
                        return false;
                }
            } catch (java.text.ParseException pe) {
                return false;
            }
            subset = bdfServer.getFichotheque().getSubset(subsetKey);
            if (subset == null) {
                return false;
            }
            if (needId) {
                try {
                    int subsetItemId = Integer.parseInt(baseName.substring(idxtiret + 1));
                    if (subsetItemId < 1) {
                        return false;
                    }
                    subsetItem = subset.getSubsetItemById(subsetItemId);
                    if (subsetItem == null) {
                        return false;
                    }
                } catch (NumberFormatException nfe) {
                    return false;
                }
            }
            return true;
        }

        private BalayageOutput checkOutput() {
            if (output != null) {
                return output;
            }
            List<BalayageOutput> outputList = balayageUnit.getOutputList();
            if (outputList.isEmpty()) {
                return null;
            }
            return outputList.get(0);
        }


        private class InternalTemplateProviderBuilderErrorHandler implements TemplateProviderBuilder.ErrorHandler {

            private InternalTemplateProviderBuilderErrorHandler() {
            }

            @Override
            public void transformerError(String xsltPath, BufferErrorListener errorListener) {
                errorKey = "XSLT error : " + errorListener.getErrorMessage();
            }

            @Override
            public void wrongXslPath(String xsltPath) {
                errorKey = "wrong xslt path : " + xsltPath;
            }

            @Override
            public void undefinedXslPath() {
                errorKey = "xslt attribute of output element is not defined";
            }

            @Override
            public void wrongBalayageUnitType() {
            }

        }

    }

}
