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

namespace System.Data.Entity.Core.Common.Internal.Materialization
{
    // <summary>
    // A coordinator is responsible for tracking state and processing result in a root or nested query
    // result collection. The coordinator exists within a graph, and knows its Parent, (First)Child,
    // and Next sibling. This allows the Shaper to use the coordinator as a simple state machine when
    // consuming store reader results.
    // </summary>
    internal abstract class Coordinator
    {
        #region State

        // <summary>
        // The factory used to generate this coordinator instance. Contains delegates used
        // by the Shaper during result enumeration.
        // </summary>
        internal readonly CoordinatorFactory CoordinatorFactory;

        // <summary>
        // Parent coordinator (the coordinator producing rows containing this collection).
        // If this is the root, null.
        // </summary>
        internal readonly Coordinator Parent;

        // <summary>
        // First coordinator for nested results below this collection. When reading a new row
        // for this coordinator, we walk down to the Child.
        // NOTE:: this cannot be readonly because we can't know both the parent and the child
        // at initialization time; we set the Child in the parent's constructor.
        // </summary>
        public Coordinator Child { get; protected set; }

        // <summary>
        // Next coordinator at this depth. Once we're done consuming results for this reader,
        // we move on to this.Next.
        // </summary>
        internal readonly Coordinator Next;

        // <summary>
        // Indicates whether data has been read for the collection being aggregated or yielded
        // by this coordinator.
        // </summary>
        public bool IsEntered { get; protected set; }

        // <summary>
        // Indicates whether this is the top level coordinator for a query.
        // </summary>
        internal bool IsRoot
        {
            get { return null == Parent; }
        }

        #endregion

        protected Coordinator(CoordinatorFactory coordinatorFactory, Coordinator parent, Coordinator next)
        {
            CoordinatorFactory = coordinatorFactory;
            Parent = parent;
            Next = next;
        }

        #region "Public" Surface Area

        // <summary>
        // Registers this hierarchy of coordinators in the given shaper.
        // </summary>
        internal void Initialize(Shaper shaper)
        {
            ResetCollection(shaper);

            // Add this coordinator to the appropriate state slot in the 
            // shaper so that it is available to materialization delegates.
            shaper.State[CoordinatorFactory.StateSlot] = this;

            if (null != Child)
            {
                Child.Initialize(shaper);
            }
            if (null != Next)
            {
                Next.Initialize(shaper);
            }
        }

        // <summary>
        // Determines the maximum depth of this subtree.
        // </summary>
        internal int MaxDistanceToLeaf()
        {
            var maxDistance = 0;
            var child = Child;
            while (null != child)
            {
                maxDistance = Math.Max(maxDistance, child.MaxDistanceToLeaf() + 1);
                child = child.Next;
            }
            return maxDistance;
        }

        // <summary>
        // This method is called when the current collection is finished and it's time to move to the next collection.
        // Recursively initializes children and siblings as well.
        // </summary>
        internal abstract void ResetCollection(Shaper shaper);

        // <summary>
        // Precondition: the current row has data for the coordinator.
        // Side-effects: updates keys currently stored in state and updates IsEntered if a new value is encountered.
        // Determines whether the row contains the next element in this collection.
        // </summary>
        internal bool HasNextElement(Shaper shaper)
        {
            // check if this row contains a new element for this coordinator
            var result = false;

            if (!IsEntered
                || !CoordinatorFactory.CheckKeys(shaper))
            {
                // remember initial keys values
                CoordinatorFactory.SetKeys(shaper);
                IsEntered = true;
                result = true;
            }

            return result;
        }

        // <summary>
        // Precondition: the current row has data and contains a new element for the coordinator.
        // Reads the next element in this collection.
        // </summary>
        internal abstract void ReadNextElement(Shaper shaper);

        #endregion
    }
}
