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

package net.ranides.assira.reflection.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;

import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.generic.LazySupplier;
import net.ranides.assira.generic.LazySuppliers;
import net.ranides.assira.generic.SerializationUtils;
import net.ranides.assira.reflection.IAttribute;
import net.ranides.assira.reflection.IAttributes;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IContext;

/**
 * Extension of RClass. It tries to resolve actual type parameters using ParameterizedType.
 *
 * Information about type parameters defined by outer/parent classes are stored inside context.
 *
 * @param <T> T
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class RPClass<T> extends RClass<T> implements Serializable {

    private final ParameterizedType ptype;
    
    private final LazySupplier<IContext> icontext = LazySuppliers.concurrent(this::icontext);

    private final LazySupplier<List<IClass<?>>> iparams = LazySuppliers.concurrent(this::iparams);

    RPClass(IContext context, Class<T> type, ParameterizedType ptype) {
        super(context, type);
        this.ptype = ptype;
    }

    @Override
    public IContext context() {
        return icontext.compute();
    }

    private IContext icontext() {
        return super.context().derive(ptype);
    }
    
    @Override
    public List<IClass<?>> params() {
        return iparams.compute();
    }

    private List<IClass<?>> iparams() {
        IContext c = super.context();
        return ListUtils.map(Arrays.asList(ptype.getActualTypeArguments()), c::typeinfo);
    }

    @Override
    protected IAttributes iattributes() {
        return IAttributes.of(raw()).add(IAttribute.RESOLVED);
    }
    
    @Override
    public IClass<?> outer() {
        return context().typeinfo(ptype.getOwnerType());
    }

    protected Object writeReplace() {
        final IContext c = context();
        final Class<?> cRaw = this.raw();
        final IClass[] cParams = Arrays.stream(ptype.getActualTypeArguments()).map(c::typeinfo).toArray(IClass[]::new);

        return SerializationUtils.proxy(() -> FClass.newClass(cRaw, cParams));
    }
}
