/* BdfServer - Copyright (c) 2013-2023 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.docstream;

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.instruction.BdfParameters;
import fr.exemole.bdfserver.api.libscol.LibsColKey;
import fr.exemole.bdfserver.api.users.BdfUser;
import fr.exemole.bdfserver.tools.instruction.DefaultBdfParameters;
import fr.exemole.bdfserver.xml.dyn.Icon32SvgWriter;
import fr.exemole.bdfserver.xml.dyn.LabelsXMLPart;
import fr.exemole.bdfserver.xml.dyn.LibsColXMLPart;
import fr.exemole.bdfserver.xml.dyn.LibsXMLPart;
import fr.exemole.bdfserver.xml.dyn.LogoResourceXMLPart;
import fr.exemole.bdfserver.xml.dyn.UserXMLPart;
import java.io.IOException;
import java.text.ParseException;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.sphere.Redacteur;
import net.fichotheque.utils.SphereUtils;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.io.docstream.StringDocStream;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.mimetype.MimeTypeConstants;
import net.mapeadores.util.text.RelativePath;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.mapeadores.util.xml.XMLUtils;


/**
 *
 * @author Vincent Calame
 */
public final class DynamicDocStreamFactory {

    private final static String[] EMPTY_SUBDIRS = new String[0];

    private DynamicDocStreamFactory() {
    }

    public static DocStream getPublicDocStream(BdfServer bdfServer, String path) {
        DynPath dynPath = toDynPath(path);
        if (dynPath == null) {
            return null;
        }
        switch (dynPath.getMainDir()) {
            case "images":
                return getSvgDocStream(bdfServer, dynPath);
            default:
                return null;
        }
    }

    public static DocStream getAdminDocStream(BdfServer bdfServer, String path) {
        DynPath dynPath = toDynPath(path);
        if (dynPath == null) {
            return null;
        }
        switch (dynPath.getMainDir()) {
            case "libs":
                return getLibsDocStream(bdfServer, dynPath);
            case "libscol":
                return getLibscolDocStream(bdfServer, dynPath);
            case "labels":
                return getLabelsDocStream(bdfServer, dynPath);
            case "labelslist":
                return getLabelsListDocStream(bdfServer, dynPath);
            case "users":
                return getUsersDocStream(bdfServer, dynPath);
            case "logos":
                return getLogosDocStream(bdfServer, dynPath);
            default:
                return null;
        }
    }

