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

namespace Microsoft.Data.Entity.Design.Model.Entity
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Xml.Linq;
    using Microsoft.Data.Entity.Design.Model.Commands;

    internal class ReferentialConstraint : EFElement
    {
        internal static readonly string ElementName = "ReferentialConstraint";
        internal static readonly string ElementNamePrincipal = "Principal";
        internal static readonly string ElementNameDependent = "Dependent";

        private ReferentialConstraintRole _principal;
        private ReferentialConstraintRole _dependent;

        internal ReferentialConstraint(EFElement parent, XElement element)
            : base(parent, element)
        {
        }

        internal ReferentialConstraintRole Dependent
        {
            get { return _dependent; }
            set
            {
                if (_dependent != null
                    && _dependent != value)
                {
                    _dependent.Dispose();
                }
                _dependent = value;
            }
        }

        internal ReferentialConstraintRole Principal
        {
            get { return _principal; }
            set
            {
                if (_principal != null
                    && _principal != value)
                {
                    _principal.Dispose();
                }
                _principal = value;
            }
        }

        // we unfortunately get a warning from the compiler when we use the "base" keyword in "iterator" types generated by using the
        // "yield return" keyword.  By adding this method, I was able to get around this.  Unfortunately, I wasn't able to figure out
        // a way to implement this once and have derived classes share the implementation (since the "base" keyword is resolved at 
        // compile-time and not at runtime.
        private IEnumerable<EFObject> BaseChildren
        {
            get { return base.Children; }
        }

        internal override IEnumerable<EFObject> Children
        {
            get
            {
                foreach (var efobj in BaseChildren)
                {
                    yield return efobj;
                }

                if (_principal != null)
                {
                    yield return _principal;
                }

                if (_dependent != null)
                {
                    yield return _dependent;
                }
            }
        }

        internal override DeleteEFElementCommand GetDeleteCommand()
        {
            return new DeleteReferentialConstraintCommand(this);
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            if (efContainer == _principal)
            {
                _principal = null;
                return;
            }

            if (efContainer == _dependent)
            {
                _dependent = null;
                return;
            }

            base.OnChildDeleted(efContainer);
        }

#if DEBUG
        internal override ICollection<string> MyChildElementNames()
        {
            var s = base.MyAttributeNames();
            s.Add(ElementNamePrincipal);
            s.Add(ElementNameDependent);
            return s;
        }
#endif

        protected override void PreParse()
        {
            Debug.Assert(State != EFElementState.Parsed, "this object should not already be in the parsed state");

            ClearEFObject(_principal);
            _principal = null;

            ClearEFObject(_dependent);
            _dependent = null;
            base.PreParse();
        }

        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement elem)
        {
            if (elem.Name.LocalName == ElementNamePrincipal)
            {
                _principal = new ReferentialConstraintRole(this, elem);
                _principal.Parse(unprocessedElements);
            }
            else if (elem.Name.LocalName == ElementNameDependent)
            {
                _dependent = new ReferentialConstraintRole(this, elem);
                _dependent.Parse(unprocessedElements);
            }
            else
            {
                return base.ParseSingleElement(unprocessedElements, elem);
            }
            return true;
        }

        internal bool IsPrimaryKeyToPrimaryKey()
        {
            if (Dependent == null)
            {
                return false;
            }

            foreach (var pr in Dependent.PropertyRefs)
            {
                if (pr.Name.Target != null)
                {
                    if (pr.Name.Target.IsKeyProperty == false)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }
}
