/*
 * @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.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collections;
import java.util.Map;
import net.ranides.assira.collection.query.CQueryBuilder;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IClasses;
import net.ranides.assira.reflection.IContext;
import static net.ranides.assira.reflection.impl.AClass.NULL;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class AContext implements IContext {
    
    public static final AContext ROOT = new AContext();
    
    AContext() {
        // do nothing
    }

    @Override
    public final IContext derive(Type type) {
        if(type instanceof ParameterizedType) {
            return new RContext(this, (ParameterizedType)type);
        } else {
            return this;
        }
    }
    
    @Override
    public final IClasses typesinfo(Type... types) {
        return new AClasses(AHints.EMPTY, CQueryBuilder.fromArray(types).map(this::typeinfo));
    }
    
    @Override
    public final IClasses typesfor(Object... objects) {
        return new AClasses(AHints.EMPTY,CQueryBuilder.fromArray(objects).map(this::typefor));
    }

    @Override
    public final IClass typefor(Object object) {
        return (null == object) ? AClass.NULL : typeinfo(object.getClass());
    }
    
    @Override
    public final IClass typeinfo(Type type) {
        if(type instanceof Class<?>) {
            return new RClass(this, (Class<?>)type);
        }
        if(type instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType)type;
            return new RPClass(this, (Class<?>)ptype.getRawType(), ptype);
        }
        if(type instanceof WildcardType) {
            return new RWClass(this, (WildcardType)type);
        }
        if(type instanceof TypeVariable) {
            return resolveTV(type);
        }
        return NULL;
    }

    private IClass resolveTV(Type type) {
        Type rtype = resolve((TypeVariable)type);
        if(rtype != type) {
            return typeinfo(rtype);
        } else {
            return new RWClass(this, (TypeVariable)type);
        }
    }
    
    protected Type resolve(TypeVariable type) {
        return type;
    }

    @Override
    public Map<String, Type> map() {
        return Collections.emptyMap();
    }
    
}
