// 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.

#nullable disable

using System.Diagnostics;

namespace System.Xaml
{
    public enum XamlNodeType:byte
    {
        None,       // need something to return when un-inited. (and past Eof)
        StartObject,
        GetObject,
        EndObject,
        StartMember,
        EndMember,
        Value,
        NamespaceDeclaration,
    };

    internal delegate void XamlNodeAddDelegate(XamlNodeType nodeType, object data);
    internal delegate void XamlLineInfoAddDelegate(int lineNumber, int linePosition);
    internal delegate XamlNode XamlNodeNextDelegate();
    internal delegate XamlNode XamlNodeIndexDelegate(int idx);

    [DebuggerDisplay("{ToString()}")]
    internal struct XamlNode
    {
        internal enum InternalNodeType:byte { None, StartOfStream, EndOfStream, EndOfAttributes, LineInfo }

        XamlNodeType _nodeType;
        InternalNodeType _internalNodeType;
        private object _data;

        public XamlNodeType NodeType
        {
            get { return _nodeType; }
        }

        public XamlNode(XamlNodeType nodeType)
        {
#if DEBUG
            switch (nodeType)
            {
            case XamlNodeType.EndObject:
            case XamlNodeType.EndMember:
            case XamlNodeType.GetObject:
                break;

            default:
                Debug.Assert(false, "XamlNode Ctor missing data argument");
                break;
            }
#endif
            _nodeType = nodeType;
            _internalNodeType = InternalNodeType.None;
            _data = null;
        }

        public XamlNode(XamlNodeType nodeType, object data)
        {
#if DEBUG
            switch(nodeType)
            {
            case XamlNodeType.StartObject:
                Debug.Assert(data is XamlType, "XamlNode ctor, StartObject data is not a XamlType");
                break;

            case XamlNodeType.StartMember:
                Debug.Assert(data is XamlMember, "XamlNode ctor, StartMember data is not a XamlMember");
                break;

            case XamlNodeType.NamespaceDeclaration:
                Debug.Assert(data is NamespaceDeclaration, "XamlNode ctor, NamespaceDeclaration data is not a NamespaceDeclaration");
                break;

            case XamlNodeType.Value:
                // can be anything;
                break;

            case XamlNodeType.EndObject:
            case XamlNodeType.EndMember:
            case XamlNodeType.GetObject:
                Debug.Assert(data == null, "XamlNode ctor, Internal XamlNode data must be null for this Node type");
                break;

            default:
                Debug.Assert(false, "XamlNode ctor, incorrect ctor called.");
                break;
            }
#endif
            _nodeType = nodeType;
            _internalNodeType = InternalNodeType.None;
            _data = data;
        }

        public XamlNode(InternalNodeType internalNodeType)
        {
            Debug.Assert(internalNodeType == InternalNodeType.EndOfAttributes ||
                            internalNodeType == InternalNodeType.StartOfStream ||
                            internalNodeType == InternalNodeType.EndOfStream, "XamlNode ctor: Illegal Internal node type");
            _nodeType = XamlNodeType.None;
            _internalNodeType = internalNodeType;
            _data = null;
        }

        public XamlNode(LineInfo lineInfo)
        {
            _nodeType = XamlNodeType.None;
            _internalNodeType = InternalNodeType.LineInfo;
            _data = lineInfo;
        }

        public override string ToString()
        {
            string str = String.Format(TypeConverterHelper.InvariantEnglishUS, "{0}: ", NodeType);
            switch(NodeType)
            {
            case XamlNodeType.StartObject:
                str += XamlType.Name;
                break;

            case XamlNodeType.StartMember:
                str += Member.Name;
                break;

            case XamlNodeType.Value:
                str += Value.ToString();
                break;

            case XamlNodeType.NamespaceDeclaration:
                str += NamespaceDeclaration.ToString();
                break;

            case XamlNodeType.None:
                switch(_internalNodeType)
                {
                case InternalNodeType.EndOfAttributes:
                    str += "End Of Attributes";
                    break;

                    case InternalNodeType.StartOfStream:
                    str += "Start Of Stream";
                    break;

                case InternalNodeType.EndOfStream:
                    str += "End Of Stream";
                    break;

                case InternalNodeType.LineInfo:
                    str += "LineInfo: " + LineInfo.ToString();
                    break;
                }
                break;
            }
            return str;
        }

        public NamespaceDeclaration NamespaceDeclaration
        {
            get
            {
                if (NodeType == XamlNodeType.NamespaceDeclaration)
                {
                    return (NamespaceDeclaration)_data;
                }
                return null;
            }
        }

        public XamlType XamlType
        {
            get
            {
                if (NodeType == XamlNodeType.StartObject)
                {
                    return (XamlType)_data;
                }
                return null;
            }
        }

        public object Value
        {
            get
            {
                if (NodeType == XamlNodeType.Value)
                {
                    return _data;
                }
                return null;
            }
        }

        public XamlMember Member
        {
            get
            {
                if (NodeType == XamlNodeType.StartMember)
                {
                    return (XamlMember)_data;
                }
                return null;
            }
        }

        public LineInfo LineInfo
        {
            get
            {
                if (NodeType == XamlNodeType.None)
                {
                    return _data as LineInfo;  // might be null for EOF and EOA.
                }
                return null;
            }
        }

        internal bool IsEof
        {
            get
            {
                if (NodeType == XamlNodeType.None && _internalNodeType == InternalNodeType.EndOfStream)
                {
                    return true;
                }
                return false;
            }
        }

        internal bool IsEndOfAttributes
        {
            get
            {
                if (NodeType == XamlNodeType.None && _internalNodeType == InternalNodeType.EndOfAttributes)
                {
                    return true;
                }
                return false;
            }
        }

        internal bool IsLineInfo
        {
            get
            {
                if (NodeType == XamlNodeType.None && _internalNodeType == InternalNodeType.LineInfo)
                {
                    return true;
                }
                return false;
            }
        }

        internal static bool IsEof_Helper(XamlNodeType nodeType, object data)
        {
            if (nodeType != XamlNodeType.None)
            {
                return false;
            }
            if (data is InternalNodeType)
            {
                InternalNodeType internalNodeType = (InternalNodeType)data;
                if (internalNodeType == InternalNodeType.EndOfStream)
                {
                    return true;
                }
            }
            return false;
        }
    }
}
