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

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

import net.ranides.assira.reflection.*;
import net.ranides.assira.reflection.impl.AClass;

/**
 * Class for passing information about parameterized types.
 * TypeToken is very specific {@link IClass} implementation, which is able
 * to reflect at runtime its own type parameter {@code T}.
 *
 * In Java, you can't pass parameterized type {@code List<String>} as method argument.
 * You can only pass {@code List.class}. TypeToken solves this problem.
 *
 * In Java, you can't pass unknown type {@code T} as method argument.
 * TypeToken solves this problem too.
 *
 * <blockquote><pre>
 * List&lt;T&gt; makeList(Class&lt;T&gt; clazz, int size) {
 *     return new ArrayList&lt;T&gt;(size);
 * }
 *
 * List&lt;T&gt; makeList(TypeToken&lt;T&gt; token, int size) {
 *     return new ArrayList&lt;T&gt;(size);
 * }
 *
 * List&lt;T&gt; copyList(List&lt;T&gt; source) {
 *     List&lt;T&gt; target = makeList( new TypeToken&lt;T&gt;(){} , source.size());
 *     for(T item : source) target.add(item);
 *     return target;
 * }
 *
 * List&lt;Pair&lt;String,Float&gt;&gt; makeComplexList(List&lt;?&gt; source) {
 *     List&lt;Pair&lt;String,Float&gt;&gt; target = makeList( new TypeToken&lt;Pair&lt;String,Float&gt;&gt;(){}, source.size());
 *     for(Object item : source) target.add(...);
 *     return target;
 * }
 * </pre></blockquote>
 *
 * <p>
 * Please note this class is able to deduce instance of {@code Class<T>}.
 * It is found inside <a href="http://wiki.fasterxml.com/JacksonHome">Jackson JSON Processor</a> library.
 * </p>
 * @param <T> type which we want to pass to other methods
 * 
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public abstract class TypeToken<T> extends AClass<T> implements Serializable {

    private final IClass<T> type;

    /**
     * Creates new instance of TypeToken.
     * Constructor is protected, because it is usefull only if you create new anonymous subclass.
     * <pre>
     * {@code new TypeToken<Integer>(){ } }
     * </pre>
     *
     */
    @SuppressWarnings("unchecked")
    protected TypeToken() {
        // typeinfo(class).params[0]
        // wstawiło by nam do kontekstu parametr "T" pobrany z deklaracji TypeTokena
        ParameterizedType ptype = (ParameterizedType)getClass().getGenericSuperclass();
        this.type = (IClass)IClass.typeinfo(ptype.getActualTypeArguments()[0]);
    }

    /**
     * Creates new instance of TypeToken.
     * It takes any reflected type and tries to convert it into IClass.
     *
     * @param type type
     */
    @SuppressWarnings("unchecked")
	protected TypeToken(Type type) {
		this.type = (IClass)IClass.typeinfo(type);
	}

    /**
     * Creates new instance of TypeToken.
     * It stores provided IClass as a token type.
     *
     * @param type type
     */
    @SuppressWarnings("unchecked")
    protected TypeToken(IClass<?> type) {
		this.type = (IClass)type;
	}

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

    @Override
    public IAnnotations annotations() {
        return type.annotations();
    }

    @Override
    public IAttributes attributes() {
        return type.attributes();
    }

    @Override
    public String name() {
        return type.name();
    }
    
    @Override
    public final String shortName() {
        return type.shortName();
    }

    @Override
    public boolean isInstance(Object value) {
        return type.isInstance(value);
    }

    @Override
    public Class<T> raw() {
        return type.raw();
    }

    @Override
    public Type reflective() {
        return type.reflective();
    }

    @Override
    public IClasses inner() {
        return type.inner();
    }

    @Override
    public IClass<?> outer() {
        return type.outer();
    }

    @Override
    public IClasses interfaces() {
        return type.interfaces();
    }

    @Override
    public IClasses parents() {
        return type.parents();
    }

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

    @Override
    public IMethods methods() {
        return type.methods();
    }

    @Override
    public IConstructors<T> constructors() {
        return type.constructors();
    }

    @Override
    public IFields fields() {
        return type.fields();
    }

    @Override
    public List<IClass<?>> params() {
        return type.params();
    }

    protected Object writeReplace() {
        return type;
    }

}
