package com.capgemini.framework.common;

import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
 * Utility class for reflection operations.
 **/
@Slf4j
public final class ReflectionHelper {

    /**
     * Cache for getting class object using its name.
     **/
    private static final Map<String, Class<?>> CACHED_CLASS_MAP = new HashMap<>();

    /**
     * Constructor.
     **/
    private ReflectionHelper() {
        // Constructor of helper classes must be private 
    }

    /**
     * Dynamic instantiation of object using class name.
     *
     * @param aClassName Specific class name
     * @return New object instance
     */
    public static Object newFromClassForName(String aClassName) {

        Object theObj = null;

        Class<?> theInvokedClass = getClassForName(aClassName);
        if (theInvokedClass != null) {
            theObj = newFromClass(theInvokedClass);
        }

        return theObj;
    }

    /**
     * Dynamic instantiation of object using class.
     *
     * @param anInvokedClass Specific class
     * @return New object instance
     */
    private static Object newFromClass(Class<?> anInvokedClass) {

        Object theObj = null;
        if (anInvokedClass != null) {
            // Try instantiation 
            try {
                theObj = anInvokedClass.newInstance();
            } catch (InstantiationException e) {
                log.error("Unable to create instance of: " + anInvokedClass.getName(), e);
            } catch (IllegalAccessException e) {
                log.error("Unable to access an instance of: " + anInvokedClass.getName(), e);
            }
        }

        return theObj;
    }

    /**
     * Dynamic creation of class using its name.
     *
     * @param aClassName Specific class name
     * @return Searched class
     */
    public static Class<?> getClassForName(String aClassName) {

        // Try to retrieve the class from cache 
        Class<?> theInvokedClass = CACHED_CLASS_MAP.get(aClassName);

        // Try class invocation when it has not been stored in cache yet 
        if (theInvokedClass == null) {
            try {
                // Run invocation 
                theInvokedClass = Class.forName(aClassName);

                // Save class in cache 
                CACHED_CLASS_MAP.put(aClassName, theInvokedClass);
            } catch (ClassNotFoundException e) {
                log.error("Unable to find class: " + aClassName, e);
            }
        }

        return theInvokedClass;
    }
}
