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

import fr.exemole.bdfserver.ResourceReference;
import fr.exemole.bdfserver.api.BdfExtensionInitializer;
import fr.exemole.bdfserver.api.BdfServerConstants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.mapeadores.util.io.DocStream;
import net.mapeadores.util.io.ResourceFolder;
import net.mapeadores.util.io.ResourceFolderBuilder;
import net.mapeadores.util.io.ResourceStorage;
import net.mapeadores.util.io.docstream.ClassResourceDocStream;
import net.mapeadores.util.mimetype.MimeTypeResolver;
import net.mapeadores.util.mimetype.MimeTypeUtils;
import net.mapeadores.util.text.RelativePath;


/**
 *
 * @author Vincent Calame
 */
public class DistResourceStorage implements ResourceStorage {

    private final ResourceFolder root;
    private final Map<String, Class> referenceClassMap;

    private DistResourceStorage(ResourceFolder root, Map<String, Class> referenceClassMap) {
        this.root = root;
        this.referenceClassMap = referenceClassMap;
    }

    public static ResourceStorage newInstance(List<BdfExtensionInitializer> bdfExtensionInitializerList) {
        Map<String, Class> referenceClassMap = new HashMap<String, Class>();
        RootBuilder rootBuilder = new RootBuilder();
        rootBuilder.addList(ResourceReference.class, "");
        for (BdfExtensionInitializer initializer : bdfExtensionInitializerList) {
            String startPath = StorageUtils.getExtensionResourcePath(initializer.getRegistrationName(), "");
            Class referenceClass = initializer.getClass();
            rootBuilder.addList(referenceClass, startPath);
            referenceClassMap.put(initializer.getRegistrationName(), referenceClass);
        }
        return new DistResourceStorage(rootBuilder.toResourceFolder(), referenceClassMap);
    }

    @Override
    public String getName() {
        return BdfServerConstants.DIST_STORAGE;
    }

    @Override
    public boolean containsResource(RelativePath path) {
        CheckedPath checkedPath = checkPath(path);
        if (checkedPath == null) {
            return false;
        }
        URL url = checkedPath.getReferenceClass().getResource("resources/" + checkedPath.getPathValue());
        return (url != null);
    }

    @Override
    public DocStream getResourceDocStream(RelativePath path, MimeTypeResolver mimeTypeResolver) {
        CheckedPath checkedPath = checkPath(path);
        if (checkedPath == null) {
            return null;
        }
        ClassResourceDocStream classResourceDocStream = ClassResourceDocStream.newInstance(checkedPath.getReferenceClass(), "resources/" + checkedPath.getPathValue());
        if (classResourceDocStream == null) {
            return null;
        }
        String mimeType = MimeTypeUtils.getMimeType(mimeTypeResolver, checkedPath.getPathValue());
        classResourceDocStream.setMimeType(mimeType);
        return classResourceDocStream;
    }

    @Override
    public ResourceFolder getRoot() {
        return root;
    }

    private CheckedPath checkPath(RelativePath path) {
        String pathValue = checkAlias(path.getPath());
        Class referenceClass = ResourceReference.class;
        if (pathValue.startsWith(StorageUtils.EXTENSION_RESOURCE_PREFIX)) {
            referenceClass = null;
            int idx1 = pathValue.indexOf('/', StorageUtils.EXTENSION_RESOURCE_PREFIX.length());
            if (idx1 != -1) {
                String registrationName = pathValue.substring(StorageUtils.EXTENSION_RESOURCE_PREFIX.length(), idx1);
                pathValue = pathValue.substring(idx1 + 1);
                referenceClass = referenceClassMap.get(registrationName);
            }
        }
        if (referenceClass == null) {
            return null;
        }
        return new CheckedPath(referenceClass, pathValue);
    }


    private static String checkAlias(String pathValue) {
        switch (pathValue) {
            case "css/_common_ficheblock.css":
                return "css/_ficheblockelements.css";
            case "css/_common_ficheblock_odt.css":
                return "css/_ficheblockelements_odt.css";
            case "css/_common_predefinedclasses.css":
                return "css/_predefinedclasses.css";
            default:
                return pathValue;
        }
    }


    private static class CheckedPath {

        private final Class referenceClass;
        private final String pathValue;

        private CheckedPath(Class referenceClass, String pathValue) {
            this.referenceClass = referenceClass;
            this.pathValue = pathValue;
        }

        private Class getReferenceClass() {
            return referenceClass;
        }

        private String getPathValue() {
            return pathValue;
        }

    }


    private static class RootBuilder {

        private final ResourceFolderBuilder rootResourceFolderBuilder = new ResourceFolderBuilder("");

        private RootBuilder() {

        }

        private void addList(Class referenceClass, String startPath) {
            if (referenceClass.getResource("resources/list.txt") != null) {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(referenceClass.getResourceAsStream("resources/list.txt"), "UTF-8"))) {
                    String ligne;
                    while ((ligne = reader.readLine()) != null) {
                        ligne = ligne.trim();
                        if (ligne.length() > 0) {
                            try {
                                addPath(RelativePath.parse(startPath + ligne));
                            } catch (ParseException pe) {

                            }
                        }
                    }
                } catch (IOException ioe) {
                }
            }
        }

        private void addPath(RelativePath relativePath) {
            String[] array = relativePath.toArray();
            int length = array.length;
            if (length > 1) {
                int lastIndex = length - 1;
                ResourceFolderBuilder subfolderBuilder = rootResourceFolderBuilder.getSubfolderBuilder(array[0]);
                for (int i = 1; i < lastIndex; i++) {
                    subfolderBuilder = subfolderBuilder.getSubfolderBuilder(array[i]);
                }
                subfolderBuilder.addResource(array[lastIndex]);
            }
        }


        private ResourceFolder toResourceFolder() {
            return rootResourceFolderBuilder.toRessourceFolder();
        }

    }

}
