using System;
using System.Reflection;
using edu.neu.ccs.demeterf.util;
using edu.neu.ccs.demeterf.control;
using edu.neu.ccs.demeterf.dispatch;
using edu.neu.ccs.demeterf.lib;
using edu.neu.ccs.demeterf.parallel;


namespace edu.neu.ccs.demeterf{

    /** A Helper Class to implement something like SYB queries. */
    public abstract class TU<T> : FC{
        // Type Unifying combiner (TU)
    
        // Default for objects without fields (unless handled earlier) 
        public T combine(object o1){ return combine(); }

        /** A Bunch of simple object* methods to catch all the rest */
        public T combine(object o1, object o2) 
          { return simplify(new object[]{o2}); }
        public T combine(object o1, object o2, object o3)
          { return simplify(new object[]{o2,o3}); }
        public T combine(object o1, object o2, object o3, object o4)
          { return simplify(new object[]{o2,o3,o4}); }
        public T combine(object o1, object o2, object o3, object o4, object o5)
          { return simplify(new object[]{o2,o3,o4,o5}); }
        public T combine(object o1, object o2, object o3, object o4,
                             object o5, object o6)
          { return simplify( new object[]{o2,o3,o4,o5,o6}); }

        //** More if needed...

        /** The simplify for an array... called from above */
        T simplify(object[] o){ return simplify(o, (T)(o[0]), 1); }

        /** Recursive fold over the array */
        T simplify(object[] o, T t, int i){
            if(i >= o.Length)return t;
            return simplify(o, fold(t, (T)o[i]), i+1);
        }

        // * * * * Actual Stuff... * * * *
        // Clients extend TU and implement the methods below.

        /** Perform one step of the combination */
        public abstract T fold(T accum, T one);

        /** Default apply (must be implemented for safety) */
        public abstract T combine();
        
        /** Use this to do a TU Traversal of an Object. It reflects on the given TUCombiner, and sees what you're
         *    interested in (single argument combine methods), making those
         *    classes BuiltIns */
        public T traverse(Object o){
            return new Traversal(this, control(this, Control.builtins())).traverse<T>(o);
        }
        
        /** Use this to do a TU Traversal of an Object with a given Control
         *    for optimization purposes. <i>Note</i>: The function object is <b>not</b> run on <i>bypassed</i> edges, so
         *    your methods *must* handle these edges correctly.  */
        public T traverse(Object o, MutableControl c){
            return new Traversal(this, control(this, c)).traverse<T>(o);
        }

       /** Use this to do a TU Traversal of an Object.
         *  It reflects on the given TU, and sees what you're
         *    interested in (single argument combine methods), making those
         *    classes BuiltIns */
        public static T traverse(Object o, TU<T> tuc){
            return new Traversal(tuc, control(tuc, Control.builtins())).traverse<T>(o);
        }
        
        /** Use this to do a TU Traversal of an Object with a given Control
         *    for optimization purposes.<br/>
         *  <i>Note</i>: The function object is <b>not</b> run on <i>bypassed</i> edges, so
         *    your methods *must* handle these edges correctly.  */
        public static T traverse(Object o, TU<T> tuc, MutableControl c){
            return new Traversal(tuc, control(tuc, c)).traverse<T>(o);
        }
        
        /** Uses ParTraversal to do combines in parallel */
        public static T partraverse(Object o, TU<T> tuc){
            return new ParTraversal(tuc, control(tuc, Control.builtins())).traverse<T>(o);
        }
        
        /** Uses ParTraversal to do combines in parallel */
        public static T partraverse(Object o, TU<T> tuc, MutableControl c){
            return new ParTraversal(tuc, control(tuc, c)).traverse<T>(o);
        }
        
        /** Collect the implied control from the TU's signatures and add it
         *    to the given MutableControl */
        private static MutableControl control(TU<T> tuc, MutableControl c){
            foreach(DBEntry<MethodInfo> dbe in
                    MethodDB<MethodInfo>.createMDB(tuc.GetType(),Traversal.BuilderMethod).getMethods()){
                if(dbe.numArgs() == 1 && !dbe.arg(0).Equals(typeof(object)))
                    c.addBuiltIn(dbe.arg(0));
            }
            return c;
        }
    }
}