package lrs.lazyLRSEvaluators;

import lrs.*;
import fp.*;

/**
 * Lazily creates a possibly infinite list that contains the result of
 * applying a lambda from one input list to a value from a second input list.
 * If the list with the lambdas ends, the created list will contain just the
 * elements in the value list. If the value list ends, this list will end.
 */
public class LazyLambdaApplyEval extends ALazyEval {
    /** Constructs a LazyLambdaApplyEval, which creates a list from a list of
      * lambdas and a list of values. The lambda determines how the value list
      * is processed and which elements are "consumed", i.e. removed from the
      * front of the value list.
      * The lambda is always removed from the front of the lambda list.
      * @param src1 input list with lambdas
      * @param src2 input list with values
      */
    public LazyLambdaApplyEval(LRStruct lambdas, LRStruct values) {
        this.lambdas = lambdas;
        this.values = values;
    }
    
    // FOR STUDENT TO COMPLETE: add fields and methods here
    private LRStruct lambdas;
    private LRStruct values;
    
    public LRStruct nextLRS() {
        return makeLRS();
    }
    
    /**
     * If the lambda list is non-empty, remove the lambda a lambda and pass the value list
     * to it. Return the value of the lambda application. If the lambda list is empty,
     * clone the value list.
     * @return a list with the elements that come out of the lambdas.
     */
    public LRStruct makeLRS() {
        return (LRStruct)lambdas.execute(new IAlgo() {
            public Object emptyCase(LRStruct host, Object... inp) {
                return values.execute(new IAlgo() {
                    public Object emptyCase(LRStruct host, Object... inp) {
                        return new LRStruct(); // both empty
                    }
                    public Object nonEmptyCase(LRStruct host, Object... inp) {
                        return makeLazyLRS(values.removeFront());
                    }
                });
            }
            public Object nonEmptyCase(LRStruct host, Object... inp) {
                final ILambda l = (ILambda)lambdas.removeFront();
                return values.execute(new IAlgo() {
                    public Object emptyCase(LRStruct host, Object... inp) {
                        return new LRStruct(); // both empty
                    }
                    public Object nonEmptyCase(LRStruct host, Object... inp) {
                        return makeLazyLRS(l.apply(values));
                    }
                });
            }
        });
    }    
}
