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

import fr.exemole.bdfserver.api.BdfServer;
import fr.exemole.bdfserver.api.configuration.PathConfiguration;
import fr.exemole.bdfserver.api.interaction.GetConstants;
import fr.exemole.bdfserver.api.storage.StorageContent;
import fr.exemole.bdfserver.api.storage.TemplateStorage;
import fr.exemole.bdfserver.tools.BdfURI;
import fr.exemole.bdfserver.tools.configuration.PathConfigurationBuilder;
import fr.exemole.bdfserver.tools.exportation.transformation.BdfTransformationUtils;
import fr.exemole.bdfserver.xml.DocStreamXMLPackWriter;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import net.fichotheque.SubsetKey;
import net.fichotheque.album.Album;
import net.fichotheque.album.AlbumConstants;
import net.fichotheque.album.Illustration;
import net.fichotheque.album.metadata.AlbumDim;
import net.fichotheque.exportation.transformation.TemplateKey;
import net.fichotheque.exportation.transformation.TransformationKey;
import net.mapeadores.util.exceptions.NestedIOException;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.io.docstream.FileDocStream;
import net.mapeadores.util.io.docstream.StringDocStream;
import net.mapeadores.util.logging.ErrorMessageException;
import net.mapeadores.util.mimetype.MimeTypeConstants;
import net.mapeadores.util.mimetype.MimeTypeUtils;
import net.mapeadores.util.text.RelativePath;
import net.mapeadores.util.text.ValidExtension;


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

    private DocStreamFactory() {
    }

    public static DocStream buildDocStream(BdfServer bdfServer, PathConfiguration pathConfiguration, URI uri) {
        try {
            return parseDocStream(bdfServer, pathConfiguration, uri);
        } catch (ErrorMessageException e) {
            return null;
        }
    }

    public static DocStream buildDocStream(BdfServer bdfServer, URI uri) {
        try {
            return parseDocStream(bdfServer, uri);
        } catch (ErrorMessageException e) {
            return null;
        }
    }

    public static DocStream parseDocStream(BdfServer bdfServer, URI uri) throws ErrorMessageException {
        return parseDocStream(bdfServer, PathConfigurationBuilder.build(bdfServer), uri);
    }

    public static DocStream parseDocStream(BdfServer bdfServer, PathConfiguration pathConfiguration, URI uri) throws ErrorMessageException {
        if (!uri.isAbsolute()) {
            return null;
        }
        if (uri.isOpaque()) {
            return null;
        }
        String scheme = uri.getScheme();
        if (!scheme.equals("bdf")) {
            return null;
        }
        uri = uri.normalize();
        String path = uri.getPath();
        if ((path == null) || (path.length() == 1)) {
            throw new ErrorMessageException("_ error.unsupported.dirlist", "/");
        }
        path = path.substring(1);
        path = checkAlias(path);
        int idx = path.indexOf('/');
        if (idx == -1) {
            throw new ErrorMessageException("_ error.unsupported.dirlist", path);
        }
        String rootDir = path.substring(0, idx);
        String filePath = path.substring(idx + 1);
        switch (rootDir) {
            case GetConstants.BALAYAGECONTENTS_ROOT:
            case "balayage":
                return getBalayageContentDocStream(bdfServer, filePath);
            case "dyn":
                return DynamicDocStreamFactory.getAdminDocStream(bdfServer, filePath);
            case "dyn-pub":
                return DynamicDocStreamFactory.getPublicDocStream(bdfServer, filePath);
            case GetConstants.ILLUSTRATIONS_ROOT:
                return getIllustrationDocStream(bdfServer, filePath);
            case "inc":
                return getOutputDocStream(bdfServer, "balayages/_inc_/" + filePath);
            case "output":
                return getOutputDocStream(bdfServer, filePath);
            case "pub":
                return getPublicDocStream(bdfServer, pathConfiguration, filePath);
            case GetConstants.TABLEEXPORT_ROOT:
                return getTableExportContentDocStream(bdfServer, filePath);
            case GetConstants.TRANSFORMATIONS_ROOT:
            case "transformation":
                return getTransformationContentDocStream(bdfServer, filePath);
            case "xml-pack":
                return getXmlPackDocStream(bdfServer, pathConfiguration, filePath);
            default:
                try {
                RelativePath relativePath = RelativePath.parse(rootDir + "/" + filePath);
                return bdfServer.getResourceStorages().getResourceDocStream(relativePath);
            } catch (ParseException pe) {
                return null;
            }
        }
    }

    private static DocStream getXmlPackDocStream(BdfServer bdfServer, PathConfiguration pathConfiguration, String filePath) throws ErrorMessageException {
        URI uri = BdfURI.toAbsoluteBdfURI(filePath);
        DocStream docStream = parseDocStream(bdfServer, pathConfiguration, uri);
        if (docStream == null) {
            if (filePath.equals("css/fiche.css")) {
                docStream = mergeFicheCss(bdfServer);
            } else {
                return null;
            }
        }
        StringBuilder buf = new StringBuilder();
        DocStreamXMLPackWriter xmlWriter = new DocStreamXMLPackWriter();
        xmlWriter.setAppendable(buf);
        try {
            xmlWriter.appendXMLDeclaration();
            xmlWriter.appendDocStream(docStream, filePath);
        } catch (IOException ioe) {
            throw new NestedIOException(ioe);
        }
        return new StringDocStream(buf.toString(), MimeTypeConstants.XML);
    }

    private static DocStream getTransformationContentDocStream(BdfServer bdfServer, String filePath) {
        int idx1 = filePath.indexOf('/');
        if (idx1 == -1) {
            return null;
        }
        TransformationKey transformationKey;
        try {
            transformationKey = TransformationKey.parse(filePath.substring(0, idx1));
        } catch (ParseException pe) {
            return null;
        }
        int idx2 = filePath.indexOf('/', idx1 + 1);
        if (idx2 == -1) {
            return null;
        }
        String templateName = filePath.substring(idx1 + 1, idx2);
        String extension = null;
        String contentName = filePath.substring(idx2 + 1);
        int extidx = templateName.lastIndexOf('.');
        if (extidx != -1) {
            extension = templateName.substring(extidx + 1);
            templateName = templateName.substring(0, extidx);
        }
        String mimeType = MimeTypeUtils.getMimeType(bdfServer.getMimeTypeResolver(), contentName);
        if (templateName.equals(TemplateKey.DEFAULT_NAME)) {
            TemplateStorage.Unit unit;
            if (extension == null) {
                unit = BdfTransformationUtils.getHtmlDefaultUnit(bdfServer, transformationKey);
            } else {
                unit = BdfTransformationUtils.getStreamDefaultUnit(bdfServer, transformationKey, extension);
            }
            if (unit != null) {
                for (StorageContent storageContent : unit.getStorageContentList()) {
                    if (storageContent.getPath().equals(contentName)) {
                        return new StorageContentDocStream(storageContent, mimeType);
                    }
                }
            }
            return null;
        } else {
            TemplateKey templateKey;
            if (extension == null) {
                try {
                    templateKey = TemplateKey.parse(transformationKey, templateName);
                } catch (ParseException pe) {
                    return null;
                }
            } else {
                try {
                    ValidExtension validExtension = ValidExtension.parse(extension);
                    templateKey = TemplateKey.parse(transformationKey, validExtension, templateName);
                } catch (ParseException pe) {
                    return null;
                }
            }
            StorageContent storageContent = bdfServer.getTransformationManager().getTemplateStorageContent(templateKey, contentName);
            if (storageContent != null) {
                return new StorageContentDocStream(storageContent, mimeType);
            } else {
                return null;
            }
        }
    }


    private static DocStream getIllustrationDocStream(BdfServer bdfServer, String filePath) {
        int idx1 = filePath.indexOf('/');
        String dimString = null;
        String illustrationName;
        if (idx1 != -1) {
            if (filePath.indexOf('/', idx1 + 1) != -1) {
                return null;
            }
            dimString = filePath.substring(0, idx1);
            illustrationName = filePath.substring(idx1 + 1);
        } else {
            illustrationName = filePath;
        }
        int idx2 = illustrationName.indexOf('-');
        if (idx2 == -1) {
            return null;
        }
        int idx3 = illustrationName.indexOf('.');
        if (idx3 != -1) {
            if (idx3 < idx2) {
                return null;
            } else {
                illustrationName = illustrationName.substring(0, idx3);
            }
        }
        SubsetKey albumKey;
        try {
            albumKey = SubsetKey.parse(SubsetKey.CATEGORY_ALBUM, illustrationName.substring(0, idx2));
        } catch (ParseException pe) {
            return null;
        }
        int illustrationid;
        try {
            illustrationid = Integer.parseInt(illustrationName.substring(idx2 + 1));
        } catch (NumberFormatException nfe) {
            return null;
        }
        Album album = (Album) bdfServer.getFichotheque().getSubset(albumKey);
        if (album == null) {
            return null;
        }
        Illustration illustration = album.getIllustrationById(illustrationid);
        if (illustration == null) {
            return null;
        }
        AlbumDim albumDim;
        if (dimString == null) {
            return new IllustrationDocStream(illustration, AlbumConstants.ORIGINAL_SPECIALDIM);
        } else {
            if (dimString.equals("_mini")) {
                return new IllustrationDocStream(illustration, AlbumConstants.MINI_SPECIALDIM);
            } else {
                albumDim = album.getAlbumMetadata().getAlbumDimByName(dimString);
                if (albumDim == null) {
                    return null;
                } else {
                    return new IllustrationDocStream(illustration, albumDim);
                }
            }
        }
    }

    private static DocStream getTableExportContentDocStream(BdfServer bdfServer, String filePath) {
        int index = filePath.indexOf('/');
        if (index == -1) {
            return null;
        }
        String tableExportName = filePath.substring(0, index);
        String contentPath = filePath.substring(index + 1);
        String content = bdfServer.getTableExportManager().getTableExportContent(tableExportName, contentPath);
        if (content == null) {
            return null;
        }
        String mimeType = MimeTypeUtils.getMimeType(bdfServer.getMimeTypeResolver(), contentPath);
        if (mimeType.equals(MimeTypeConstants.OCTETSTREAM)) {
            mimeType = MimeTypeConstants.PLAIN;
        }
        return new StringDocStream(content, mimeType);
    }

    private static DocStream getBalayageContentDocStream(BdfServer bdfServer, String filePath) {
        int index = filePath.indexOf('/');
        if (index == -1) {
            return null;
        }
        String balayageName = filePath.substring(0, index);
        String contentPath = filePath.substring(index + 1);
        String content = bdfServer.getBalayageManager().getBalayageContent(balayageName, contentPath);
        if (content == null) {
            return null;
        }
        String mimeType = MimeTypeUtils.getMimeType(bdfServer.getMimeTypeResolver(), contentPath);
        if (mimeType.equals(MimeTypeConstants.OCTETSTREAM)) {
            mimeType = MimeTypeConstants.PLAIN;
        }
        return new StringDocStream(content, mimeType);
    }

    private static DocStream getPublicDocStream(BdfServer bdfServer, PathConfiguration pathConfiguration, String filePath) {
        File publicDirectory = pathConfiguration.getPublicDirectory();
        File f = new File(publicDirectory, filePath);
        if (!f.exists()) {
            return null;
        }
        String mimeType = MimeTypeUtils.getMimeType(bdfServer.getMimeTypeResolver(), f.getName());
        FileDocStream fileDocStream = new FileDocStream(f);
        fileDocStream.setMimeType(mimeType);
        return fileDocStream;
    }

    private static DocStream getOutputDocStream(BdfServer bdfServer, String filePath) {
        RelativePath relativePath;
        try {
            relativePath = RelativePath.parse(filePath);
        } catch (ParseException pe) {
            return null;
        }
        File f = bdfServer.getOutputStorage().getFile(relativePath);
        if (!f.exists()) {
            return null;
        }
        if (f.isDirectory()) {
            return null;
        }
        String mimeType = MimeTypeUtils.getMimeType(bdfServer.getMimeTypeResolver(), f.getName());
        FileDocStream fileDocStream = new FileDocStream(f);
        fileDocStream.setMimeType(mimeType);
        return fileDocStream;
    }

    private static String checkAlias(String path) {
        if (path.startsWith("dyn/import")) {
            return "xslt/_" + path.substring(4);
        }
        return path;
    }

    private static DocStream mergeFicheCss(BdfServer bdfServer) {
        StringBuilder buf = new StringBuilder();
        String[] paths = {"css/fiche-elements.css", "css/fiche-classes.css"};
        for (String path : paths) {
            RelativePath relativePath = RelativePath.build(path);
            DocStream docStream = bdfServer.getResourceStorages().getResourceDocStream(relativePath);
            if (docStream != null) {
                buf.append(docStream.getContent());
                buf.append("\n");
            }
        }
        return new StringDocStream(buf.toString(), MimeTypeConstants.CSS);
    }

}
