/*
 * @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.generic.HashUtils;
import net.ranides.assira.reflection.*;

import java.io.Serializable;

/**
 * This is default implementation of IHints.
 * It keeps track of two IAttributes: required and discarded.
 *
 * Please note that IHints is mutable class.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class AHints implements IHints, Serializable {

    private static final AHints NONE = new NULLHints();

    private IAttributes require;
    
    private IAttributes discard;

    AHints(IAttributes require, IAttributes discard) {
        this.require = require;
        this.discard = discard;
    }

    /**
     * Internal method, used by {@link IHints#none()}
     *
     * @return IHints
     */
    public static IHints $none() {
        return NONE;
    }
    
    @Override
    public IAttributes require() {
        return require;
    }
    
    @Override
    public IHints require(IAttribute attr) {
        require.add(attr);
        return this;
    }
    
    @Override
    public IHints require(IAttributes attrs) {
        require.add(attrs);
        return this;
    }

    @Override
    public IAttributes discard() {
        return discard;
    }
    
    @Override
    public IHints discard(IAttribute attr) {
        discard.add(attr);
        return this;
    }
    
    @Override
    public IHints discard(IAttributes attrs) {
        discard.add(attrs);
        return this;
    }

    @Override
    public IHints replace(IHints value) {
        require = value.require();
        discard = value.discard();
        return this;
    }

    /**
     * Returns true, if this HintSet should match inherited elements
     * For example, if when you search for all protected methods.
     *
     * @return boolean
     */
    public boolean isInheritedHint() {
        return 
            require.contains(IAttribute.ANY) ||
            require.contains(IAttribute.PRIVATE) ||
            require.contains(IAttribute.PROTECTED) ||
            require.contains(IAttribute.PACKAGE) ||
            discard.contains(IAttribute.PUBLIC);
    }

    /**
     * Returns true, if this HintSet should match elements declared in class
     *
     * @return boolean
     */
    public boolean isDeclaredHint() {
        return require.contains(IAttribute.DECLARED);
    }

    @Override
    public int hashCode() {
        return HashUtils.hashPair(require,discard);
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (!(object instanceof IHints)) {
            return false;
        }
        AHints that = (AHints) object;
        return this.require().equals(that.require()) && this.discard().equals(that.discard());
    }
    
    @Override
    public String toString() {
        return "AHints{" + "require=" + require + ", discard=" + discard + '}';
    }

    private static final class NULLHints extends AHints {

        public NULLHints() {
            super(IAttributes.EMPTY, IAttributes.EMPTY);
        }
        
        @Override
        public IHints replace(IHints value) {
            return this;
        }
        
    }
}
