/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Sep 28, 2011
 */

package com.bigdata.rdf.sparql.ast.eval;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.openrdf.query.Dataset;
import org.openrdf.query.algebra.StatementPattern.Scope;

import com.bigdata.bop.BOp;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.PipelineOp;
import com.bigdata.bop.cost.SubqueryCostReport;
import com.bigdata.rdf.sparql.ast.ASTBase;
import com.bigdata.rdf.sparql.ast.StatementPatternNode;

/**
 * Base class provides support for triples, sids, and quads mode joins which
 * was refactored from the old Rule2BOpUtility class.
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public class AST2BOpBase {

    private static final Logger log = Logger.getLogger(AST2BOpBase.class);

    protected AST2BOpBase() {
        
    }
    
    /**
     * Annotations used to mark named and default graph patterns on the
     * {@link IPredicate}s. Rather than attaching a named or default graph
     * expander, we annotate the predicate with the metadata for the access path
     * and then convert that annotation to the appropriate pipeline operators.
     */
    public interface Annotations {

        /**
         * Boolean flag indicates that the database is operating in quads mode.
         */
        String QUADS = AST2BOpBase.class.getName() + ".quads";

        boolean DEFAULT_QUADS = false;

        /**
         * The {@link Dataset} associated with the access path (quads mode
         * only). The {@link Dataset} is only provided by openrdf when FROM or
         * FROM NAMED was used in the query. Otherwise the {@link Dataset} will
         * be <code>null</code> and is not attached as an annotation.
         * <p>
         * Note: This annotation MUST be stripped from the query plan to prevent
         * an attempt to serialized it for RMI in scale-out (the {@link Dataset}
         * is not {@link Serializable}, can be quite large, and is captured by
         * other constructions in the generated query plan).
         */
        String DATASET = AST2BOpBase.class.getName() + ".dataset";

        /**
         * The {@link Scope} of the access path (quads mode only). In quads mode
         * the {@link Scope} is always provided by openrdf.
         *
         * @see Scope#NAMED_CONTEXTS
         * @see Scope#DEFAULT_CONTEXTS
         */
        String SCOPE = AST2BOpBase.class.getName() + ".scope";

        /**
         * Boolean annotation indicates whether the generated JOIN is simple (a
         * single JOIN operator with optional constraints but without any
         * variable materialization requirements) or complex (a JOIN operator
         * associated with at least one constraint which requires the
         * materialization of variables that are not already known to be
         * materialized).
         */
        String SIMPLE_JOIN = AST2BOpBase.class.getName() + ".simpleJoin";
        
        /*
         * Query planner and cost estimates.
         */

        /**
         * The original index assigned to the access path by the static query
         * optimizer.
         * <p>
         * Note: The actual index will be chosen at runtime based on the asBound
         * predicate. In scale-out, the binding sets are send to the node having
         * the shard on which the asBound predicate would read.
         * 
         * TODO It would make sense to lift this annotation into a different AST
         * optimizer so it is always present. An optimization for index locality
         * for as-bound evaluation depends on the presence of this annotation.
         * 
         * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/150" >
         *      Choosing the index for testing fully bound access paths based on
         *      index locality</a>
         */
        String ORIGINAL_INDEX = AST2BOpBase.class.getName()
                + ".originalIndex";

        /**
         * The estimated cardinality of an access path as determined during
         * static query optimization. This is the fast range count if the
         * predicate and {@link Long#MAX_VALUE} if the predicate is part of an
         * optional join (this is used by the query optimized to order the
         * optional joins to the end since they can not increase the selectivity
         * of the query).
         */
        String ESTIMATED_CARDINALITY = AST2BOpBase.class.getName()
                + ".estimatedCardinality";

        /**
         * The estimated cost of a SCAN + FILTER approach to a default graph or
         * named graph query.
         */
        String COST_SCAN = AST2BOpBase.class.getName() + ".cost.scan";

        /**
         * A {@link SubqueryCostReport} on the estimated cost of a SUBQUERY
         * approach to a default graph or named graph query.
         */
        String COST_SUBQUERY = AST2BOpBase.class.getName()
                + ".cost.subquery";

        /**
         * The #of known graphs in the {@link Dataset} for a default graph or
         * named graph query.
         */
        String NKNOWN = AST2BOpBase.class.getName() + ".nknown";
        
        
        /**
         * Boolean indicating whether the AST2BOpUpdate should autocommit.  Default
         * value is TRUE.
         */
        
        String AUTO_COMMIT = AST2BOpBase.class.getName() + "AUTO_COMMIT";

    }

    /**
     * Return either <i>left</i> wrapped as the sole member of an array or
     * {@link BOp#NOARGS} iff <i>left</i> is <code>null</code>.
     * 
     * @param left
     *            The prior operator in the pipeline (optional).
     * @return The array.
     */
    static protected BOp[] leftOrEmpty(final PipelineOp left) {

        return left == null ? BOp.NOARGS : new BOp[] { left };

    }