    private static DocStream getSvgDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("svg")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String svgText = null;
        switch (dynPath.getBasename()) {
            case "icon32":
                svgText = Icon32SvgWriter.getSvgString(bdfServer);
                break;
        }
        if (svgText == null) {
            return null;
        } else {
            return toDocStream(svgText, MimeTypeConstants.SVG);
        }
    }


    private static DocStream getLibsDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String basename = dynPath.getBasename();
        int idx = basename.indexOf('_');
        if (idx == -1) {
            return null;
        }
        int idx2 = basename.indexOf('_', idx + 1);
        if (idx2 == -1) {
            return null;
        }
        Lang lang;
        try {
            lang = Lang.parse(basename.substring(idx2 + 1));
        } catch (ParseException pe) {
            return null;
        }
        String text;
        String subname = basename.substring(0, idx2);
        if (subname.startsWith("special_")) {
            text = LibsXMLPart.getLibsXML(lang, bdfServer.getFichotheque(), basename.substring(8), true);
        } else {
            try {
                SubsetKey subsetKey = SubsetKey.parse(subname);
                text = LibsXMLPart.getLibsXML(lang, bdfServer.getFichotheque(), subsetKey, true);
            } catch (java.text.ParseException pe) {
                return null;
            }
        }
        return toDocStream(text, MimeTypeConstants.XML);
    }

    private static DocStream getLibscolDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String basename = dynPath.getBasename();
        int idxlang = basename.lastIndexOf('_');
        if (idxlang == -1) {
            return null;
        }
        try {
            Lang lang = Lang.parse(basename.substring(idxlang + 1));
            LibsColKey key = LibsColKey.parse(basename.substring(0, idxlang));
            String text = LibsColXMLPart.getLibsColXML(lang, bdfServer, key, true);
            return toDocStream(text, MimeTypeConstants.XML);
        } catch (ParseException pe) {
            return null;
        }
    }

    private static DocStream getLabelsDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String basename = dynPath.getBasename();
        int langIdx = basename.lastIndexOf('_');
        if (langIdx == -1) {
            return null;
        }
        Lang lang;
        try {
            lang = Lang.parse(basename.substring(langIdx + 1));
        } catch (ParseException pe) {
            return null;
        }
        Fichotheque fichotheque = bdfServer.getFichotheque();
        Subset subset;
        try {
            SubsetKey subsetKey = SubsetKey.parse(basename.substring(0, langIdx));
            subset = fichotheque.getSubset(subsetKey);

        } catch (ParseException pe) {
            return null;
        }
        if (subset == null) {
            return null;
        }
        StringBuilder buf = new StringBuilder(1024);
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf, 0);
        try {
            xmlWriter.appendXMLDeclaration();
            LabelsXMLPart labelsXMLPart = new LabelsXMLPart(xmlWriter, bdfServer, lang);
            labelsXMLPart.appendLabels(subset);
        } catch (IOException ioe) {
        }
        return toDocStream(buf.toString(), MimeTypeConstants.XML);
    }

    private static DocStream getLabelsListDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String basename = dynPath.getBasename();
        int langIdx = basename.lastIndexOf('_');
        if (langIdx == -1) {
            return null;
        }
        Lang lang;
        try {
            lang = Lang.parse(basename.substring(langIdx + 1));
        } catch (ParseException pe) {
            return null;
        }
        Fichotheque fichotheque = bdfServer.getFichotheque();
        String typeString = basename.substring(0, langIdx);
        switch (typeString) {
            case "corpus":
                break;
            default:
                return null;
        }
        StringBuilder buf = new StringBuilder(1024);
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf, 0);
        try {
            xmlWriter.appendXMLDeclaration();
            LabelsXMLPart labelsColXMLPart = new LabelsXMLPart(xmlWriter, bdfServer, lang);
            xmlWriter.openTag("labels-list");
            for (Corpus corpus : fichotheque.getCorpusList()) {
                labelsColXMLPart.appendLabels(corpus);
            }
            xmlWriter.closeTag("labels-list");
        } catch (IOException ioe) {
        }
        return toDocStream(buf.toString(), MimeTypeConstants.XML);
    }

    private static DocStream getUsersDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.getSubDirsLength() != 2) {
            return null;
        }
        if (!dynPath.getSubDir(1).equals("ui")) {
            return null;
        }
        String baseName = dynPath.getBasename();
        boolean isAll = false;
        Corpus specificCorpus;
        if (baseName.equals("_all")) {
            isAll = true;
            specificCorpus = null;
        } else {
            try {
                SubsetKey subsetKey = SubsetKey.parse(baseName);
                Subset subset = bdfServer.getFichotheque().getSubset(subsetKey);
                if ((subset != null) && (subsetKey.isCorpusSubset())) {
                    specificCorpus = (Corpus) subset;
                } else {
                    return null;
                }
            } catch (ParseException pe) {
                return null;
            }
        }
        StringBuilder buf = new StringBuilder(1024);
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf, 0);
        if (dynPath.getSubDir(0).equals("_default")) {
            try {
                xmlWriter.appendXMLDeclaration();
                UserXMLPart.writeDefaultUser(xmlWriter);
            } catch (IOException ioe) {
            }
        } else {
            Redacteur redacteur;
            try {
                redacteur = SphereUtils.parse(bdfServer.getFichotheque(), dynPath.getSubDir(0));
            } catch (SphereUtils.RedacteurLoginException rle) {
                return null;
            }
            BdfUser bdfUser = bdfServer.createBdfUser(redacteur);
            BdfParameters bdfParameters = new DefaultBdfParameters(bdfServer, bdfUser);
            PermissionSummary permissionSummary = bdfParameters.getPermissionSummary();
            try {
                xmlWriter.appendXMLDeclaration();
                UserXMLPart userXMLPart = new UserXMLPart(xmlWriter, bdfServer, bdfUser, permissionSummary);
                userXMLPart.start();
                if (isAll) {
                    for (Corpus corpus : bdfServer.getFichotheque().getCorpusList()) {
                        userXMLPart.addCorpusUi(corpus);
                    }
                } else {
                    userXMLPart.addCorpusUi(specificCorpus);
                }
                userXMLPart.end();
            } catch (IOException ioe) {
            }
        }
        return toDocStream(buf.toString(), MimeTypeConstants.XML);
    }

    private static DocStream getLogosDocStream(BdfServer bdfServer, DynPath dynPath) {
        if (!dynPath.getExtension().equals("xml")) {
            return null;
        }
        if (dynPath.hasSubDirs()) {
            return null;
        }
        String basename = dynPath.getBasename();
        switch (basename) {
            case "odt":
                break;
            default:
                return null;
        }
        StringBuilder buf = new StringBuilder(1024);
        AppendableXMLWriter xmlWriter = XMLUtils.toXMLWriter(buf, 0);
        try {
            xmlWriter.appendXMLDeclaration();
            LogoResourceXMLPart logoXMLPart = new LogoResourceXMLPart(xmlWriter, bdfServer);
            logoXMLPart.appendForDestination(basename);
        } catch (IOException ioe) {
        }
        return toDocStream(buf.toString(), MimeTypeConstants.XML);
    }

    private static DocStream toDocStream(String s, String mimeType) {
        if (s == null) {
            return null;
        }
        return new StringDocStream(s, mimeType);
    }

    private static DynPath toDynPath(String filePath) {
        String[] parseResult = parse(filePath);
        if (parseResult == null) {
            return null;
        }
        int length = parseResult.length;
        if (length < 2) {
            return null;
        }
        String mainDir = parseResult[0];
        String fileName = parseResult[length - 1];
        int idx = fileName.lastIndexOf('.');
        if ((idx < 1) || (idx == (fileName.length() - 1))) {
            return null;
        }
        String basename = fileName.substring(0, idx);
        String extension = fileName.substring(idx + 1);
        String[] subDirs;
        if (length > 2) {
            subDirs = new String[length - 2];
            System.arraycopy(parseResult, 1, subDirs, 0, length - 2);
        } else {
            subDirs = EMPTY_SUBDIRS;
        }
        return new DynPath(mainDir, subDirs, basename, extension);
    }

    private static String[] parse(String filePath) {
        int idx = filePath.indexOf('/');
        if (idx == -1) {
            int idx2 = filePath.indexOf('_');
            if ((idx2 > 0) && (idx2 < (filePath.length() - 1))) {
                String[] result = new String[2];
                result[0] = filePath.substring(0, idx2);
                result[1] = filePath.substring(idx2 + 1);
                return result;
            } else {
                return null;
            }
        } else {
            try {
                RelativePath relativePath = RelativePath.parse(filePath);
                String[] result = relativePath.toArray();
                if (result.length < 2) {
                    return null;
                } else {
                    return result;
                }
            } catch (ParseException pe) {
                return null;
            }
        }
    }


    private static class DynPath {

        private final String mainDir;
        private final String[] subDirs;
        private final String basename;
        private final String extension;

        private DynPath(String mainDir, String[] subDirs, String basename, String extension) {
            this.mainDir = mainDir;
            this.subDirs = subDirs;
            this.basename = basename;
            this.extension = extension;
        }

        private String getMainDir() {
            return mainDir;
        }

        private int getSubDirsLength() {
            return subDirs.length;
        }

        private String getSubDir(int index) {
            return subDirs[index];
        }

        private String getExtension() {
            return extension;
        }

        private boolean hasSubDirs() {
            return (subDirs.length > 0);
        }

        private String getBasename() {
            return basename;
        }

    }

}
