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

import net.mapeadores.util.io.ResourceFolder;
import net.mapeadores.util.io.ResourceStorage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import net.mapeadores.util.json.JSONWriter;


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

    public final static String BINARY_TYPE = "binary";
    public final static String IMAGE_TYPE = "image";
    public final static String TEXT_TYPE = "text";

    private ResourceTreeJson() {

    }

    public static void properties(JSONWriter jw, List<ResourceStorage> resourceStorageList) throws IOException {
        InternalFolder root = mergeTrees(resourceStorageList);
        addFolder(jw, root);
    }

    private static void addFolder(JSONWriter jw, InternalFolder folder) throws IOException {
        Collection<InternalFolder> subfolders = folder.getSubfolders();
        jw.key("subfolderArray");
        jw.array();
        for (InternalFolder subfolder : subfolders) {
            jw.object();
            {
                jw.key("name")
                        .value(subfolder.name);
                jw.key("origins");
                jw.array();
                for (String origin : subfolder.getOriginList()) {
                    jw.value(origin);
                }
                jw.endArray();
                addFolder(jw, subfolder);
            }
            jw.endObject();
        }
        jw.endArray();
        Collection<ResourceInfo> resourceInfos = folder.getResourceInfos();
        jw.key("resourceArray");
        jw.array();
        for (ResourceInfo resourceInfo : resourceInfos) {
            String name = resourceInfo.getName();
            jw.object();
            {
                jw.key("name")
                        .value(name);
                jw.key("type")
                        .value(resourceInfo.getType());
                jw.key("origins");
                jw.array();
                for (String origin : resourceInfo.getOriginList()) {
                    jw.value(origin);
                }
                jw.endArray();
            }
            jw.endObject();
        }
        jw.endArray();
    }

    private static InternalFolder mergeTrees(List<ResourceStorage> resourceStorageList) {
        InternalFolder root = new InternalFolder("");
        for (ResourceStorage resourceStorage : resourceStorageList) {
            root.addResourceFolder(resourceStorage.getRoot(), resourceStorage.getName());
        }
        return root;
    }


    private static class InternalFolder {

        private final SortedMap<String, InternalFolder> nodeMap = new TreeMap<String, InternalFolder>();
        private final SortedMap<String, ResourceInfo> resourceInfoMap = new TreeMap<String, ResourceInfo>();
        private final List<String> originList = new ArrayList<String>();
        private final String name;

        private InternalFolder(String name) {
            this.name = name;
        }

        private Collection<ResourceInfo> getResourceInfos() {
            return resourceInfoMap.values();
        }

        private Collection<InternalFolder> getSubfolders() {
            return nodeMap.values();
        }

        private void addOrigin(String origin) {
            originList.add(origin);
        }

        private List<String> getOriginList() {
            return originList;
        }

        private void addResourceFolder(ResourceFolder folder, String origin) {
            for (String resourceName : folder.getResourceNameList()) {
                ResourceInfo resourceInfo = resourceInfoMap.get(resourceName);
                if (resourceInfo == null) {
                    resourceInfo = new ResourceInfo(resourceName);
                    resourceInfoMap.put(resourceName, resourceInfo);
                }
                resourceInfo.addOrigin(origin);
            }
            for (ResourceFolder subfolder : folder.getSubfolderList()) {
                String subfolderName = subfolder.getName();
                InternalFolder internalFolder = nodeMap.get(subfolderName);
                if (internalFolder == null) {
                    internalFolder = new InternalFolder(subfolderName);
                    nodeMap.put(subfolderName, internalFolder);
                }
                internalFolder.addOrigin(origin);
                internalFolder.addResourceFolder(subfolder, origin);
            }
        }

    }


    private static class ResourceInfo {

        private final String name;
        private final String type;
        private final List<String> originList = new ArrayList<String>();

        private ResourceInfo(String name) {
            this.name = name;
            int idx = name.lastIndexOf(".");
            if (idx != -1) {
                this.type = getMatchingType(name.substring(idx + 1));
            } else {
                this.type = BINARY_TYPE;
            }
        }

        private String getName() {
            return name;
        }

        private String getType() {
            return type;
        }

        private void addOrigin(String origin) {
            originList.add(origin);
        }

        private List<String> getOriginList() {
            return originList;
        }

    }

    private static String getMatchingType(String extension) {
        switch (extension) {
            case "txt":
            case "js":
            case "css":
            case "xml":
            case "xsl":
            case "dtd":
            case "html":
            case "ini":
            case "properties":
            case "json":
            case "yaml":
                return TEXT_TYPE;
            case "png":
            case "gif":
            case "jpg":
            case "jpeg":
                return IMAGE_TYPE;
            default:
                return BINARY_TYPE;
        }
    }

}
