package lrs.visitor;
import lrs.*;

/**
 * Splits the host in half.  The host gets the first n/2 elements,
 * and the other half gets the last n - n/2 elements.
 */
public class Splitter implements IAlgo {
    public final static Splitter Singleton = new Splitter ();
    
    private Splitter() {
    }
    
    /**
     * @param host
     * @param input
     * @return
     */
    public Object emptyCase(LRStruct host, Object... input) {
        return new LRStruct();
    }
    
    /**
     *  Asks the host's tail to split passing host as a potential "split point".
     * @param host
     * @param input not used
     * @return
     */
    public Object nonEmptyCase(LRStruct host, Object... input) {
        return host.getRest().execute(new Advance0(), host);
    }
}

class Advance0 implements IAlgo {
    
    IAlgo advance1 = new IAlgo() {
        /**
         * We know the input is a potential split point and now we are at the end of
         * the original list.  So we split right here at the split point.
         * @param host
         * @param accSplit
         * @return
         */
        public Object emptyCase(LRStruct host, Object... accSplit) {
            return cutAt((LRStruct)accSplit[0]);
        }
        
        /**
         *  Asks the host's tail to split passing input as a potential "split point".
         * @param host
         * @param accSplit  a LRStruct.
         * @return
         */
        public Object nonEmptyCase(LRStruct host, Object... accSplit) {
            return host.getRest().execute(Advance0.this, accSplit);
        }
    };
    
    /**
     * We know the input is a potential split point and now we are at the end of
     * the original list.  So we split right here at the split point.
     * @param host
     * @param accSplit a LRStruct
     * @return
     */
    public Object emptyCase(LRStruct host, Object... accSplit) {
        return cutAt((LRStruct)accSplit[0]);
    }
    
    /**
     * We have not reached the end yet, so we advance the accumlated split point
     * by one.
     * @param host
     * @param accSplit
     * @return
     */
    public Object nonEmptyCase(LRStruct host, Object... accSplit) {
        return host.getRest().execute(advance1, ((LRStruct)accSplit[0]).getRest());
    }
    
    /**
     * Cut off the list containing splitPoint at splitPoint.
     * Return a list that shares the same node with splitPoint.
     * @param splitPoint
     * @return
     */
    private LRStruct cutAt(LRStruct splitPoint) {
        LRStruct tailHalf = new LRStruct ();
        tailHalf.execute (Becomes.Singleton, splitPoint);
        splitPoint.execute (Becomes.Singleton, new LRStruct ());
        return tailHalf;
    }
}
