/* BDF - Copyright (c) 2010-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.servlets;

import fr.exemole.bdfserver.api.BdfExtensionInitializer;
import fr.exemole.bdfserver.api.BdfServerConstants;
import fr.exemole.bdfserver.api.storage.BdfStorageException;
import fr.exemole.bdfserver.conf.BdfServerDirs;
import fr.exemole.bdfserver.conf.ConfConstants;
import fr.exemole.bdfserver.conf.WebappDirs;
import fr.exemole.bdfserver.storage.directory.StorageFactory;
import fr.exemole.bdfserver.tools.storage.DirectoryEditableResourceStorage;
import fr.exemole.bdfserver.tools.storage.DistResourceStorage;
import fr.exemole.bdfserver.tools.storage.Storages;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import net.mapeadores.util.buildinfo.BuildInfo;
import net.mapeadores.util.buildinfo.BuildInfoParser;
import net.mapeadores.util.exceptions.ImplementationException;
import net.mapeadores.util.exceptions.InitException;
import net.mapeadores.util.exceptions.InternalResourceException;
import net.mapeadores.util.io.DirectoryResourceStorage;
import net.mapeadores.util.io.FileUtils;
import net.mapeadores.util.io.ResourceStorage;
import net.mapeadores.util.io.ResourceStorages;
import net.mapeadores.util.io.ResourceUtils;
import net.mapeadores.util.logging.SourceLogsBuilder;


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

    private BDFInit() {

    }

    public static BuildInfo buildInfo() {
        if (BDFInit.class.getResource("VERSION") != null) {
            try (InputStream is = BDFInit.class.getResourceAsStream("VERSION")) {
                BuildInfo buildInfo = BuildInfoParser.parse(is);
                return buildInfo;
            } catch (IOException ioe) {
                throw new InternalResourceException(ioe);
            }
        } else {
            return null;
        }
    }

    public static Storages storages(BdfServerDirs dirs, ResourceStorages webappsResourceStorages) {
        checkBalayage(dirs);
        SourceLogsBuilder logsBuilder = new SourceLogsBuilder();
        Storages storages = StorageFactory.newInstance(dirs, logsBuilder);
        storages
                .setInitLogs(logsBuilder.toSourceLogs())
                .setResourceStorages(initBdfServerResourceStorages(dirs, webappsResourceStorages));
        return storages;
    }

    public static List<BdfExtensionInitializer> extensionInitializerList(WebappDirs webappDirs) {
        File dir = webappDirs.getDir(ConfConstants.LIB_EXTENSIONS);
        if ((dir == null) || (!dir.exists())) {
            return Collections.emptyList();
        }
        List<BdfExtensionInitializer> bdfExtensionInitializerList;
        try {
            bdfExtensionInitializerList = extensionInitializerList(dir);
        } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new InitException(e);
        }
        return bdfExtensionInitializerList;
    }

    private static List<BdfExtensionInitializer> extensionInitializerList(File dir) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        Map<String, String> existingRegistrationNameMap = new HashMap<String, String>();
        String intializerClassName = BdfExtensionInitializer.class.getName();
        List<URL> urlList = new ArrayList<URL>();
        List<File> fileList = new ArrayList<File>();
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                continue;
            }
            if (!file.getName().endsWith(".jar")) {
                continue;
            }
            urlList.add(file.toURI().toURL());
            fileList.add(file);
        }
        if (urlList.isEmpty()) {
            return Collections.emptyList();
        }
        List<BdfExtensionInitializer> bdfExtensionInitializerList = new ArrayList<BdfExtensionInitializer>();
        URLClassLoader loader = new URLClassLoader(urlList.toArray(new URL[urlList.size()]), BDFInit.class.getClassLoader());
        Enumeration enumeration;
        String tmp;
        Class tmpClass;
        for (File file : fileList) {
            JarFile jar = new JarFile(file.getAbsolutePath());
            enumeration = jar.entries();
            while (enumeration.hasMoreElements()) {
                tmp = enumeration.nextElement().toString();
                if (tmp.length() > 6 && tmp.substring(tmp.length() - 6).compareTo(".class") == 0) {
                    tmp = tmp.substring(0, tmp.length() - 6);
                    tmp = tmp.replaceAll("/", ".");
                    tmpClass = Class.forName(tmp, true, loader);
                    for (Class interfaceClass : tmpClass.getInterfaces()) {
                        if (interfaceClass.getName().equals(intializerClassName)) {
                            try {
                                BdfExtensionInitializer initializer = (BdfExtensionInitializer) tmpClass.getDeclaredConstructor().newInstance();
                                String registrationName = initializer.getRegistrationName();
                                String current = existingRegistrationNameMap.get(registrationName);
                                if (current != null) {
                                    throw new InitException("same registration name: " + registrationName + " (" + current + " / " + initializer.getClass().getName() + ")");
                                } else {
                                    existingRegistrationNameMap.put(registrationName, initializer.getClass().getName());
                                }
                                bdfExtensionInitializerList.add(initializer);
                            } catch (ReflectiveOperationException roe) {
                                throw new ImplementationException("Class " + tmpClass.getCanonicalName() + " has no public constructor without parameters");
                            }
                        }
                    }
                }
            }
        }
        return bdfExtensionInitializerList;
    }

    public static ResourceStorages initWebappResourceStorages(WebappDirs webappDirs, List<BdfExtensionInitializer> bdfExtensionInitializerList) {
        File resourceEtcDir = webappDirs.getDir(ConfConstants.ETC_RESOURCES);
        ResourceStorage[] array = new ResourceStorage[2];
        array[0] = new DirectoryResourceStorage(BdfServerConstants.ETC_STORAGE, resourceEtcDir);
        array[1] = DistResourceStorage.newInstance(bdfExtensionInitializerList);
        return ResourceUtils.toResourceStorages(array);
    }

    private static ResourceStorages initBdfServerResourceStorages(BdfServerDirs dirs, ResourceStorages webappsResourceStorages) {
        String subdirName = "rscdata";
        File resourceVarDir = dirs.getSubPath(ConfConstants.VAR_DATA, subdirName);
        File resourceBackupDir = dirs.getSubPath(ConfConstants.VAR_BACKUP, subdirName);
        testOldVersion(resourceVarDir, resourceBackupDir);
        List<ResourceStorage> list = new ArrayList<ResourceStorage>();
        list.add(new DirectoryEditableResourceStorage(BdfServerConstants.VAR_STORAGE, resourceVarDir, resourceBackupDir));
        list.addAll(webappsResourceStorages);
        return ResourceUtils.toResourceStorages(list.toArray(new ResourceStorage[list.size()]));
    }


    /*
     * Avant la révision 798 : les fichiers logos étaient directement placés dans le répertoire => on les déplace vers le répertoire images
     * Avant la révision 1198 : les répertoires commençaient par un trait de soulignement (exemple : _rsc : on l'enlève)
     * Avant la révision 3093 : on renomme rsc en images
     */
    private static void testOldVersion(File varRscDirectory, File backupRscDirectory) {
        if (varRscDirectory.exists()) {
            File[] fileArray = varRscDirectory.listFiles();
            int length = fileArray.length;
            for (int i = 0; i < length; i++) { //r1198
                File f = fileArray[i];
                if (f.isDirectory()) {
                    String name = f.getName();
                    if ((name.startsWith("_")) && (name.length() > 1)) {
                        f.renameTo(new File(varRscDirectory, name.substring(1)));
                    }

                }
            }
            File rscDir = new File(varRscDirectory, "rsc");
            File imagesDir = new File(varRscDirectory, "images"); // r3093
            if (rscDir.exists()) {
                rscDir.renameTo(imagesDir);
            }
            for (int i = 0; i < length; i++) { //r798
                File f = fileArray[i];
                if (!f.isDirectory()) {
                    String name = f.getName();
                    if ((name.endsWith(".png")) || (name.endsWith(".jpg"))) {
                        if (!imagesDir.exists()) {
                            imagesDir.mkdir();
                        }
                        f.renameTo(new File(imagesDir, name));
                    }
                }
            }
        }
        if (backupRscDirectory.exists()) {
            File[] fileArray = backupRscDirectory.listFiles();
            int length = fileArray.length;
            for (int i = 0; i < length; i++) { //r1198
                File f = fileArray[i];
                if (f.isDirectory()) {
                    String name = f.getName();
                    if ((name.startsWith("_")) && (name.length() > 1)) {
                        f.renameTo(new File(backupRscDirectory, name.substring(1)));
                    }

                }
            }
            File rscDir = new File(backupRscDirectory, "rsc");
            File imagesDir = new File(backupRscDirectory, "images"); // r3093
            if (rscDir.exists()) {
                rscDir.renameTo(imagesDir);
            }
        }
    }

    /*
     * Avant la révision 3888, les balayages étaient stockés dans etc, cette fonction les transfère si balayagedata est vide
     */
    private static void checkBalayage(BdfServerDirs dirs) {
        File varBalayageDir = dirs.getSubPath(ConfConstants.VAR_DATA, "balayagedata");
        if (!varBalayageDir.exists()) {
            String oldBalayagesPath;
            if (dirs.getName().equals(ConfConstants.UNIQUE_NAME)) {
                oldBalayagesPath = "balayages";
            } else {
                oldBalayagesPath = dirs.getName() + "/balayages";
            }
            File etcBalayageDir = dirs.getSubPath(ConfConstants.ETC_ROOT, oldBalayagesPath);
            if (etcBalayageDir.exists()) {
                try {
                    FileUtils.copyDirectory(etcBalayageDir, varBalayageDir, true);
                } catch (IOException ioe) {
                    throw new BdfStorageException(ioe);
                }
            }
        }

    }

}
