package de.renew.formalism.fs;

import de.uni_hamburg.fs.FeatureStructure;
import de.uni_hamburg.fs.UnificationFailure;

import de.renew.expression.Function;
import de.renew.formalism.function.BasicFunction;
import de.renew.unify.Impossible;
import de.renew.unify.Tuple;
import de.renew.util.Value;


/**
 * An abstract class of Function in the context of FS.
 */
public abstract class FSFunction implements Function {

    /**
     * Default constructor for subclasses of FSFunction.
     */
    protected FSFunction() {}

    /**
     *  An anonymous class instance of FSFunction that checks if left is strictly subsumes by right.
     */
    public final static FSFunction LESS = new FSFunction() {
        @Override
        public Object doFSFunction(FeatureStructure left, FeatureStructure right) {
            return new Value(Boolean.valueOf(!left.equals(right) && left.subsumes(right)));
        }

        @Override
        public Function getOrdFunction() {
            return BasicFunction.LESS;
        }
    };

    /**
     * An anonymous class instance of FSFunction that checks if left is subsumed by right.
     */
    public final static FSFunction LESSEQUAL = new FSFunction() {
        @Override
        public Object doFSFunction(FeatureStructure left, FeatureStructure right) {
            return new Value(Boolean.valueOf(left.subsumes(right)));
        }

        @Override
        public Function getOrdFunction() {
            return BasicFunction.LESSEQUAL;
        }
    };

    /**
     * An anonymous class instance of FSFunction that checks if left is strictly more specific than right.
     */
    public final static FSFunction GREATER = new FSFunction() {
        @Override
        public Object doFSFunction(FeatureStructure left, FeatureStructure right) {
            return new Value(Boolean.valueOf(!left.equals(right) && right.subsumes(left)));
        }

        @Override
        public Function getOrdFunction() {
            return BasicFunction.GREATER;
        }
    };

    /**
     * An anonymous class instance of FSFunction that checks if left is more specific than right.
     */
    public final static FSFunction GREATEREQUAL = new FSFunction() {
        @Override
        public Object doFSFunction(FeatureStructure left, FeatureStructure right) {
            return new Value(Boolean.valueOf(right.subsumes(left)));
        }

        @Override
        public Function getOrdFunction() {
            return BasicFunction.GREATEREQUAL;
        }
    };

    /**
     * An anonymous class instance of FSFunction that returns the unification of left and right.
     */
    public final static FSFunction PLUS = new FSFunction() {
        @Override
        public Object doFSFunction(FeatureStructure left, FeatureStructure right)
            throws Impossible
        {
            try {
                return left.unify(right);
            } catch (UnificationFailure uff) {
                throw new Impossible();
            }
        }

        @Override
        public Function getOrdFunction() {
            return BasicFunction.PLUS;
        }
    };

    /**
     * Returns the value the function evaluates to with a given input of two FeatureStructures.
     *
     * @param left the left FeatureStructure
     * @param right the right FeatureStructure
     * @return the value the function evaluates to
     * @throws Impossible if evaluation is impossible
     */
    public abstract Object doFSFunction(FeatureStructure left, FeatureStructure right)
        throws Impossible;

    /**
     * Returns a BasicFunction to fall back on when inputs are not FeatureStructures.
     * @return the BasicFunction to fall back on
     */
    public abstract Function getOrdFunction();

    @Override
    public Object function(Object param) throws Impossible {
        Tuple tuple = (Tuple) param;
        if (tuple.getArity() != 2) {
            throw new Impossible();
        }
        Object obj1 = tuple.getComponent(0);
        Object obj2 = tuple.getComponent(1);

        if (obj1 instanceof FeatureStructure && obj2 instanceof FeatureStructure) {
            FeatureStructure fs1 = (FeatureStructure) obj1;
            FeatureStructure fs2 = (FeatureStructure) obj2;
            return doFSFunction(fs1, fs2);
        } else {
            return getOrdFunction().function(param);
        }
    }
}