/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.reflection.impl;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

import net.ranides.assira.collection.arrays.ArrayUtils;
import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.generic.LazyReference;
import net.ranides.assira.trace.ExceptionUtils;
import net.ranides.assira.reflection.util.MethodUtils;
import net.ranides.assira.reflection.util.ReflectUtils;
import net.ranides.assira.reflection.*;
import net.ranides.assira.collection.query.CQueryBuilder;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class RConstructor extends AMethod {

    private final Constructor<?> method;
    private final Supplier<MethodHandle> handle;

	RConstructor(IContext context, Constructor<?> method) {
        super(context);
		ReflectUtils.access(method);
		this.method = method;
        this.handle = LazyReference.concurrent(() -> {
            try {
                return MethodHandles.lookup().unreflectConstructor(method);
            } catch (IllegalAccessException cause) {
                throw ExceptionUtils.rethrow(cause);
            }
        });
	}
    
	@Override
	public List<IClass<?>> params() {
        IContext c = context();
        return ListUtils.map(Arrays.asList(method.getTypeParameters()), c::typeinfo);
	}

	@Override
	public IClass<?> returns() {
        return parent();
	}

	@Override
	public IArguments arguments() {
        IContext c = context();
        return FElements.newArguments(
            AHints.EMPTY, 
            method.getParameterCount(), 
            CQueryBuilder.fromArray(method.getParameters()).map((i,v)-> FArgument.newArgument(c, this, v, i))
        );
	}

	@Override
	public IClass<?> parent() {
        return context().typeinfo(method.getDeclaringClass());
	}

	@Override
	public Object apply() {
		return MethodUtils.invoke(method);
	}

	@Override
	public Object apply(Object that) {
        if(that != null) {
            throw new IllegalArgumentException("constructors doesn't support 'this'");
        }
		return MethodUtils.invoke(method);
	}

	@Override
	public Object apply(Object that, Object... arguments) {
        if(that != null) {
            throw new IllegalArgumentException("constructors doesn't support 'this'");
        }
		return MethodUtils.invoke(method, arguments);
	}

    @Override
    public Object call(Object... arguments) {
        try {
            return handle.get().invokeWithArguments(arguments);
        } catch (Throwable cause) { // NOPMD rethrow idiom
            throw ExceptionUtils.rethrow(cause);
        }
    }
    
	@Override
	public Method reflective() {
        throw new UnsupportedOperationException();
	}

	@Override
	public MethodHandle handle() {
        try {
            return MethodHandles.lookup().unreflectConstructor(method);
        } catch (IllegalAccessException cause) {
            throw ExceptionUtils.rethrow(cause);
        }
	}

	@Override
	public String name() {
        return method.getName();
	}

	@Override
	public Set<IAttribute> attributes() {
        return IAttribute.resolve(method);
	}

	@Override
	public IAnnotations annotations() {
        return FElements.newAnnotations(AHints.EMPTY, CQueryBuilder.fromArray(method.getAnnotations()));
	}

	protected String[] $debugParams() {
		return CDParameters.getParams(method);
	}

}
