package com.fasterxml.jackson.databind.type;

import java.lang.reflect.*;

/**
 * Simple replacement for {@link java.lang.Class} (and/or various Type subtypes)
 * that is used as part of single-path extends/implements chain to express
 * specific relationship between one subtype and one supertype. This is needed
 * for resolving type parameters. Instances are doubly-linked so that chain
 * can be traversed in both directions
 */
public class HierarchicType
{
    /**
     * Type which will be either plain {@link java.lang.Class} or
     * {@link java.lang.reflect.ParameterizedType}.
     */
    protected final Type _actualType;
    
    protected final Class<?> _rawClass;

    protected final ParameterizedType _genericType;
    
    protected HierarchicType _superType;

    protected HierarchicType _subType;
    
    public HierarchicType(Type type)
    {
        this._actualType = type;
        if (type instanceof Class<?>) {
            _rawClass = (Class<?>) type;
            _genericType = null;
        } else if (type instanceof ParameterizedType) {
            _genericType = (ParameterizedType) type;
            _rawClass = (Class<?>) _genericType.getRawType();
        } else { // should never happen... can't extend GenericArrayType?
            throw new IllegalArgumentException("Type "+type.getClass().getName()+" can not be used to construct HierarchicType");
        }
    }

    private HierarchicType(Type actualType, Class<?> rawClass, ParameterizedType genericType,
        HierarchicType superType, HierarchicType subType)
    {
        _actualType = actualType;
        _rawClass = rawClass;
        _genericType = genericType;
        _superType = superType;
        _subType = subType;
    }
    
    /**
     * Method that can be used to create a deep clone of this hierarchic type, including
     * super types (but not subtypes)
     */
    public HierarchicType deepCloneWithoutSubtype()
    {
        HierarchicType sup = (_superType == null) ? null : _superType.deepCloneWithoutSubtype();
        HierarchicType result = new HierarchicType(_actualType, _rawClass, _genericType, sup, null);
        if (sup != null) {
            sup.setSubType(result);
        }
        return result;
    }
    
    public void setSuperType(HierarchicType sup) { _superType = sup; }
    public final HierarchicType getSuperType() { return _superType; }
    public void setSubType(HierarchicType sub) { _subType = sub; }
    public final HierarchicType getSubType() { return _subType; }
    
    public final boolean isGeneric() { return _genericType != null; }
    public final ParameterizedType asGeneric() { return _genericType; }

    public final Class<?> getRawClass() { return _rawClass; }
    
    @Override
    public String toString() {
        if (_genericType != null) {
            return _genericType.toString();
        }
        return _rawClass.getName();
    }
    
}
