// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace System.Data.Entity.Core.Query.InternalTrees
{
    using System.Data.Entity.Utilities;
    using System.Diagnostics;

    // <summary>
    // A Rule - more specifically, a transformation rule - describes an action that is to
    // be taken when a specific kind of subtree is found in the tree
    // </summary>
    internal abstract class Rule
    {
        // <summary>
        // The "callback" function for each rule.
        // Every callback function must return true if the subtree has
        // been modified (or a new subtree has been returned); and must return false
        // otherwise. If the root of the subtree has not changed, but some internal details
        // of the subtree have changed, it is the responsibility of the rule to update any
        // local bookkeeping information.
        // </summary>
        // <param name="context"> The rule processing context </param>
        // <param name="subTree"> the subtree to operate on </param>
        // <param name="newSubTree"> possibly transformed subtree </param>
        // <returns> transformation status - true, if there was some change; false otherwise </returns>
        internal delegate bool ProcessNodeDelegate(RuleProcessingContext context, Node subTree, out Node newSubTree);

        #region private state

        private readonly ProcessNodeDelegate m_nodeDelegate;
        private readonly OpType m_opType;

        #endregion

        #region Constructors

        // <summary>
        // Basic constructor
        // </summary>
        // <param name="opType"> The OpType we're interested in processing </param>
        // <param name="nodeProcessDelegate"> The callback to invoke </param>
        protected Rule(OpType opType, ProcessNodeDelegate nodeProcessDelegate)
        {
            DebugCheck.NotNull(nodeProcessDelegate);
            Debug.Assert(opType != OpType.NotValid, "bad OpType");
            Debug.Assert(opType != OpType.Leaf, "bad OpType - Leaf");

            m_opType = opType;
            m_nodeDelegate = nodeProcessDelegate;
        }

        #endregion

        #region protected methods

        #endregion

        #region public methods

        // <summary>
        // Does the rule match the current node?
        // </summary>
        // <param name="node"> the node in question </param>
        // <returns> true, if a match was found </returns>
        internal abstract bool Match(Node node);

        // <summary>
        // We need to invoke the specified callback on the subtree in question - but only
        // if the match succeeds
        // </summary>
        // <param name="ruleProcessingContext"> Current rule processing context </param>
        // <param name="node"> The node (subtree) to process </param>
        // <param name="newNode"> the (possibly) modified subtree </param>
        // <returns> true, if the subtree was modified </returns>
        internal bool Apply(RuleProcessingContext ruleProcessingContext, Node node, out Node newNode)
        {
            // invoke the real callback
            return m_nodeDelegate(ruleProcessingContext, node, out newNode);
        }

        // <summary>
        // The OpType we're interested in transforming
        // </summary>
        internal OpType RuleOpType
        {
            get { return m_opType; }
        }

#if DEBUG
    /// <summary>
    /// The method name for the rule
    /// </summary>
        internal string MethodName
        {
            get { return m_nodeDelegate.Method.Name; }
        }
#endif

        #endregion
    }
}
