/*
// 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) 2002-2014 Pentaho and others
// All Rights Reserved.
*/
package mondrian.olap.fun;

import mondrian.calc.*;
import mondrian.calc.impl.GenericCalc;
import mondrian.mdx.ResolvedFunCall;
import mondrian.olap.*;
import mondrian.olap.type.TypeUtil;
import mondrian.resource.MondrianResource;
import mondrian.rolap.*;

import java.util.*;

/**
 * Definition of the <code>ValidMeasure</code> MDX function.
 *
 * <p>Returns a valid measure in a virtual cube by forcing inapplicable
 * dimensions to their top level.
 *
 * <p>Syntax:
 * <blockquote><code>
 * ValidMeasure(&lt;Tuple&gt;)
 * </code></blockquote>
 *
 * @author kwalker, mpflug
 */
public class ValidMeasureFunDef extends FunDefBase
{
    static final ValidMeasureFunDef instance = new ValidMeasureFunDef();

    private ValidMeasureFunDef() {
        super(
            "ValidMeasure",
                "Returns a valid measure in a virtual cube by forcing inapplicable dimensions to their top level.",
                "fnt");
    }

    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
        final Calc calc;
        final Exp arg = call.getArg(0);
        if (TypeUtil.couldBeMember(arg.getType())) {
            calc = compiler.compileMember(arg);
        } else {
            calc = compiler.compileTuple(arg);
        }
        return new CalcImpl(call, calc);
    }

    private static class CalcImpl
        extends GenericCalc
    {
        private final Calc calc;

        public CalcImpl(ResolvedFunCall call, Calc calc) {
            super(call);
            this.calc = calc;
        }

        public Object evaluate(Evaluator evaluator) {
            RolapCube baseCube;
            RolapCube virtualCube = (RolapCube) evaluator.getCube();
            final List<Member> memberList = getCalcsMembers(evaluator);

            if (!virtualCube.isVirtual()) {
                // this is not a virtual cube,
                // there's nothing for ValidMeasure to do.
                // just evaluate sub-expression
                evaluator.setContext(memberList.toArray(
                    new Member[memberList.size()]));
                return evaluator.evaluateCurrent();
            }
            // find the measure in the tuple
            int measurePosition = -1;
            for (int i = 0; i < memberList.size(); i++) {
                if (memberList.get(i).getDimension().isMeasures()) {
                    measurePosition = i;
                    break;
                }
            }

            final Member vcMeasure = memberList.get(measurePosition);

            if (!RolapVirtualCubeMeasure.class
                .isAssignableFrom(vcMeasure.getClass()))
            {
                // Cannot use calculated members in ValidMeasure.
                throw MondrianResource.instance()
                    .ValidMeasureUsingCalculatedMember
                    .ex(vcMeasure.getUniqueName());
            }

            baseCube = ((RolapVirtualCubeMeasure)vcMeasure).getCube();

            List<Dimension> vMinusBDimensions =
                getDimensionsToForceToAllLevel(
                    virtualCube, baseCube, memberList);
            // declare members array and fill in with all needed members
            final List<Member> validMeasureMembers =
                new ArrayList<Member>(memberList);
            // start adding to validMeasureMembers at right place
            for (Dimension vMinusBDimension : vMinusBDimensions) {
                final Hierarchy hierarchy = vMinusBDimension.getHierarchy();
                if (hierarchy.hasAll()) {
                    validMeasureMembers.add(hierarchy.getAllMember());
                } else {
                    validMeasureMembers.add(hierarchy.getDefaultMember());
                }
            }
            // this needs to be done before validmeasuremembers are set on the
            // context since calculated members defined on a non joining
            // dimension might have been pulled to default member
            List<Member> calculatedMembers =
                getCalculatedMembersFromContext(evaluator);

            evaluator.setContext(validMeasureMembers);
            evaluator.setContext(calculatedMembers);

            return evaluator.evaluateCurrent();
        }

        private List<Member> getCalcsMembers(Evaluator evaluator) {
            List<Member> memberList;
            if (calc.isWrapperFor(MemberCalc.class)) {
                memberList = Collections.singletonList(
                    calc.unwrap(MemberCalc.class).evaluateMember(evaluator));
            } else {
                final Member[] tupleMembers =
                    calc.unwrap((TupleCalc.class)).evaluateTuple(evaluator);
                memberList = Arrays.asList(tupleMembers);
            }
            return memberList;
        }

        private List<Member> getCalculatedMembersFromContext(
            Evaluator evaluator)
        {
            Member[] currentMembers = evaluator.getMembers();
            List<Member> calculatedMembers = new ArrayList<Member>();
            for (Member currentMember : currentMembers) {
                if (currentMember.isCalculated()) {
                    calculatedMembers.add(currentMember);
                }
            }
            return calculatedMembers;
        }

        public Calc[] getCalcs() {
            return new Calc[]{calc};
        }

        private List<Dimension> getDimensionsToForceToAllLevel(
            RolapCube virtualCube,
            RolapCube baseCube,
            List<Member> memberList)
        {
            List<Dimension> vMinusBDimensions = new ArrayList<Dimension>();
            Set<Dimension> virtualCubeDims = new HashSet<Dimension>();
            virtualCubeDims.addAll(Arrays.asList(virtualCube.getDimensions()));

            Set<Dimension> nonJoiningDims =
                baseCube.nonJoiningDimensions(virtualCubeDims);

            for (Dimension nonJoiningDim : nonJoiningDims) {
                if (!isDimInMembersList(memberList, nonJoiningDim)) {
                    vMinusBDimensions.add(nonJoiningDim);
                }
            }
            return vMinusBDimensions;
        }

        private boolean isDimInMembersList(
            List<Member> members,
            Dimension dimension)
        {
            for (Member member : members) {
                if (member.getName().equalsIgnoreCase(dimension.getName())) {
                    return true;
                }
            }
            return false;
        }

        public boolean dependsOn(Hierarchy hierarchy) {
            // depends on all hierarchies
            return butDepends(getCalcs(), hierarchy);
        }
    }
}

// End ValidMeasureFunDef.java

@@ -3587,129 +3587,624 @@
     
-// start adding to validMeasureMembers at right place%0A            for (Dimension vMinusBDimension : vMinusBDimensions) %7B%0A
+%0A            final Set%3CHierarchy%3E hierarchies = new HashSet%3CHierarchy%3E();%0A            for (Member member : memberList) %7B%0A            %09hierarchies.add(member.getHierarchy());%0A            %7D%0A            %0A            // start adding to validMeasureMembers at right place%0A            for (Dimension vMinusBDimension : vMinusBDimensions) %7B%0A            %09for (Hierarchy hierarchy : vMinusBDimension.getHierarchies()) %7B%0A            %09%09if (!hierarchies.contains(hierarchy)) %7B%0A            %09%09%09validMeasureMembers.add(hierarchy.hasAll() ? hierarchy.getAllMember() : hierarchy.getDefaultMember());%0A            %09%09%7D%0A            %09%7D%0A//
     
@@ -4272,24 +4272,26 @@
 ierarchy();%0A
+//
             
@@ -4316,24 +4316,26 @@
 hasAll()) %7B%0A
+//
             
@@ -4385,32 +4385,34 @@
 etAllMember());%0A
+//
                 
@@ -4412,32 +4412,34 @@
        %7D else %7B%0A
+//
                 
@@ -4489,32 +4489,34 @@
 faultMember());%0A
+//
                 

check for all the hierarchies instead of considering first hierarchy