package net.ranides.assira.reflection.impl;

import net.ranides.assira.reflection.*;

import java.io.Serializable;
import java.lang.reflect.Type;

/**
 * This implementation of IClass does not store too much details about type,
 * but preserves it's name.
 *
 * Symbolic types could be used for example, in bytecode generators,
 * where you don't need more that fully qualified name.
 *
 * It does not give information about inheritance, but tries to resolve information
 * about outer classes by parsing name (JDK uses $ character inside inner class names)
 */
public class SClass extends AClass<Object> implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final IAttributes ATTRS = IAttributes.of(IAttribute.SYMBOLIC).immutable();

    private final String fqn;

    private final Class<Object> raw;

    private final Type type;

    /**
     * Internal constructor: takes name and  raw type.
     * It uses raw type only in most trivial cases (instanceof, reflective getter)
     *
     * @param fqn fqn
     * @param raw raw
     */
    @SuppressWarnings("unchecked")
    public SClass(String fqn, Class<?> raw) {
        this.fqn = fqn;
        this.raw = (Class)raw;
        this.type = new Type(){
            @Override
            public String toString() {
                return fqn;
            }
        };
    }

    /**
     * Internal constructor: takes only fully qualified name
     * It does not try to resolve/load this class.
     *
     * @param fqn fqn
     */
    public SClass(String fqn) {
        this(fqn, null);
    }

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

    @Override
    public IClass<?> outer() {
        int i = fqn.lastIndexOf('$');
        return i < 0 ? NULL : new SClass(fqn.substring(i + 1));
    }

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

    @Override
    public Class<Object> raw() {
        if(raw == null) {
            throw new IReflectiveException("Symbolic IClass<"+fqn+"> is not reflective");
        } else {
            return raw;
        }
    }

    @Override
    public boolean isInstance(Object value) {
        return raw!=null && raw.isInstance(value);
    }

    @Override
    public Object cast(Object value) {
        throw new ClassCastException(value + " is not instance of " + name());
    }

    @Override
    public String name() {
        return fqn;
    }

    @Override
    public String shortName() {
        int i = fqn.lastIndexOf('.');
        return i < 0 ? fqn : fqn.substring(i + 1);
    }

    @Override
    public IAttributes attributes() {
        return ATTRS;
    }

}
