//---------------------------------------------------------------------
// <copyright file="TypeHelpers.cs" company="Microsoft">
//      Copyright(c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       [....]
// @backupOwner [....]
//---------------------------------------------------------------------

namespace System.Data.Common
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common.CommandTrees;
    using System.Data.Metadata.Edm;
    using System.Data.Objects.ELinq;
    using System.Diagnostics;
    using System.Globalization;

    /// <summary>
    /// Represents a set of static Type helpers operating on TypeMetadata
    /// </summary>
    internal static class TypeHelpers
    {
        #region Assert Types

        /// <summary>
        /// Asserts types are in Model space
        /// </summary>
        /// <param name="typeUsage"></param>
        [Conditional("DEBUG")]
        internal static void AssertEdmType(TypeUsage typeUsage)
        {
            EdmType type = typeUsage.EdmType;
            if (TypeSemantics.IsCollectionType(typeUsage))
            {
                AssertEdmType(TypeHelpers.GetElementTypeUsage(typeUsage));
            }
            else if (TypeSemantics.IsStructuralType(typeUsage) && !Helper.IsComplexType(typeUsage.EdmType) && !Helper.IsEntityType(typeUsage.EdmType))
            {
                foreach (EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(typeUsage))
                {
                    AssertEdmType(m.TypeUsage);
                }
            }
            else if (TypeSemantics.IsPrimitiveType(typeUsage))
            {
                PrimitiveType pType = type as PrimitiveType;
                if (null != pType)
                {
                    if (pType.DataSpace != DataSpace.CSpace)
                        throw new NotSupportedException(String.Format(CultureInfo.InvariantCulture, "PrimitiveType must be CSpace '{0}'", typeUsage.ToString()));
                }
            }
        }

        /// <summary>
        /// Asserts querycommandtrees are in model space type terms
        /// </summary>
        /// <param name="commandTree"></param>
        [Conditional("DEBUG")]
        internal static void AssertEdmType(DbCommandTree commandTree)
        {
            DbQueryCommandTree queryCommandTree = commandTree as DbQueryCommandTree;
            if (null != queryCommandTree)
            {
                AssertEdmType(queryCommandTree.Query.ResultType);
            }
        }
        #endregion

        //
        // Type Semantics
        //
        #region Type Semantics

        /// <summary>
        /// Determines whether a given typeUsage is valid as OrderBy sort key
        /// </summary>
        /// <param name="typeUsage"></param>
        /// <returns></returns>
        internal static bool IsValidSortOpKeyType(TypeUsage typeUsage)
        {
            if (TypeSemantics.IsRowType(typeUsage))
            {
                RowType rowType = (RowType)typeUsage.EdmType;
                foreach (EdmProperty property in rowType.Properties)
                {
                    if (!IsValidSortOpKeyType(property.TypeUsage))
                    {
                        return false;
                    }
                }
                return true;
            }
            else
            {
                return TypeSemantics.IsOrderComparable(typeUsage);
            }
        }

        /// <summary>
        /// Determines whether a given typeusage is valid as GroupBy key
        /// </summary>
        /// <param name="typeUsage"></param>
        /// <returns></returns>
        internal static bool IsValidGroupKeyType(TypeUsage typeUsage)
        {
            return IsSetComparableOpType(typeUsage);
        }

        /// <summary>
        /// Determine wheter a given typeusage is valid for Distinct operator
        /// </summary>
        /// <param name="typeUsage"></param>
        /// <returns></returns>
        internal static bool IsValidDistinctOpType(TypeUsage typeUsage)
        {
            return IsSetComparableOpType(typeUsage);
        }

        /// <summary>
        /// Determine wheter a given typeusage is valid for set comparison operator such as UNION, INTERSECT and EXCEPT
        /// </summary>
        /// <param name="typeUsage"></param>
        /// <returns></returns>
        internal static bool IsSetComparableOpType(TypeUsage typeUsage)
        {
            if (Helper.IsEntityType(typeUsage.EdmType)    ||
                Helper.IsPrimitiveType(typeUsage.EdmType) ||
                Helper.IsEnumType(typeUsage.EdmType)      ||
                Helper.IsRefType(typeUsage.EdmType)        )
            {
                return true;
            }
            else if (TypeSemantics.IsRowType(typeUsage))
            {
                RowType rowType = (RowType)typeUsage.EdmType;
                foreach (EdmProperty property in rowType.Properties)
                {
                    if (!IsSetComparableOpType(property.TypeUsage))
                    {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// Returns true if typeUsage type is valid for IS [NOT] NULL (expr) operator
        /// </summary>
        /// <param name="typeUsage"></param>
        /// <returns></returns>
        internal static bool IsValidIsNullOpType(TypeUsage typeUsage)
        {
            return TypeSemantics.IsReferenceType(typeUsage) ||
                   TypeSemantics.IsEntityType(typeUsage)    ||
                   TypeSemantics.IsScalarType(typeUsage);
        }


        internal static bool IsValidInOpType(TypeUsage typeUsage)
        {
            return TypeSemantics.IsReferenceType(typeUsage) ||
                   TypeSemantics.IsEntityType(typeUsage) ||
                   TypeSemantics.IsScalarType(typeUsage);
        }

        internal static TypeUsage GetCommonTypeUsage(TypeUsage typeUsage1, TypeUsage typeUsage2)
        {
            return TypeSemantics.GetCommonType(typeUsage1, typeUsage2);
        }

        internal static TypeUsage GetCommonTypeUsage(IEnumerable<TypeUsage> types)
        {
            TypeUsage commonType = null;
            foreach (TypeUsage testType in types)
            {
                if (null == testType)
                {
                    return null;
                }

                if (null == commonType)
                {
                    commonType = testType;
                }
                else
                {
                    commonType = TypeSemantics.GetCommonType(commonType, testType);
                    if (null == commonType)
                    {
                        break;
                    }
                }
            }
            return commonType;
        }

        #endregion

        //
        // Type property extractors
        //
        #region Type property extractors
        
        internal static bool TryGetClosestPromotableType(TypeUsage fromType, out TypeUsage promotableType)
        {
            promotableType = null;
            if (Helper.IsPrimitiveType(fromType.EdmType))
            {
                PrimitiveType fromPrimitiveType = (PrimitiveType)fromType.EdmType;
                IList<PrimitiveType> promotableTypes = EdmProviderManifest.Instance.GetPromotionTypes(fromPrimitiveType);
                int index = promotableTypes.IndexOf(fromPrimitiveType);
                if (-1 != index && index + 1 < promotableTypes.Count)
                {
                    promotableType = TypeUsage.Create(promotableTypes[index + 1]);
                }
            }
            return (null != promotableType);
        }


        #endregion

        //
        // Facet Helpers
        //
        #region Facet Helpers

        internal static bool TryGetBooleanFacetValue(TypeUsage type, string facetName, out bool boolValue)
        {
            boolValue = false;
            Facet boolFacet;
            if (type.Facets.TryGetValue(facetName, false, out boolFacet) && boolFacet.Value != null)
            {
                boolValue = (bool)boolFacet.Value;
                return true;
            }

            return false;
        }

        internal static bool TryGetByteFacetValue(TypeUsage type, string facetName, out byte byteValue)
        {
            byteValue = 0;
            Facet byteFacet;
            if (type.Facets.TryGetValue(facetName, false, out byteFacet) && byteFacet.Value != null && !Helper.IsUnboundedFacetValue(byteFacet))
            {
                byteValue = (byte)byteFacet.Value;
                return true;
            }

            return false;
        }

        internal static bool TryGetIntFacetValue(TypeUsage type, string facetName, out int intValue)
        {
            intValue = 0;
            Facet intFacet;
            if (type.Facets.TryGetValue(facetName, false, out intFacet) && intFacet.Value != null && !Helper.IsUnboundedFacetValue(intFacet) && !Helper.IsVariableFacetValue(intFacet))
            {
                intValue = (int)intFacet.Value;
                return true;
            }

            return false;
        }

        internal static bool TryGetIsFixedLength(TypeUsage type, out bool isFixedLength)
        {
            if (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String) &&
                !TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Binary))
            {
                isFixedLength = false;
                return false;
            }

            // Binary and String MaxLength facets share the same name
            return TypeHelpers.TryGetBooleanFacetValue(type, DbProviderManifest.FixedLengthFacetName, out isFixedLength);
        }

        internal static bool TryGetIsUnicode(TypeUsage type, out bool isUnicode)
        {
            if (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String))
            {
                isUnicode = false;
                return false;
            }

            return TypeHelpers.TryGetBooleanFacetValue(type, DbProviderManifest.UnicodeFacetName, out isUnicode);
        }

        internal static bool IsFacetValueConstant(TypeUsage type, string facetName)
        {
            // Binary and String FixedLength facets share the same name
            return Helper.GetFacet(((PrimitiveType)type.EdmType).FacetDescriptions, facetName).IsConstant;
        }

        internal static bool TryGetMaxLength(TypeUsage type, out int maxLength)
        {
            if (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String) &&
                !TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Binary))
            {
                maxLength = 0;
                return false;
            }

            // Binary and String FixedLength facets share the same name
            return TypeHelpers.TryGetIntFacetValue(type, DbProviderManifest.MaxLengthFacetName, out maxLength);
        }

        internal static bool TryGetPrecision(TypeUsage type, out byte precision)
        {
            if (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Decimal))
            {
                precision = 0;
                return false;
            }

            return TypeHelpers.TryGetByteFacetValue(type, DbProviderManifest.PrecisionFacetName, out precision);
        }

        internal static bool TryGetScale(TypeUsage type, out byte scale)
        {
            if (!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.Decimal))
            {
                scale = 0;
                return false;
            }

            return TypeHelpers.TryGetByteFacetValue(type, DbProviderManifest.ScaleFacetName, out scale);
        }

        internal static bool TryGetPrimitiveTypeKind(TypeUsage type, out PrimitiveTypeKind typeKind)
        {
            if (type != null && type.EdmType != null && type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
            {
                typeKind = ((PrimitiveType)type.EdmType).PrimitiveTypeKind;
                return true;
            }

            typeKind = default(PrimitiveTypeKind);
            return false;
        }

        #endregion

        //
        // Type Constructors
        //
        #region Type Constructors
        internal static CollectionType CreateCollectionType(TypeUsage elementType)
        {
            return new CollectionType(elementType);
        }

        internal static TypeUsage CreateCollectionTypeUsage(TypeUsage elementType)
        {
            return CreateCollectionTypeUsage(elementType, false /* readOnly */ );
        }

        internal static TypeUsage CreateCollectionTypeUsage(TypeUsage elementType, bool readOnly)
        {
            return TypeUsage.Create(new CollectionType(elementType));
        }

        internal static RowType CreateRowType(IEnumerable<KeyValuePair<string, TypeUsage>> columns)
        {
            return CreateRowType(columns, null);
        }

        internal static RowType CreateRowType(IEnumerable<KeyValuePair<string, TypeUsage>> columns, InitializerMetadata initializerMetadata)
        {
            List<EdmProperty> rowElements = new List<EdmProperty>();
            foreach (KeyValuePair<string, TypeUsage> kvp in columns)
            {
                rowElements.Add(new EdmProperty(kvp.Key, kvp.Value));
            }
            return new RowType(rowElements, initializerMetadata);
        }

        internal static TypeUsage CreateRowTypeUsage(IEnumerable<KeyValuePair<string, TypeUsage>> columns, bool readOnly)
        {
            return TypeUsage.Create(CreateRowType(columns));
        }

        internal static RefType CreateReferenceType(EntityTypeBase entityType)
        {
            return new RefType((EntityType)entityType);
        }

        internal static TypeUsage CreateReferenceTypeUsage(EntityType entityType)
        {
            return TypeUsage.Create(CreateReferenceType(entityType));
        }

        /// <summary>
        /// Creates metadata for a new row type with column names and types based on the key members of the specified Entity type
        /// </summary>
        /// <param name="entityType">The Entity type that provides the Key members on which the column names and types of the new row type will be based</param>
        /// <returns>A new RowType info with column names and types corresponding to the Key members of the specified Entity type</returns>
        internal static RowType CreateKeyRowType(EntityTypeBase entityType)
        {
            IEnumerable<EdmMember> entityKeys = entityType.KeyMembers;
            if (null == entityKeys)
            {
                throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntityTypeNullKeyMembersInvalid, "entityType");
            }

            List<KeyValuePair<string, TypeUsage>> resultCols = new List<KeyValuePair<string, TypeUsage>>();
            //int idx = 0;
            foreach (EdmProperty keyProperty in entityKeys)
            {
                //this.CheckMember(keyProperty, "property", CommandTreeUtils.FormatIndex("entityType.KeyMembers", idx++));
                resultCols.Add(new KeyValuePair<string, TypeUsage>(keyProperty.Name, Helper.GetModelTypeUsage(keyProperty)));
            }

            if (resultCols.Count < 1)
            {
                throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntityTypeEmptyKeyMembersInvalid, "entityType");
            }

            return TypeHelpers.CreateRowType(resultCols);
        }

        /// <summary>
        /// Gets primitive type usage for <paramref name="scalarType"/>.
        /// </summary>
        /// <param name="scalarType">Primitive or enum type usage.</param>
        /// <returns>
        /// Primitive type usage for <paramref name="scalarType"/>.
        /// </returns>
        /// <remarks>
        /// For enum types a new type usage based on the underlying type will be created. For primitive types
        /// the value passed to the function will be returned.
        /// </remarks>
        internal static TypeUsage GetPrimitiveTypeUsageForScalar(TypeUsage scalarType)
        {
            Debug.Assert(scalarType != null, "scalarType != null");
            Debug.Assert(TypeSemantics.IsScalarType(scalarType), "Primitive or enum type expected.");

            return TypeSemantics.IsEnumerationType(scalarType) ?
                CreateEnumUnderlyingTypeUsage(scalarType) :
                scalarType;
        }

        /// <summary>
        /// Factory method for creating a type usage for underlying type of enum type usage.
        /// </summary>
        /// <param name="enumTypeUsage">Enum type usage used to create an underlying type usage of.</param>
        /// <returns>Type usage for the underlying enum type.</returns>
        internal static TypeUsage CreateEnumUnderlyingTypeUsage(TypeUsage enumTypeUsage)
        {
            Debug.Assert(enumTypeUsage != null, "enumTypeUsage != null");
            Debug.Assert(TypeSemantics.IsEnumerationType(enumTypeUsage), "enumTypeUsage is not an enumerated type");

            return TypeUsage.Create(Helper.GetUnderlyingEdmTypeForEnumType(enumTypeUsage.EdmType), enumTypeUsage.Facets);
        }

        /// <summary>
        /// Factory method for creating a type usage for underlying union type of spatial type usage.
        /// </summary>
        /// <param name="spatialTypeUsage">Spatial type usage used to create a union type usage of.</param>
        /// <returns>Type usage for the spatial union type of the correct topology.</returns>
        internal static TypeUsage CreateSpatialUnionTypeUsage(TypeUsage spatialTypeUsage)
        {
            Debug.Assert(spatialTypeUsage != null, "spatialTypeUsage != null");
            Debug.Assert(TypeSemantics.IsStrongSpatialType(spatialTypeUsage), "spatialTypeUsage is not a strong spatial type");
            return TypeUsage.Create(Helper.GetSpatialNormalizedPrimitiveType(spatialTypeUsage.EdmType), spatialTypeUsage.Facets);
        }

        #endregion

        //
        // Type extractors
        //
        #region Type Extractors

        /// <summary>
        /// Retrieves Properties and/or RelationshipEnds declared by the specified type or any base type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static IBaseList<EdmMember> GetAllStructuralMembers(TypeUsage type)
        {
            return GetAllStructuralMembers(type.EdmType);
        }

        internal static IBaseList<EdmMember> GetAllStructuralMembers(EdmType edmType)
        {
            System.Diagnostics.Debug.Assert(edmType != null);
            switch (edmType.BuiltInTypeKind)
            {
                case BuiltInTypeKind.AssociationType:
                    return (IBaseList<EdmMember>)((AssociationType)edmType).AssociationEndMembers;
                case BuiltInTypeKind.ComplexType:
                    return (IBaseList<EdmMember>)((ComplexType)edmType).Properties;
                case BuiltInTypeKind.EntityType:
                    return (IBaseList<EdmMember>)((EntityType)edmType).Properties;
                case BuiltInTypeKind.RowType:
                    return (IBaseList<EdmMember>)((RowType)edmType).Properties;
                default:
                    return EmptyArrayEdmProperty;
            }
        }

        /// <summary>
        /// Retrieves Properties and/or RelationshipEnds declared by (and ONLY by) the specified type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static IEnumerable GetDeclaredStructuralMembers(TypeUsage type)
        {
            return GetDeclaredStructuralMembers(type.EdmType);
        }

        /// <summary>
        /// Retrieves Properties and/or RelationshipEnds declared by (and ONLY by) the specified type.
        /// </summary>
        /// <param name="edmType"></param>
        /// <returns></returns>
        internal static IEnumerable GetDeclaredStructuralMembers(EdmType edmType)
        {
            switch (edmType.BuiltInTypeKind)
            {
                case BuiltInTypeKind.AssociationType:
                    return ((AssociationType)edmType).GetDeclaredOnlyMembers<AssociationEndMember>();
                case BuiltInTypeKind.ComplexType:
                    return ((ComplexType)edmType).GetDeclaredOnlyMembers<EdmProperty>();
                case BuiltInTypeKind.EntityType:
                    return ((EntityType)edmType).GetDeclaredOnlyMembers<EdmProperty>();
                case BuiltInTypeKind.RowType:
                    return ((RowType)edmType).GetDeclaredOnlyMembers<EdmProperty>();
                default:
                    return EmptyArrayEdmProperty;
            }
        }

        internal static readonly ReadOnlyMetadataCollection<EdmMember> EmptyArrayEdmMember = new ReadOnlyMetadataCollection<EdmMember>(new MetadataCollection<EdmMember>().SetReadOnly());
        internal static readonly FilteredReadOnlyMetadataCollection<EdmProperty, EdmMember> EmptyArrayEdmProperty = new FilteredReadOnlyMetadataCollection<EdmProperty, EdmMember>(EmptyArrayEdmMember, null);

        internal static ReadOnlyMetadataCollection<EdmProperty> GetProperties(TypeUsage typeUsage)
        {
            return GetProperties(typeUsage.EdmType);
        }

        internal static ReadOnlyMetadataCollection<EdmProperty> GetProperties(EdmType edmType)
        {
            switch (edmType.BuiltInTypeKind)
            {
                case BuiltInTypeKind.ComplexType:
                    return ((ComplexType)edmType).Properties;
                case BuiltInTypeKind.EntityType:
                    return ((EntityType)edmType).Properties;
                case BuiltInTypeKind.RowType:
                    return ((RowType)edmType).Properties;
                default:
                    return EmptyArrayEdmProperty;
            }
        }

        internal static TypeUsage GetElementTypeUsage(TypeUsage type)
        {
            if (TypeSemantics.IsCollectionType(type))
            {
                return ((CollectionType)type.EdmType).TypeUsage;
            }
            else if (TypeSemantics.IsReferenceType(type))
            {
                return TypeUsage.Create(((RefType)type.EdmType).ElementType);
            }

            return null;
        }

        /// <summary>
        /// Returns row type if supplied function is a tvf returning Collection(RowType), otherwise null.
        /// </summary>
        internal static RowType GetTvfReturnType(EdmFunction tvf)
        {
            if (tvf.ReturnParameter != null && TypeSemantics.IsCollectionType(tvf.ReturnParameter.TypeUsage))
            {
                var expectedElementTypeUsage = ((CollectionType)tvf.ReturnParameter.TypeUsage.EdmType).TypeUsage;
                if (TypeSemantics.IsRowType(expectedElementTypeUsage))
                {
                    return (RowType)expectedElementTypeUsage.EdmType;
                }
            }
            return null;
        }

        //
        // Element type
        //
        internal static bool TryGetCollectionElementType(TypeUsage type, out TypeUsage elementType)
        {
            CollectionType collectionType;
            if (TypeHelpers.TryGetEdmType<CollectionType>(type, out collectionType))
            {
                elementType = collectionType.TypeUsage;
                return (elementType != null);
            }

            elementType = null;
            return false;
        }

        /// <summary>
        /// If the type refered to by the TypeUsage is a RefType, extracts the EntityType and returns true,
        /// otherwise returns false.
        /// </summary>
        /// <param name="type">TypeUsage that may or may not refer to a RefType</param>
        /// <param name="referencedEntityType">Non-null if the TypeUsage refers to a RefType, null otherwise</param>
        /// <returns>True if the TypeUsage refers to a RefType, false otherwise</returns>
        internal static bool TryGetRefEntityType(TypeUsage type, out EntityType referencedEntityType)
        {
            RefType refType;
            if (TryGetEdmType<RefType>(type, out refType) &&
                Helper.IsEntityType(refType.ElementType))
            {
                referencedEntityType = (EntityType)refType.ElementType;
                return true;
            }

            referencedEntityType = null;
            return false;
        }

        internal static TEdmType GetEdmType<TEdmType>(TypeUsage typeUsage)
            where TEdmType : EdmType
        {
            return (TEdmType)typeUsage.EdmType;
        }

        internal static bool TryGetEdmType<TEdmType>(TypeUsage typeUsage, out TEdmType type)
            where TEdmType : EdmType
        {
            type = typeUsage.EdmType as TEdmType;
            return (type != null);
        }
        #endregion

        //
        // Misc
        //
        #region Misc
        internal static TypeUsage GetReadOnlyType(TypeUsage type)
        {
            if (!(type.IsReadOnly))
            {
                type.SetReadOnly();
            }
            return type;
        }

        //
        // Type Description
        //

        internal static string GetFullName(TypeUsage type)
        {
            return type.ToString();
        }
                
        internal static string GetFullName(EdmType type)
        {
            return GetFullName(type.NamespaceName, type.Name);
        }

        internal static string GetFullName(EntitySetBase entitySet)
        {
            Debug.Assert(entitySet.EntityContainer != null, "entitySet.EntityContainer is null");
            return GetFullName(entitySet.EntityContainer.Name, entitySet.Name);
        }
                
        internal static string GetFullName(string qualifier, string name)
        {
            if (string.IsNullOrEmpty(qualifier))
            {
                return string.Format(CultureInfo.InvariantCulture, "{0}", name);
            }
            else
            {
                return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", qualifier, name);
            }
        }

        /// <summary>
        /// Converts the given CLR type into a DbType
        /// </summary>
        /// <param name="clrType">The CLR type to convert</param>
        /// <returns></returns>
        internal static DbType ConvertClrTypeToDbType(Type clrType)
        {
            switch (Type.GetTypeCode(clrType))
            {
                case TypeCode.Empty:
                    throw EntityUtil.InvalidDataType(TypeCode.Empty);

                case TypeCode.Object:
                    if (clrType == typeof(System.Byte[]))
                    {
                        return DbType.Binary;
                    }
                    if (clrType == typeof(System.Char[]))
                    {
                        // Always treat char and char[] as string
                        return DbType.String;
                    }
                    else if (clrType == typeof(System.Guid))
                    {
                        return DbType.Guid;
                    }
                    else if (clrType == typeof(System.TimeSpan))
                    {
                        return DbType.Time;
                    }
                    else if (clrType == typeof(System.DateTimeOffset))
                    {
                        return DbType.DateTimeOffset;
                    }

                    return DbType.Object;

                case TypeCode.DBNull:
                    return DbType.Object;
                case TypeCode.Boolean:
                    return DbType.Boolean;
                case TypeCode.SByte:
                    return DbType.SByte;
                case TypeCode.Byte:
                    return DbType.Byte;
                case TypeCode.Char:
                    // Always treat char and char[] as string
                    return DbType.String;
                case TypeCode.Int16:
                    return DbType.Int16;
                case TypeCode.UInt16:
                    return DbType.UInt16;
                case TypeCode.Int32:
                    return DbType.Int32;
                case TypeCode.UInt32:
                    return DbType.UInt32;
                case TypeCode.Int64:
                    return DbType.Int64;
                case TypeCode.UInt64:
                    return DbType.UInt64;
                case TypeCode.Single:
                    return DbType.Single;
                case TypeCode.Double:
                    return DbType.Double;
                case TypeCode.Decimal:
                    return DbType.Decimal;
                case TypeCode.DateTime:
                    return DbType.DateTime;
                case TypeCode.String:
                    return DbType.String;
                default:
                    throw EntityUtil.UnknownDataTypeCode(clrType, Type.GetTypeCode(clrType));
            }
        }

        internal static bool IsIntegerConstant(TypeUsage valueType, object value, long expectedValue)
        {
            if (!TypeSemantics.IsIntegerNumericType(valueType))
            {
                return false;
            }

            if (null == value)
            {
                return false;
            }

            PrimitiveType intType = (PrimitiveType)valueType.EdmType;
            switch (intType.PrimitiveTypeKind)
            {
                case PrimitiveTypeKind.Byte:
                    return (expectedValue == (byte)value);

                case PrimitiveTypeKind.Int16:
                    return (expectedValue == (short)value);

                case PrimitiveTypeKind.Int32:
                    return (expectedValue == (int)value);

                case PrimitiveTypeKind.Int64:
                    return (expectedValue == (long)value);

                case PrimitiveTypeKind.SByte:
                    return (expectedValue == (sbyte)value);

                default:
                    {
                        Debug.Assert(false, "Integer primitive type was not one of Byte, Int16, Int32, Int64, SByte?");
                        return false;
                    }
            }
        }

        /// <summary>
        /// returns a Typeusage 
        /// </summary>
        /// <param name="primitiveTypeKind"></param>
        /// <returns></returns>
        static internal TypeUsage GetLiteralTypeUsage(PrimitiveTypeKind primitiveTypeKind)
        {
            // all clr strings by default are unicode
            return GetLiteralTypeUsage(primitiveTypeKind, true /* unicode */);
        }

        static internal TypeUsage GetLiteralTypeUsage(PrimitiveTypeKind primitiveTypeKind, bool isUnicode)
        {
            TypeUsage typeusage;
            PrimitiveType primitiveType = EdmProviderManifest.Instance.GetPrimitiveType(primitiveTypeKind);
            switch (primitiveTypeKind)
            {
                case PrimitiveTypeKind.String:
                    typeusage = TypeUsage.Create(primitiveType,
                        new FacetValues{ Unicode = isUnicode, MaxLength = TypeUsage.DefaultMaxLengthFacetValue, FixedLength = false, Nullable = false});
                    break;

                default:
                    typeusage = TypeUsage.Create(primitiveType,
                        new FacetValues{ Nullable = false });
                    break;
            }
            return typeusage;
        }

        #endregion

        #region EdmFunction Helpers
        internal static bool IsCanonicalFunction(EdmFunction function)
        {
            bool isCanonicalFunction = (function.DataSpace == DataSpace.CSpace && function.NamespaceName == EdmConstants.EdmNamespace);

            Debug.Assert(!isCanonicalFunction || (isCanonicalFunction && !function.HasUserDefinedBody),
                "Canonical function '" + function.FullName + "' can not have a user defined body");

            return isCanonicalFunction;
        }
        #endregion
    }
}
