/*
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2004-2005 Julian Hyde
// Copyright (C) 2005-2011 Pentaho
// All Rights Reserved.
*/

package mondrian.olap.fun;

import mondrian.calc.*;
import mondrian.calc.impl.AbstractListCalc;
import mondrian.mdx.ResolvedFunCall;
import mondrian.olap.*;

import java.util.*;

/**
 * Definition of the <code>DrilldownMember</code> MDX function.
 *
 * @author Grzegorz Lojek
 * @since 6 December, 2004
 */
class DrilldownMemberFunDef extends FunDefBase {
    static final String[] reservedWords = new String[] {"RECURSIVE"};
    static final ReflectiveMultiResolver Resolver =
        new ReflectiveMultiResolver(
            "DrilldownMember",
            "DrilldownMember(<Set1>, <Set2>[, RECURSIVE])",
            "Drills down the members in a set that are present in a second specified set.",
            new String[]{"fxxx", "fxxxy"},
            DrilldownMemberFunDef.class,
            reservedWords);

    public DrilldownMemberFunDef(FunDef funDef) {
        super(funDef);
    }

    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
        final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
        final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
        final String literalArg = getLiteralArg(call, 2, "", reservedWords);
        final boolean recursive = literalArg.equals("RECURSIVE");

        return new AbstractListCalc(
            call,
            new Calc[] {listCalc1, listCalc2})
        {
            public TupleList evaluateList(Evaluator evaluator) {
                final TupleList list1 = listCalc1.evaluateList(evaluator);
                final TupleList list2 = listCalc2.evaluateList(evaluator);
                return drilldownMember(list1, list2, evaluator);
            }

            /**
             * Drills down an element.
             *
             * <p>Algorithm: If object is present in {@code memberSet} adds to
             * result children of the object. If flag {@code recursive} is set
             * then this method is called recursively for the children.
             *
             * @param evaluator Evaluator
             * @param tuple Tuple (may have arity 1)
             * @param memberSet Set of members
             * @param resultList Result
             */
            protected void drillDownObj(
                Evaluator evaluator,
                Member[] tuple,
                Set<Member> memberSet,
                TupleList resultList)
            {
                for (int k = 0; k < tuple.length; k++) {
                    Member member = tuple[k];
                    if (memberSet.contains(member)) {
                        List<Member> children =
                            evaluator.getSchemaReader().getMemberChildren(
                                member);
                        final Member[] tuple2 = tuple.clone();
                        for (Member childMember : children) {
                            tuple2[k] = childMember;
                            resultList.addTuple(tuple2);
                            if (recursive) {
                                drillDownObj(
                                    evaluator, tuple2, memberSet, resultList);
                            }
                        }
                        break;
                    }
                }
            }

            private TupleList drilldownMember(
                TupleList v0,
                TupleList v1,
                Evaluator evaluator)
            {
                assert v1.getArity() == 1;
                if (v0.isEmpty() || v1.isEmpty()) {
                    return v0;
                }

                Set<Member> set1 = new HashSet<Member>(v1.slice(0));

                TupleList result = TupleCollections.createList(v0.getArity());
                int i = 0, n = v0.size();
                final Member[] members = new Member[v0.getArity()];
                while (i < n) {
                    List<Member> o = v0.get(i++);
                    o.toArray(members);
                    result.add(o);
                    drillDownObj(evaluator, members, set1, result);
                }
                return result;
            }
        };
    }
}

// End DrilldownMemberFunDef.java
