/*
 * @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.GenericArrayType;
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 AContext() {
        // do nothing
    }

    @Override
    public final IContext derive(Type type) {
        if(type instanceof ParameterizedType) {
            return RContext.create(this, (ParameterizedType)type);
        } else {
            return this;
        }
    }
    
    @Override
    public final IClasses typesinfo(Type... types) {
        return FElements.newClasses(AHints.EMPTY, CQueryBuilder.fromArray(types).map(this::typeinfo));
    }
    
    @Override
    public final IClasses typesfor(Object... objects) {
        return FElements.newClasses(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 <R> IClass<R> typeinfo(Class<R> type) {
        return itypeinfo(type);
    }
    
    @Override
    public final IClass<?> typeinfo(Type type) {
        return itypeinfo(type);
    }
    
    private IClass itypeinfo(Type type) {
        if(type instanceof Class<?>) {
            return FClass.newClass(this, (Class<?>)type);
        }
        if(type instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType)type;
            return FClass.newClass(this, (Class<?>)ptype.getRawType(), ptype);
        }
        if(type instanceof GenericArrayType) {
            return FClass.newClass(this, (GenericArrayType)type);
        }
        if(type instanceof WildcardType) {
            return FClass.newClass(this, (WildcardType)type);
        }
        if(type instanceof TypeVariable) {
            return resolveTV((TypeVariable)type);
        }
        return NULL;
    }

    protected IClass<?> resolveTV(TypeVariable type) {
        Type rtype = resolve(type);
        // sprawdzamy czy udało się znaleźć jakiś inny typ, jeśli wynikiem jest podany argument,
        // to znaczy, że nic już nie możemy mądrego zrobić: zwróćmy wynik
        if(rtype != type) { //NOPMD
            return typeinfo(rtype);
        } else {
            return FClass.newClass(this, (TypeVariable)type);
        }
    }
    
    protected Type resolve(TypeVariable type) {
        return type;
    }

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