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

namespace System.Data.Entity.SqlServer.SqlGen
{
    using System.Collections.Generic;
    using System.Data.Entity.Core.Common.CommandTrees;
    using System.Data.Entity.Core.Metadata.Edm;

    // <summary>
    // A Join symbol is a special kind of Symbol.
    // It has to carry additional information
    // <list type="bullet">
    //     <item>
    //         ColumnList for the list of columns in the select clause if this
    //         symbol represents a sql select statement.  This is set by
    //         <see cref="SqlGenerator.AddDefaultColumns" />
    //         .
    //     </item>
    //     <item>ExtentList is the list of extents in the select clause.</item>
    //     <item>
    //         FlattenedExtentList - if the Join has multiple extents flattened at the
    //         top level, we need this information to ensure that extent aliases are renamed
    //         correctly in
    //         <see cref="SqlSelectStatement.WriteSql" />
    //     </item>
    //     <item>
    //         NameToExtent has all the extents in ExtentList as a dictionary.
    //         This is used by
    //         <see cref="SqlGenerator.Visit(DbPropertyExpression)" />
    //         to flatten
    //         record accesses.
    //     </item>
    //     <item>
    //         IsNestedJoin - is used to determine whether a JoinSymbol is an
    //         ordinary join symbol, or one that has a corresponding SqlSelectStatement.
    //     </item>
    // </list>
    // All the lists are set exactly once, and then used for lookups/enumerated.
    // </summary>
    internal sealed class JoinSymbol : Symbol
    {
        private List<Symbol> columnList;

        internal List<Symbol> ColumnList
        {
            get
            {
                if (null == columnList)
                {
                    columnList = new List<Symbol>();
                }
                return columnList;
            }
            set { columnList = value; }
        }

        private readonly List<Symbol> extentList;

        internal List<Symbol> ExtentList
        {
            get { return extentList; }
        }

        private List<Symbol> flattenedExtentList;

        internal List<Symbol> FlattenedExtentList
        {
            get
            {
                if (null == flattenedExtentList)
                {
                    flattenedExtentList = new List<Symbol>();
                }
                return flattenedExtentList;
            }
            set { flattenedExtentList = value; }
        }

        private readonly Dictionary<string, Symbol> nameToExtent;

        internal Dictionary<string, Symbol> NameToExtent
        {
            get { return nameToExtent; }
        }

        internal bool IsNestedJoin { get; set; }

        public JoinSymbol(string name, TypeUsage type, List<Symbol> extents)
            : base(name, type)
        {
            extentList = new List<Symbol>(extents.Count);
            nameToExtent = new Dictionary<string, Symbol>(extents.Count, StringComparer.OrdinalIgnoreCase);
            foreach (var symbol in extents)
            {
                nameToExtent[symbol.Name] = symbol;
                ExtentList.Add(symbol);
            }
        }
    }
}
