// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//
// Description:
//      Represent a node in the doubly linked list a DocumentSequenceTextContainer
//      maintains for the child-TextContainers it aggregates.  Each child-TextContainer
//      would have a corresponding entry in the list.
//

namespace System.Windows.Documents
{
    using MS.Internal.Documents;
    using System;
    using System.Diagnostics;


    //=====================================================================
    /// <summary>
    /// Represent a node in the doubly linked list a DocumentSequenceTextContainer
    /// maintains for the child-TextContainers it aggregates.  Each child-TextContainer
    /// would have a corresponding entry in the list.
    /// </summary>
    internal sealed class ChildDocumentBlock
    {
        //--------------------------------------------------------------------
        //
        // Enum
        //
        //---------------------------------------------------------------------

        [Flags()]
        internal enum BlockStatus
        {
            None = 0,
            UnloadedBlock = 1,          // Child block has not been loaded
        }

        //--------------------------------------------------------------------
        //
        // Connstructors
        //
        //---------------------------------------------------------------------


        #region Ctors
        internal ChildDocumentBlock(DocumentSequenceTextContainer aggregatedContainer, ITextContainer childContainer)
        {
            Debug.Assert(childContainer != null);
            _aggregatedContainer = aggregatedContainer;
            _container = childContainer;
        }

        internal ChildDocumentBlock(DocumentSequenceTextContainer aggregatedContainer, DocumentReference docRef)
        {
            Debug.Assert(docRef != null);
            _aggregatedContainer = aggregatedContainer;
            _docRef = docRef;
            _SetStatus(BlockStatus.UnloadedBlock);
        }
        #endregion Ctors


        //--------------------------------------------------------------------
        //
        // Internal Properties
        //
        //---------------------------------------------------------------------
        #region Internal Methods

        // Insert a new node into this list.
        internal ChildDocumentBlock InsertNextBlock(ChildDocumentBlock newBlock)
        {
            Debug.Assert(newBlock != null);

            // Setup the new block correctly
            newBlock._nextBlock     = this._nextBlock;
            newBlock._previousBlock = this;

            // Link old next block to the new block
            if (this._nextBlock != null)
            {
                this._nextBlock._previousBlock = newBlock;
            }

            // Link this block to new block
            this._nextBlock = newBlock;

            return newBlock;
        }

        #endregion Internal Methods

        //--------------------------------------------------------------------
        //
        // Internal Properties
        //
        //---------------------------------------------------------------------

        #region Internal Properties
        // The AggregatedContainer that holds the list of ChildDocumentBlock.
        internal DocumentSequenceTextContainer AggregatedContainer
        {
            get
            {
                return _aggregatedContainer;
            }
        }

        // Reference to the child TextContainer
        internal ITextContainer ChildContainer
        {
            get
            {
                _EnsureBlockLoaded();
                return _container;
            }
        }

        // The highlight layer used to notify child TextContainer
        // about TextSelection highlight change.
        internal DocumentSequenceHighlightLayer ChildHighlightLayer
        {
            get
            {
                if (_highlightLayer == null)
                {
                    _highlightLayer = new DocumentSequenceHighlightLayer(_aggregatedContainer);
                    Debug.Assert(ChildContainer.Highlights.GetLayer(typeof(TextSelection)) == null);
                    ChildContainer.Highlights.AddLayer(_highlightLayer);
                }
                return _highlightLayer;
            }
        }


        // Reference to the DocumentReference
        internal DocumentReference DocRef
        {
            get
            {
                return _docRef;
            }
        }

        // End TextPointer of the child container
        internal ITextPointer End
        {
            get
            {
                return ChildContainer.End;
            }
        }

#if DEBUG
        // Get if this entry is non-head/tail block in the linked list
        internal bool IsInsideBlock
        {
            get
            {
                return (this._previousBlock != null && this._nextBlock != null);
            }
        }
#endif

        // Get if this entry is Head of list
        internal bool IsHead
        {
            get
            {
                return (this._previousBlock == null);
            }
        }


        // Get if this entry is Tail of list
        internal bool IsTail
        {
            get
            {
                return (this._nextBlock == null);
            }
        }


        // Previous container entry in the link list
        internal ChildDocumentBlock PreviousBlock
        {
            get
            {
                return _previousBlock;
            }
        }

        // Next container entry in the link list
        internal ChildDocumentBlock NextBlock
        {
            get
            {
                return _nextBlock;
            }
        }

#if DEBUG
        internal uint DebugId
        {
            get
            {
                return _debugId;
            }
        }
#endif
        #endregion Internal Properties

        //--------------------------------------------------------------------
        //
        // Private Methods
        //
        //---------------------------------------------------------------------

        #region Private Methods
        private void _EnsureBlockLoaded()
        {
            if (_HasStatus(BlockStatus.UnloadedBlock))
            {
                _ClearStatus(BlockStatus.UnloadedBlock);

                DocumentsTrace.FixedDocumentSequence.TextOM.Trace($"Loading TextContainer {_docRef}");
                // Load the TextContainer
                IDocumentPaginatorSource idp = _docRef.GetDocument(false /*forceReload*/);
                IServiceProvider isp = idp as IServiceProvider;
                if (isp != null)
                {
                    ITextContainer tc = isp.GetService(typeof(ITextContainer)) as ITextContainer;
                    if (tc != null)
                    {
                        _container = tc;
                        DocumentsTrace.FixedDocumentSequence.TextOM.Trace("Got ITextContainer");
                    }
                }

                if (_container == null)
                {
                    _container = new NullTextContainer();
                }
            }
        }


        private bool _HasStatus(BlockStatus flags)
        {
            return ((_status & flags) == flags);
        }

        private void _SetStatus(BlockStatus flags)
        {
            _status |= flags;
        }

        private void _ClearStatus(BlockStatus flags)
        {
            _status &= (~flags);
        }
        #endregion Private Methods

        //--------------------------------------------------------------------
        //
        // Private Fields
        //
        //---------------------------------------------------------------------

        #region Private Fields
        private readonly DocumentSequenceTextContainer _aggregatedContainer;
        private readonly DocumentReference _docRef;          // Reference to the sub-document
        private ITextContainer _container;           // Child TextContainer
        private DocumentSequenceHighlightLayer  _highlightLayer;  // Highlight layer used to notify child TextContainer highlight changes.
        private BlockStatus _status;                // Status of this block, such as Loaded, etc.
        private ChildDocumentBlock _previousBlock;  // Link to previous block
        private ChildDocumentBlock _nextBlock;      // Link to next block

#if DEBUG
        private uint _debugId = (_debugIdCounter++);
        private static uint _debugIdCounter = 0;
#endif
        #endregion  Private Fields
    }
}
