// Copyright (C) 2010-2020 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Denis Krjuchkov
// Created:    2010.02.11

using System.Linq;
using Xtensive.Core;
using Xtensive.Sql;
using Xtensive.Sql.Info;

namespace Xtensive.Orm.Providers
{
  internal static class ProviderInfoBuilder
  {
    public static ProviderInfo Build(string providerName, SqlDriver driver)
    {
      var coreServerInfo = driver.CoreServerInfo;
      var serverInfo = driver.ServerInfo;
      var queryFeatures = serverInfo.Query.Features;
      var serverFeatures = serverInfo.ServerFeatures;
      var indexFeatures = serverInfo.Index.Features;

      var f = ProviderFeatures.None;
      if (queryFeatures.Supports(QueryFeatures.DdlBatches))
        f |= ProviderFeatures.DdlBatches;
      if (queryFeatures.Supports(QueryFeatures.DmlBatches))
        f |= ProviderFeatures.DmlBatches;
      if (indexFeatures.Supports(IndexFeatures.Clustered))
        f |= ProviderFeatures.ClusteredIndexes;
      if (indexFeatures.Supports(IndexFeatures.Filtered))
        f |= ProviderFeatures.PartialIndexes;
      if (serverInfo.Collation!=null)
        f |= ProviderFeatures.Collations;
      if (serverInfo.ForeignKey!=null) {
        f |= ProviderFeatures.ForeignKeyConstraints;
        if (serverInfo.ForeignKey.Features.Supports(ForeignKeyConstraintFeatures.Deferrable))
          f |= ProviderFeatures.DeferrableConstraints;
      }
      if (indexFeatures.Supports(IndexFeatures.NonKeyColumns))
        f |= ProviderFeatures.IncludedColumns;
      if (indexFeatures.Supports(IndexFeatures.SortOrder))
        f |= ProviderFeatures.KeyColumnSortOrder;
      if (serverInfo.Sequence!=null)
        f |= ProviderFeatures.Sequences;
      else {
        if (serverInfo.Identity!=null && serverInfo.Identity.Features.Supports(IdentityFeatures.Increment))
          f |= ProviderFeatures.ArbitraryIdentityIncrement;
        if (serverInfo.Identity!=null && serverInfo.Identity.Features.Supports(IdentityFeatures.AutoIncrementSettingsInMemory))
          f |= ProviderFeatures.AutoIncrementSettingsInMemory;
      }
      if (queryFeatures.Supports(QueryFeatures.CrossApply))
        f |= ProviderFeatures.Apply;
      if (serverFeatures.Supports(ServerFeatures.LargeObjects))
        f |= ProviderFeatures.LargeObjects;
      if (serverFeatures.Supports(ServerFeatures.Savepoints))
        f |= ProviderFeatures.Savepoints;
      if (queryFeatures.Supports(QueryFeatures.FullBooleanExpressionSupport))
        f |= ProviderFeatures.FullFeaturedBooleanExpressions;
      if (queryFeatures.Supports(QueryFeatures.NamedParameters))
        f |= ProviderFeatures.NamedParameters;
      if (queryFeatures.Supports(QueryFeatures.UpdateFrom))
        f |= ProviderFeatures.UpdateFrom;
      if (queryFeatures.Supports(QueryFeatures.DeleteFrom))
        f |= ProviderFeatures.DeleteFrom;
      if (queryFeatures.Supports(QueryFeatures.Limit))
        f |= ProviderFeatures.Take | ProviderFeatures.NativeTake;
      if (queryFeatures.Supports(QueryFeatures.Offset))
        f |= ProviderFeatures.Skip | ProviderFeatures.NativeSkip;
      if (queryFeatures.Supports(QueryFeatures.RowNumber))
        f |= ProviderFeatures.RowNumber | ProviderFeatures.Paging;
      if (serverFeatures.Supports(ServerFeatures.MultipleResultsViaCursorParameters))
        f |= ProviderFeatures.MultipleResultsViaCursorParameters;
      if (coreServerInfo.MultipleActiveResultSets)
        f |= ProviderFeatures.MultipleActiveResultSets;
      if (queryFeatures.Supports(QueryFeatures.InsertDefaultValues))
        f |= ProviderFeatures.InsertDefaultValues;
      if (queryFeatures.Supports(QueryFeatures.UpdateDefaultValues))
        f |= ProviderFeatures.UpdateDefaultValues;
      if (queryFeatures.Supports(QueryFeatures.ScalarSubquery))
        f |= ProviderFeatures.ScalarSubqueries;
      if (queryFeatures.Supports(QueryFeatures.MultischemaQueries))
        f |= ProviderFeatures.Multischema;
      if (queryFeatures.Supports(QueryFeatures.MultidatabaseQueries))
        f |= ProviderFeatures.Multidatabase;
      if (serverFeatures.Supports(ServerFeatures.TransactionalDdl))
        f |= ProviderFeatures.TransactionalDdl;
      if (serverFeatures.Supports(ServerFeatures.TransactionalFullTextDdl))
        f |= ProviderFeatures.TransactionalFullTextDdl;
      if (queryFeatures.Supports(QueryFeatures.PagingRequiresOrderBy))
        f |= ProviderFeatures.PagingRequiresOrderBy;
      if (queryFeatures.Supports(QueryFeatures.ZeroLimitIsError))
        f |= ProviderFeatures.ZeroLimitIsError;
      if (serverFeatures.Supports(ServerFeatures.TransactionalKeyGenerators))
        f |= ProviderFeatures.TransactionalKeyGenerators;
      if (serverInfo.Column.AllowedDdlStatements.Supports(DdlStatements.Drop))
        f |= ProviderFeatures.ColumnDrop;
      if (serverFeatures.Supports(ServerFeatures.ExclusiveWriterConnection))
        f |= ProviderFeatures.ExclusiveWriterConnection;
      if (queryFeatures.Supports(QueryFeatures.StrictJoinSyntax))
        f |= ProviderFeatures.StrictJoinSyntax;
      if (serverFeatures.Supports(ServerFeatures.SingleConnection))
        f |= ProviderFeatures.SingleConnection;
      if (queryFeatures.Supports(QueryFeatures.SelfReferencingRowRemovalIsError))
        f |= ProviderFeatures.SelfReferencingRowRemovalIsError;
      if(serverFeatures.Supports(ServerFeatures.FullTextColumnDataTypeSpecification))
        f |= ProviderFeatures.FullTextColumnDataTypeSpecification;
      if (queryFeatures.Supports(QueryFeatures.DeleteLimit))
        f |= ProviderFeatures.DeleteLimit;
      if (queryFeatures.Supports(QueryFeatures.UpdateLimit))
        f |= ProviderFeatures.UpdateLimit;
      if (serverFeatures.Supports(ServerFeatures.DateTimeEmulation))
        f |= ProviderFeatures.DateTimeEmulation;
      if (serverFeatures.Supports(ServerFeatures.DateTimeOffsetEmulation))
        f |= ProviderFeatures.DateTimeOffsetEmulation;

      var temporaryTable = serverInfo.TemporaryTable;
      if (temporaryTable!=null && temporaryTable.Features.Supports(TemporaryTableFeatures.Local))
        f |= ProviderFeatures.TemporaryTables;
      else if (serverFeatures.Supports(ServerFeatures.TemporaryTableEmulation))
        f |= ProviderFeatures.TemporaryTableEmulation;

      if (serverInfo.FullTextSearch!=null) {
        f |= ProviderFeatures.FullText;
        if (serverInfo.FullTextSearch.Features==FullTextSearchFeatures.Full)
          f |= ProviderFeatures.FullFeaturedFullText;
        if (serverInfo.FullTextSearch.Features==FullTextSearchFeatures.SingleKeyRankTable)
          f |= ProviderFeatures.SingleKeyRankTableFullText;
      }

      var column = serverInfo.Column;
      if ((column.AllowedDdlStatements & DdlStatements.Alter)==DdlStatements.Alter)
        f |= ProviderFeatures.ColumnRename;

      var table = serverInfo.Table;
      if ((table.AllowedDdlStatements & DdlStatements.Rename)==DdlStatements.Rename)
        f |= ProviderFeatures.TableRename;

      var dataTypes = serverInfo.DataTypes;
      if (dataTypes.DateTimeOffset!=null)
        f |= ProviderFeatures.DateTimeOffset;
      var binaryTypeInfo = dataTypes.VarBinary ?? dataTypes.VarBinaryMax;
      if (binaryTypeInfo!=null && binaryTypeInfo.Features.Supports(DataTypeFeatures.ZeroLengthValueIsNull))
        f |= ProviderFeatures.TreatEmptyBlobAsNull;
      var stringTypeInfo = dataTypes.VarChar ?? dataTypes.VarCharMax;
      if (stringTypeInfo!=null && stringTypeInfo.Features.Supports(DataTypeFeatures.ZeroLengthValueIsNull))
        f |= ProviderFeatures.TreatEmptyStringAsNull;

      var storageVersion = coreServerInfo.ServerVersion;
      var maxIdentifierLength = new EntityInfo[] {
        serverInfo.Column,
        serverInfo.ForeignKey,
        serverInfo.Index,
        serverInfo.PrimaryKey,
        serverInfo.Sequence,
        serverInfo.Table,
        serverInfo.TemporaryTable,
        serverInfo.UniqueConstraint
      }
        .Select(e => e==null ? int.MaxValue : e.MaxIdentifierLength)
        .Min();

      var supportedTypes = driver.TypeMappings.ReverseMappings.Values;

      return new ProviderInfo(
        providerName, storageVersion, f,
        maxIdentifierLength,
        serverInfo.PrimaryKey.ConstantName,
        coreServerInfo.DatabaseName, 
        coreServerInfo.DefaultSchemaName,
        supportedTypes,
        serverInfo.Query.MaxQueryParameterCount);
    }
  }
}