package de.renew.plugin.jpms;

import java.lang.module.ModuleFinder;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * A  factory  to create new {@link ComponentLayer}s. It's  factory  method  is
 * {@link #create}.  The  primary  purpose of this interface  is  to  hide  the
 * implementation details of the component creation process.
 *
 * @author Kjell Ehlers
 * @since Renew 4.2
 */
public sealed interface ComponentLayerFactory permits AbstractLayerFactory {

    /**
     * Creates a new {@link ComponentLayer}.
     * <p>
     * This  operation uses the template method pattern to allow subclasses  to
     * override the building steps while maintaining a set core algorithm. It's
     * only meant to be implemented by {@link AbstractLayerFactory}. Said steps
     * are specified by the {@link Facilitator} interface.
     * <p>
     * <b>The  result  is  optional  and thus  may  not  be  present.</b>  More
     * precisely,  no value will be present if a subclass returns  null  during
     * the {@link Facilitator#createComponentLayer createComponentLayer} step.
     *
     * @param  name the name to be used in the creation of the component layer,
     *              may be null. Furthermore, subclasses may opt to ignore this
     *              hint.
     * @param  moduleJarPaths paths  denoting  the  modular jar  files,  to  be
     *                        included  in  this component. Each jar  file  may
     *                        only include a single module. May not be null.
     * @param  dependencyLocations paths  where dependent modules can be found.
     * @param  loadedLayers all  currently  loaded components.  Must  at  least
     *                      include the parent layers of this component.
     * @param  parentCL the class loader to be used as a parent for the created
     *                  component's class loader(s).
     * @return A new non-null possibly-empty {@code ComponentLayer}.
     * @throws ModuleLayerCreationException
     *         if  the creation of the component fails due to conflicts in  the
     *         loading order of the contained modules.
     */
    Optional<ComponentLayer> create(
        String name, Set<Path> moduleJarPaths, Set<Path> dependencyLocations,
        List<ComponentLayer> loadedLayers, ClassLoader parentCL)
        throws ModuleLayerCreationException;

    /**
     * A collection of steps needed to create a {@code ComponentLayer}.
     * <p>
     * This  interface  encapsulates the aforementioned templated  build  steps
     * used in a {@link ComponentLayerFactory} to facilitate the creation of  a
     * new {@code ComponentLayer}. It's not supposed to be implemented directly
     * but rather intended to be subclassed from {@link AbstractLayerFactory}.
     *
     * @author Kjell Ehlers
     * @since Renew 4.2
     */
    sealed interface Facilitator permits AbstractLayerFactory {

        /**
         * Creates a new {@link ComponentLayer}.
         *
         * @param  name the  name to be used in the creation of  the  component
         *              layer,  may be {@code null}. The actual resulting  name
         *              of the new component is implementation dependent.
         * @param  control the controller obj that governs the new  component's
         *                 module layer.
         * @param  parents the parent components of the new layer.
         * @return A new possibly-null {@code ComponentLayer}.
         */
        ComponentLayer createComponentLayer(
            String name, ModuleLayer.Controller control, List<ComponentLayer> parents);

        /**
         * Creates  a  module  finder able to locate the  modules  of  the  new
         * {@code ComponentLayer}.  When trying to locate possible dependencies
         * for   these  modules,  their  location  needs  to  be  provided with
         * <code>dependencyLocations</code>.
         *
         * @param  modulesToLoad paths  denoting the modular jar files,  to  be
         *                       included  in  the new  {@code ComponentLayer}.
         *                       May not be {@code null}.
         * @param  dependencyLocations paths  where  dependent  modules  can be
         *                             found.
         * @return A new {@code ModuleFinder} for the component.
         */
        ModuleFinder createModuleFinder(Set<Path> modulesToLoad, Set<Path> dependencyLocations);
    }
}
