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

namespace System.Data.Entity.Migrations.Infrastructure
{
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.Entity.Core.Common.CommandTrees;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.Migrations.Model;
    using System.Data.Entity.Migrations.Sql;
    using System.Data.Entity.Resources;
    using System.Data.Entity.Utilities;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Xml.Linq;

    /// <summary>
    /// Base class for decorators that wrap the core <see cref="DbMigrator" />
    /// </summary>
    [DebuggerStepThrough]
    public abstract class MigratorBase
    {
        private MigratorBase _this;

        /// <summary>
        /// Initializes a new instance of the MigratorBase class.
        /// </summary>
        /// <param name="innerMigrator"> The migrator that this decorator is wrapping. </param>
        protected MigratorBase(MigratorBase innerMigrator)
        {
            if (innerMigrator == null)
            {
                _this = this;
            }
            else
            {
                _this = innerMigrator;

                var nextMigrator = innerMigrator;

                while (nextMigrator._this != innerMigrator)
                {
                    nextMigrator = nextMigrator._this;
                }

                nextMigrator._this = this;
            }
        }

        /// <summary>
        /// Gets a list of the pending migrations that have not been applied to the database.
        /// </summary>
        /// <returns> List of migration Ids </returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public virtual IEnumerable<string> GetPendingMigrations()
        {
            return _this.GetPendingMigrations();
        }

        /// <summary>
        /// Gets the configuration being used for the migrations process.
        /// </summary>
        public virtual DbMigrationsConfiguration Configuration
        {
            get { return _this.Configuration; }
        }

        /// <summary>
        /// Updates the target database to the latest migration.
        /// </summary>
        public void Update()
        {
            Update(null);
        }

        /// <summary>
        /// Updates the target database to a given migration.
        /// </summary>
        /// <param name="targetMigration"> The migration to upgrade/downgrade to. </param>
        public virtual void Update(string targetMigration)
        {
            _this.Update(targetMigration);
        }

        internal virtual string GetMigrationId(string migration)
        {
            DebugCheck.NotEmpty(migration);
            Debug.Assert(migration != Strings.AutomaticMigration);

            return _this.GetMigrationId(migration);
        }

        /// <summary>
        /// Gets a list of the migrations that are defined in the assembly.
        /// </summary>
        /// <returns> List of migration Ids </returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public virtual IEnumerable<string> GetLocalMigrations()
        {
            return _this.GetLocalMigrations();
        }

        /// <summary>
        /// Gets a list of the migrations that have been applied to the database.
        /// </summary>
        /// <returns> List of migration Ids </returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public virtual IEnumerable<string> GetDatabaseMigrations()
        {
            return _this.GetDatabaseMigrations();
        }

        internal virtual void AutoMigrate(
            string migrationId, VersionedModel sourceModel, VersionedModel targetModel, bool downgrading)
        {
            DebugCheck.NotNull(targetModel);

            _this.AutoMigrate(migrationId, sourceModel, targetModel, downgrading);
        }

        internal virtual void ApplyMigration(DbMigration migration, DbMigration lastMigration)
        {
            DebugCheck.NotNull(migration);

            _this.ApplyMigration(migration, lastMigration);
        }

        internal virtual void EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
        {
            _this.EnsureDatabaseExists(mustSucceedToKeepDatabase);
        }

        internal virtual void RevertMigration(
            string migrationId, DbMigration migration, XDocument targetModel)
        {
            DebugCheck.NotEmpty(migrationId);
            DebugCheck.NotNull(migration);
            DebugCheck.NotNull(targetModel);

            _this.RevertMigration(migrationId, migration, targetModel);
        }

        internal virtual void SeedDatabase()
        {
            _this.SeedDatabase();
        }

        internal virtual void ExecuteStatements(IEnumerable<MigrationStatement> migrationStatements)
        {
            DebugCheck.NotNull(migrationStatements);

            _this.ExecuteStatements(migrationStatements);
        }

        internal virtual IEnumerable<MigrationStatement> GenerateStatements(
            IList<MigrationOperation> operations, string migrationId)
        {
            DebugCheck.NotNull(operations);

            return _this.GenerateStatements(operations, migrationId);
        }

        internal virtual IEnumerable<DbQueryCommandTree> CreateDiscoveryQueryTrees()
        {
            return _this.CreateDiscoveryQueryTrees();
        }

        internal virtual void ExecuteSql(
            MigrationStatement migrationStatement, DbConnection connection, DbTransaction transaction,
            DbInterceptionContext interceptionContext)
        {
            DebugCheck.NotNull(migrationStatement);
            DebugCheck.NotNull(connection);

            _this.ExecuteSql(migrationStatement, connection, transaction, interceptionContext);
        }

        internal virtual void Upgrade(
            IEnumerable<string> pendingMigrations, string targetMigrationId, string lastMigrationId)
        {
            DebugCheck.NotNull(pendingMigrations);

            _this.Upgrade(pendingMigrations, targetMigrationId, lastMigrationId);
        }

        internal virtual void Downgrade(IEnumerable<string> pendingMigrations)
        {
            DebugCheck.NotNull(pendingMigrations);
            Debug.Assert(pendingMigrations.Count() > 1);

            _this.Downgrade(pendingMigrations);
        }

        internal virtual void UpgradeHistory(IEnumerable<MigrationOperation> upgradeOperations)
        {
            DebugCheck.NotNull(upgradeOperations);

            _this.UpgradeHistory(upgradeOperations);
        }

        internal virtual string TargetDatabase
        {
            get { return _this.TargetDatabase; }
        }

        internal virtual bool HistoryExists()
        {
            return _this.HistoryExists();
        }
    }
}
