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

import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.generic.Wrapper;
import net.ranides.assira.generic.Wrappers;
import net.ranides.assira.generic.Wrappers.AbstractWrapper;
import net.ranides.assira.reflection.*;
import net.ranides.assira.trace.ExceptionUtils;

import java.io.Serializable;
import java.lang.invoke.MethodHandle;

/**
 * Base abstract class for IField implementations.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public abstract class AField extends AElement implements IField, Serializable {

    private static final long serialVersionUID = 1L;

    private final IContext context;

    protected AField(IContext context) {
        this.context = context;
    }
    
    @Override
    public final IContext context() {
        return context;
    }
    
    @Override
    public final IFields collect() {
        return FElements.newFields(IHints.none(), CQuery.from().value(this));
    }

    @Override
    public final Object getStatic() {
        if(!isStatic()) {
            throw new IReflectiveException("Field is not static: " + this);
        }
        return get(null);
    }

    @Override
    public final Object replace(Object that, Object value) {
        Object prv = get(that);
        set(that,value);
        return prv;
    }

    @Override
    public final Wrapper<Object> bind(Object that) {
        return new FWrapper(that);
    }

    @Override
    public final boolean isVolatile() {
        return attribute(IAttribute.VOLATILE);
    }

    @Override
    public final boolean isTransient() {
        return attribute(IAttribute.TRANSIENT);
    }

    @Override
    public abstract int hashCode();

    @Override
    public final boolean equals(Object object) {
        if(!(object instanceof IField)) {
            return false;
        }
        IField that = (IField)object;
        return this.name().equals(that.name()) && this.type().equals(that.type()) && this.parent().equals(that.parent());
    }

    @Override
    public final String toString() {
        return type() + " " + parent() + "." + name();
    }

    private final class FWrapper implements AbstractWrapper<Object> {
        
        private final MethodHandle getter;
        private final MethodHandle setter;

        public FWrapper(Object that) {
            this.getter = getter().bindTo(that);
            this.setter = setter().bindTo(that);
        }

        @Override
        public Object get() {
            try {
                return getter.invoke();
            } catch (Throwable cause) { //NOPMD rethrow idiom
                throw ExceptionUtils.rethrow(cause);
            }
        }

        @Override
        public void set(Object value) {
            try {
                setter.invoke(value);
            } catch (Throwable cause) { //NOPMD rethrow idiom
                throw ExceptionUtils.rethrow(cause);
            }
        }

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