//    /**
//     * Apply any query hints to the operator as annotations of that operator.
//     * <p>
//     * Note: This method is responsible for transferring query hints from
//     * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}.
//     * 
//     * @param op
//     *            The operator.
//     * @param queryHints
//     *            The query hints (from {@link ASTBase#getQueryHints()}).
//     * 
//     * @return A copy of that operator to which the query hints (if any) have
//     *         been applied. If there are no query hints then the original
//     *         operator is returned.
//     * 
//     * @deprecated by
//     *             {@link #applyQueryHints(PipelineOp, ASTBase, AST2BOpContext)}
//     *             which allows by global and AST node specific query hints to
//     *             be applied.
//     *             
//     * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" >
//     *      Clean up query hints </a>
//     */
//    @Deprecated
//    protected static PipelineOp applyQueryHints(PipelineOp op,
//            final Properties queryHints) {
//
//        return _applyQueryHints(op, queryHints);
//
//    }

    /**
     * Apply any query hints to the operator as annotations of that operator.
     * <p>
     * Note: This method is responsible for transferring query hints from
     * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}.
     * 
     * @param op
     *            The pipeline operator generated from some AST node.
     * @param node
     *            The AST node from which the pipeline operator was generated
     *            (required). The query hints (from
     *            {@link ASTBase#getQueryHints()}) will be applied to that
     *            pipeline operator.
     * @param ctx
     *            The evaluation context (ignored). Global query hints declared
     *            here will be applied to the generated pipeline operator.
     *            Global hints are applied <strong>first</strong> so they can be
     *            override by AST node specific hints.
     * 
     * @return A copy of that operator to which the query hints (if any) have
     *         been applied. If there are no query hints then the original
     *         operator is returned.
     * 
     * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" >
     *      Clean up query hints </a>
     */
    protected static PipelineOp applyQueryHints(PipelineOp op,
            final ASTBase node, final AST2BOpContext ctx) {

        /*
         * Note: The global query hints are transferred onto the AST nodes by
         * the ASTQueryHintOptimizer and the registered IQueryHint classes. They
         * do NOT need to be reapplied here.
         */
//        // Apply global query hints from ASTContext.
//        op = _applyQueryHints(op, ctx.queryHints);

//        if (node != null) {
            // Apply ASTBase node specific query hints.
            op = _applyQueryHints(op, node.getQueryHints());
//        }

        return op;
        
    }

    /**
     * Apply any query hints to the operator as annotations of that operator.
     * <p>
     * Note: This method is responsible for transferring query hints from
     * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}.
     * <p>
     * Note: This alternative form is for use within caller contexts in which
     * (due to historical reasons) we have the query hints object for the AST
     * node, but not the AST node itself. This pattern shows up in the join()
     * methods since the {@link StatementPatternNode} is not passed through.
     * 
     * @param op
     *            The pipeline operator generated from some AST node (required).
     * @param nodeQueryHints
     *            The query hints for the AST node from which the pipeline
     *            operator was generated or its dominating operator context
     *            since not all operators have query hints applied (required).
     * @param ctx
     *            The evaluation context (ignored). Global query hints declared
     *            here will be applied to the generated pipeline operator.
     *            Global hints are applied <strong>first</strong> so they can be
     *            override by AST node specific hints.
     * 
     * @return A copy of that operator to which the query hints (if any) have
     *         been applied. If there are no query hints then the original
     *         operator is returned.
     * 
     * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/791" >
     *      Clean up query hints </a>
     */
    protected static PipelineOp applyQueryHints(PipelineOp op,
            final Properties nodeQueryHints, final AST2BOpContext ctx) {

        /*
         * Note: The global query hints are transferred onto the AST nodes by
         * the ASTQueryHintOptimizer and the registered IQueryHint classes. They
         * do NOT need to be reapplied here.
         */
//        // Apply global query hints from ASTContext.
//        op = _applyQueryHints(op, ctx.queryHints);

//        if (nodeQueryHints != null) {
            // Apply ASTBase node specific query hints.
            op = _applyQueryHints(op, nodeQueryHints);
//        }

        return op;
        
    }

    /**
     * Apply any query hints to the operator as annotations of that operator.
     * <p>
     * Note: This method is responsible for transferring query hints from
     * {@link ASTBase#getQueryHints()} onto a generated {@link PipelineOp}.
     * 
     * @param op
     *            The operator.
     * @param queryHints
     *            The query hints (optional).
     * 
     * @return A copy of that operator to which the query hints (if any) have
     *         been applied. If there are no query hints then the original
     *         operator is returned.
     */
    private static PipelineOp _applyQueryHints(PipelineOp op,
            final Properties queryHints) {

        if (queryHints == null)
            return op;

        final Enumeration<?> pnames = queryHints.propertyNames();

        while (pnames.hasMoreElements()) {

            final String name = (String) pnames.nextElement();

            final String value = queryHints.getProperty(name);

            if (log.isDebugEnabled())
                log.debug("Query hint: op=" + (op.getClass().getSimpleName())
                        + " [" + name + "=" + value + "]");

            op = (PipelineOp) op.setProperty(name, value);

        }

        return op;

    }

}
