/*
 * Copyright (c) 2009 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util.reflection;

import com.jscape.util.collections.TransformIterator;
import com.jscape.util.functor.UnaryFunction;
import com.jscape.util.functor.UnaryPredicate;

import java.lang.reflect.Constructor;
import java.util.Collection;

import static com.jscape.util.collections.Algorithms.copy;

/**
 * @author Alex Usun
 */
public class ConstructorInvoker<T>
        extends UnaryFunction<Class, T> {

    private final Class[] argTypes;
    private final Object[] args;

    @SuppressWarnings("unchecked")
    public static <T> T invoke(Class target, UnaryPredicate<? super Constructor> predicate, Object... args)
            throws ReflectionException {
        try {
            Constructor method = ConstructorFinder.find(target, predicate);
            method.setAccessible(true);
            return (T) method.newInstance(args);
        } catch (Exception e) {
            throw new ReflectionException(e);
        }
    }

    public static <T> T invoke(Class target, Class[] argTypes, Object... args)
            throws ReflectionException {
        return ConstructorInvoker.<T>invoke(target, new ConstructorParametersEqual(true, argTypes), args);
    }

    public static <T> T invoke(Class target, Object... args)
            throws ReflectionException {
        Collection<Class> actualTypes = copy(TransformIterator.iterator(args, new ObjectClass()));
        return ConstructorInvoker.<T>invoke(target, new ConstructorParametersEqual(false, actualTypes), args);
    }

    public static <T> T invoke(String aClass)
            throws ReflectionException {
        try {
            return ConstructorInvoker.<T>invoke(Class.forName(aClass));
        } catch (ClassNotFoundException e) {
            throw new ReflectionException(e);
        }
    }

    public ConstructorInvoker(Class[] argTypes, Object[] args) {
        this.argTypes = argTypes;
        this.args = args;
    }

    public ConstructorInvoker() {
        this(new Class[0], new Object[0]);
    }

    public T evaluate(Class value) {
        try {
            return ConstructorInvoker.<T>invoke(value, this.argTypes, this.args);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }
}
