/*
 * @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.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.*;

import net.ranides.assira.reflection.*;
import net.ranides.assira.text.StrBuilder;
import net.ranides.assira.collection.query.CQueryBuilder;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class AClass<T> implements IClass<T> {
    
    private static final Set<IAttribute> ATTRS = IAttribute.constant(IAttribute.FINAL, IAttribute.NATIVE, IAttribute.PUBLIC);
    
    private static final Object[] EMPTY_ARGS = new Object[0];
    
    private final IContext context;
    
    protected AClass() {
        this(IContext.DEFAULT);
    }
    
    protected AClass(IContext context) {
        this.context = context;
    }
    
    @Override
    public IContext context() {
        return context;
    }
    
    @Override
    public List<IClass<?>> params() {
        return Collections.emptyList();
    }

    @Override
    public IFields fields() {
        return AFields.EMPTY;
    }
    
    @Override
    public Optional<IField> field(String name) {
        return fields().require(IAttribute.ANY).require(name).first();
    }

    @Override
    public IMethods constructors() {
        return AMethods.EMPTY;
    }

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

    @Override
    public Optional<IMethod> method(String name) {
        return methods().require(IAttribute.ANY).require(name).first();
    }

    @Override
    public IClass<?> parent() {
        throw new NoSuchElementException("IClass<NULL> has no parent");
    }

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

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

    @Override
    public IClass<?> outer() {
        throw new NoSuchElementException("IClass<NULL> has no outer class");
    }

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

    @Override
    public final T construct() {
        return construct(EMPTY_ARGS);
    }

    @SuppressWarnings("unchecked")
    @Override
    public final T construct(Object... arguments) {
        return (T)constructors()
            .require(IAttribute.DECLARED).accepts(arguments).first().get().call(arguments);
    }
    
    @Override
    public IClass<?> component() {
        if(!attributes().contains(IAttribute.ARRAY)) {
            throw new UnsupportedOperationException(name() + " is not array");
        } else {
            return context.typeinfo(raw().getComponentType());
        }
    }

    @Override
    public Type reflective() {
        throw new UnsupportedOperationException("IClass<NULL> is not reflective");
    }

    @Override
    public Class<?> raw() {
       throw new UnsupportedOperationException("IClass<NULL> is not reflective");
    }

    @Override
    public final boolean isSuper(IClass<?> type) {
        return RCompareUtils.isSuper(this, type);
    }

    @Override
    public final boolean isSubclass(IClass<?> type) {
        return RCompareUtils.isSubclass(this, type);
    }
    
    @Override
    public final boolean isEquivalent(IClass<?> type) {
        return RCompareUtils.isEquivalent(this, type);
    }

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

    @SuppressWarnings("unchecked")
    @Override
    public T cast(Object value) {
        if(!isInstance(value)) {
            throw new ClassCastException(value + " is not instance of " + name());
        }
        return (T)value;
    }
    
    
    
    @Override
    public final IClasses collect() {
        return FElements.newClasses(AHints.EMPTY, CQueryBuilder.of(this));
    }

    @Override
    public String name() {
        return "$NULL";
    }

    @Override
    public String shortname() {
        return name();
    }
    
    @Override
    public Set<IAttribute> attributes() {
        return ATTRS;
    }

    @Override
    public IAnnotations annotations() {
        return AAnnotations.EMPTY;
    }
    
    @Override
    public <A extends Annotation> Optional<A> annotation(Class<A> type) {
        return annotations().first(type);
    }

    @Override
    public final int compareTo(IClass<?> object) {
        return RCompareUtils.compare(this, object);
    }

    @Override
    public final boolean equals(Object that) {
        return (that instanceof IClass<?>) && RCompareUtils.equals(this, (IClass<?>)that);
    }

    @Override
    public final int hashCode() {
        return RCompareUtils.hashcode(this);
    }
    
    @Override
    public final String toString() {
        List<IClass<?>> params = params();
        if(params.isEmpty()) {
            return name();
        }
        return new StrBuilder().append(name()).open("<",">",", ").append(params).close().toString();
    }
    
}
