//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace CommonHelpers {

//**************************************************************************************************
[HDbOrmType][HSyncOnlyDisposable]
public partial class HOuterConnection: HDisposable {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[HAsyncStatic]
public static bool HasCurrentInstance =>
   Current != null;

/// <exception cref="InvalidOperationException" />
[HAsyncStatic][HOptionalValue]
public static HOuterConnection? Current =>
   GetCurrent();

/// <exception cref="InvalidOperationException" />
[HAsyncStatic]
public static HOuterConnection CurrentNN
{
   get
   {
      var curr = GetCurrent();
      if ( curr == null )
         throw new InvalidOperationException(HLocal.TEXT(
            "Outer connection instance is absent (\"HOuterConnection\").",
            "Отсутствует экземпляр обрамляющего подключения (\"HOuterConnection\")." ));
      return curr;
   }
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public SqlConnection Connection =>
   _Connection_ ?? throw new InvalidOperationException(
      "\"Connection\"-property has not been initialized (\"HOuterConnection\")." );
/// <exception cref="InvalidOperationException" />
public string ConnectionString { get; }
/// <exception cref="InvalidOperationException" />
public bool IsInTransaction =>
   Transaction != null;
/// <exception cref="InvalidOperationException" />
[HOptionalValue]
public SqlTransaction? Transaction =>
   _Transaction_;
/// <exception cref="InvalidOperationException" />
public SqlTransaction TransactionNN
{
   get {
      var rslt = Transaction;
      if ( rslt == null )
         throw OpenTransactionWasExpected_Error();
      return rslt;
   }
}

//==================================================================================================

/// <exception cref="InvalidOperationException" />
public static HOuterConnection Open()
{
   var instance = new HOuterConnection();
   var conn = new SqlConnection( instance.ConnectionString );
   conn.Open();
   instance._Connection_ = conn;
   instance.ValidateDisposable();
   SetCurrent( instance );
   return instance;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction() =>
   OpenTransaction( conn => conn.BeginTransaction() );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( IsolationLevel isolationLevel ) =>
   OpenTransaction( conn => conn.BeginTransaction( isolationLevel ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( string transactionName ) =>
   OpenTransaction( conn => conn.BeginTransaction( transactionName ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
public static HOuterConnection OpenTransaction( IsolationLevel isolationLevel,
                                                string transactionName ) =>
   OpenTransaction( conn => conn.BeginTransaction( isolationLevel, transactionName ) );

/// <exception cref="InvalidOperationException" />
/// <exception cref="SqlException" />
static HOuterConnection OpenTransaction( Func<SqlConnection,SqlTransaction> beginTransaction )
{
   var instance = new HOuterConnection();
   var conn = new SqlConnection( instance.ConnectionString );
   conn.Open();
   try {
      instance._Transaction_ = beginTransaction( conn );
   } catch {
      HOps.Try(()=>{ conn.Close(); });
      throw;
   }
   instance._Connection_ = conn;
   instance.ValidateDisposable();
   SetCurrent( instance );
   return instance;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public void VerifyInTransaction()
{
   if ( !IsInTransaction )
      throw OpenTransactionWasExpected_Error();
}
/// <exception cref="InvalidOperationException" />
public void VerifyNotInTransaction()
{
   if ( IsInTransaction )
      throw new InvalidOperationException(HLocal.TEXT(
         "Open transaction was not expected.",
         "Не ожидалось наличия открытой транзакции." ));
}

static InvalidOperationException OpenTransactionWasExpected_Error() =>
   new InvalidOperationException(HLocal.TEXT(
      "Open transaction was expected.",
      "Ожидалась открытая транзакция." ));

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction() =>
   BeginTransaction( conn => conn.BeginTransaction() );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( IsolationLevel isolationLevel ) =>
   BeginTransaction( conn => conn.BeginTransaction( isolationLevel ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( string transactionName ) =>
   BeginTransaction( conn => conn.BeginTransaction( transactionName ) );
/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void BeginTransaction( IsolationLevel isolationLevel,
                              string transactionName ) =>
   BeginTransaction( conn => conn.BeginTransaction( isolationLevel, transactionName ) );

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
void BeginTransaction(Func<SqlConnection,SqlTransaction> beginTransaction)
{
   VerifyNotInTransaction();
   _Transaction_ = beginTransaction( Connection );
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void CommitTransaction()
{
   var tran = TransactionNN;
   tran.Commit();
   _Transaction_ = null;
}

/// <exception cref="InvalidOperationException" />
/// <exception cref="Exception" />
public void RollbackTransaction()
{
   VerifyInTransaction();
   TransactionNN.Rollback();
   _Transaction_ = null;
}

//==================================================================================================

/// <exception cref="InvalidOperationException" />
HOuterConnection()
{
   if ( HasCurrentInstance )
      throw new InvalidOperationException(HLocal.TEXT(
         "Attempt to repeatedly create \"HOuterConnection\"-object.",
         "Попытка повторного создания объекта \"HOuterConnection\"." ));
   var connStrAccessor = HQueryExecContextBase.ConnectionStringAccessor;
   if ( connStrAccessor == null )
      throw HInvOpExceptionCreator.Create(
         HLocal.TEXT(
            "Property is not assigned: \"{0}\".",
            "Свойство не присвоено: \"{0}\"." ),
         "HQueryExecContextBase.ConnectionStringAccessor" );
   ConnectionString = connStrAccessor();
}

//==================================================================================================

/// <exception cref="InvalidOperationException" />
[HAsyncStatic]
protected override void OnDispose(bool synchronous)
{
   HAssertion.Verify( synchronous );
   if ( _Connection_ == null )
      return;
   SetCurrent( null );
   var conn = HOps.NullExchange(ref _Connection_).NN();
   HQueryExecContextBase.EnsureConnected_UnexpectedConnectionState( conn );
   conn.Close();
}

//==================================================================================================

[HAsyncStatic][HOptionalResult]
static HOuterConnection? GetCurrent() =>
   _AsyncStatic_.Value;
[HAsyncStatic]
static void SetCurrent([HOptional] HOuterConnection? instance) =>
   _AsyncStatic_.Value = instance;

//==================================================================================================

static readonly AsyncLocal<HOuterConnection?>
   _AsyncStatic_ = new AsyncLocal<HOuterConnection?>();

//--------------------------------------------------------------------------------------------------

SqlConnection? _Connection_;
SqlTransaction? _Transaction_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HOuterConnection
//**************************************************************************************************

} // CommonHelpers
