package lrs.lazyLRSEvaluators;

import lrs.*;
import fp.*;

/**
 * Lazily creates a possibly infinite list that contains the result of
 * a lambda on two input lists, given as two LRStructs.
 * The lambda determines how the firsts of the two input lists will be
 * combined, and which of the elements will be "consumed", i.e. removed
 * from the front of the input lists.
 * If one of the lists ends, the created list will contain just the elements
 * in the other list. If both lists end, this list will end.
 */
public class LazyCombineEval extends ALazyEval {
    /** Constructs a LazyCombineEval, which creates a list from two input
      * lists using a lambda. The lambda determines how the firsts of the
      * two input lists will be combined, and which of the elements will
      * be "consumed", i.e. removed from the front of the input lists.
      * @param src1 first input list
      * @param src2 second input list
      * @param op a lambda that takes src1 as param[0] and src2 as param[1]
      *           and returns the value for the output list.
      */
    public LazyCombineEval(LRStruct src1, LRStruct src2, ILambda op) {
        this.src1 = src1;
        this.src2 = src2;
        this.op = op;
    }
    
    // FOR STUDENT TO COMPLETE: add fields and methods here
    private LRStruct src1;
    private LRStruct src2;
    private ILambda op;
    
    public LRStruct nextLRS() {
        return makeLRS();
    }
    
    /**
     * Pass the two lists to a lambda and put the result in the list.
     * @return a list with the elements that come out of the lambda.
     */
    public LRStruct makeLRS() {
        return (LRStruct)src1.execute(new IAlgo() {
            public Object emptyCase(LRStruct host, Object... inp) {
                return src2.execute(new IAlgo() {
                    public Object emptyCase(LRStruct host, Object... inp) {
                        return new LRStruct(); // both empty
                    }
                    public Object nonEmptyCase(LRStruct host, Object... inp) {
                        return makeLazyLRS(src2.removeFront());
                    }
                });
            }
            public Object nonEmptyCase(LRStruct host, Object... inp) {
                return src2.execute(new IAlgo() {
                    public Object emptyCase(LRStruct host, Object... inp) {
                        return makeLazyLRS(src1.removeFront());
                    }
                    public Object nonEmptyCase(LRStruct host, Object... inp) {
                        return makeLazyLRS(op.apply(src1, src2));
                    }
                });
            }
        });
    }    
}

