package org.realityforge.arez.api2;

import java.util.ArrayList;
import java.util.Objects;
import javax.annotation.Nonnull;

public final class Transaction
  extends ArezElement
{
  /**
   * List of observables that reached zero observers within the scope of the transaction.
   * When the transaction completes, these observers are passivated
   */
  private final ArrayList<Observable> _pendingPassivations = new ArrayList<>();
  @Nonnull
  private int _batchDepth;

  boolean inBatch()
  {
    return _batchDepth > 0;
  }

  public void batch( @Nonnull final Runnable action )
  {
    startBatch();
    try
    {
      action.run();
    }
    finally
    {
      endBatch();
    }
  }

  public void startBatch()
  {
    _batchDepth++;
  }

  public void endBatch()
  {
    _batchDepth--;
    Guards.invariant( () -> _batchDepth >= 0,
                      () -> String.format( "Invoked endBatch on transaction named '%s' when no batch is active.",
                                           _name ) );
  }

  void commit()
  {
    Guards.invariant( () -> 0 == _batchDepth,
                      () -> String.format( "Invoked commit on transaction named '%s' when batch is active.",
                                           _name ) );
    passivatePendingPassivations();
  }

  private void passivatePendingPassivations()
  {
    //WARNING: Passivations can be enqueued during the passivation process
    // so always need to call _pendingPassivations.size() through each iteration
    // of loop to ensure new passivations are collected.
    //noinspection ForLoopReplaceableByForEach
    for( int i = 0; i < _pendingPassivations.size(); i++ )
    {
      final Observable observable = _pendingPassivations.get( i );
      observable.resetPendingPassivation();
      if( !observable.hasObservers() )
      {
        observable.passivate();
      }
    }
  }

  void queueForPassivation( @Nonnull final Observable observable )
  {
    Guards.invariant( observable::canPassivate,
                      () -> String.format(
                        "Invoked queueForPassivation on transaction named '%s' for observable named '%s' when observable can not be passivated.",
                        _name,
                        observable.getName() ) );
    Guards.invariant( () -> !_pendingPassivations.contains( observable ),
                      () -> String.format(
                        "Invoked queueForPassivation on transaction named '%s' for observable named '%s' when pending passivation already exists for observable.",
                        _name,
                        observable.getName() ) );
    _pendingPassivations.add( Objects.requireNonNull( observable ) );
  }
}

@@ -426,588 +426,8 @@
 %3E();
-%0A  @Nonnull%0A  private int _batchDepth;%0A%0A  boolean inBatch()%0A  %7B%0A    return _batchDepth %3E 0;%0A  %7D%0A%0A  public void batch( @Nonnull final Runnable action )%0A  %7B%0A    startBatch();%0A    try%0A    %7B%0A      action.run();%0A    %7D%0A    finally%0A    %7B%0A      endBatch();%0A    %7D%0A  %7D%0A%0A  public void startBatch()%0A  %7B%0A    _batchDepth++;%0A  %7D%0A%0A  public void endBatch()%0A  %7B%0A    _batchDepth--;%0A    Guards.invariant( () -%3E _batchDepth %3E= 0,%0A                      () -%3E String.format( %22Invoked endBatch on transaction named '%25s' when no batch is active.%22,%0A                                           _name ) );%0A  %7D
 %0A%0A  

Remove the concept of batches