package net.ranides.assira.reflection;

import net.ranides.assira.reflection.impl.FClassLoader;

import java.io.IOException;
import java.nio.file.Path;

/**
 * This interface is extension of ClassLoader.
 *
 * It adds support for named classloaders.
 * It adds support for defining classes on the fly.
 */
public interface IClassLoader {

    /**
     * Returns default IClassLoader which has system class loader as parent.
     *
     * This classloader is always defined and has name "system"
     *
     * @return IClassLoader singleton
     */
    static IClassLoader getSystem() {
        return FClassLoader.getSystem();
    }

    /**
     * Returns IClassLoader with provided name and parent.
     *
     * New instance will be created only if it is not already created.
     *
     * @param name name
     * @param parent parent
     * @return IClassLoader singleton
     */
    static IClassLoader getInstance(String name, IClassLoader parent) {
        return FClassLoader.getInstance(name, parent.reflective());
    }

    /**
     * Returns IClassLoader with provided name and parent.
     *
     * New instance will be created only if it is not already created.
     *
     * @param name name
     * @param parent parent
     * @return IClassLoader singleton
     */
    static IClassLoader getInstance(String name, ClassLoader parent) {
        return FClassLoader.getInstance(name, parent);
    }

    /**
     * Returns IClassLoader with provided name.
     *
     * Throws IReflectiveException if classloader with provided name is not found.
     *
     * @param name name
     * @return IClassLoader
     */
    static IClassLoader getInstance(String name) {
        return FClassLoader.getInstance(name);
    }

    /**
     * Unregisters classloader with provided name.
     *
     * ClassLoader and all classes loaded by it will still exist.
     * Simply, you won't be able to find it by name anymore.
     *
     * @param name name
     */
    static void dispose(String name) {
        FClassLoader.dispose(name);
    }

    /**
     * Returns name of this loader
     *
     * @return String
     */
    String name();

    /**
     * Returns ClassLoader used by this loader
     *
     * @return ClassLoader
     */
    ClassLoader reflective();

    /**
     * Creates new IClass with provided name and bytecode.
     * Parameters "off" and "len" allows you to use only specified fragment of array as bytecode.
     *
     * @param name name
     * @param bytecode bytecode
     * @param off off
     * @param len len
     * @return IClass
     */
    IClass<?> defineIClass(String name, byte[] bytecode, int off, int len);

    /**
     * Creates new IClass with provided name and bytecode.
     *
     * @param name name
     * @param bytecode bytecode
     * @return IClass
     */
    IClass<?> defineIClass(String name, byte[] bytecode);

    /**
     * Creates new IClass with provided bytecode.
     *
     * Note: method reads class name from provided bytecode.
     *
     * @param bytecode bytecode
     * @return IClass
     */
    IClass<?> defineIClass(byte[] bytecode);

    /**
     * Creates new IClass from provided ".class" file.
     *
     * @param path path
     * @return IClass
     * @throws IOException on error
     */
    IClass<?> defineIClass(Path path) throws IOException;

}
